Thursday, May 21, 2015

Schematron Checks to help Technical Writing

Share to Facebook Share to Twitter Email This Share on Google Plus Share on Tumblr

The Oxygen XML Editor User's Manual is written in DITA. In an older post I described in more detail how we collaborate internally on our User's Guide Project. And we also made available a copy of our User's Manual as a project on GitHub.

During these years on working on it, we progressively developed a set of simple rules which were originally kept in a plain text document. The problem is that nobody can really remember all these rules when actually writing. So recently we started to migrate these rules to Schematron and have them reported automatically has validation warnings and errors while editing the topics. And with the release of Oxygen 17 we can now also add quick fixes for each of these problems.

So below I will try to tell you what each rule imposes and what it's Schematron implementation looks like. If you want to quickly test these rules on your side, you can add them to the Schematron file which is used by default to validate DITA topics located in: OXYGEN_INSTALL_DIR/frameworks/dita/resources/dita-1.2-for-xslt2-mandatory.sch.

  1. Try as much as possible to add at least an indexterm element in each topic. This is useful when the Index page is created for the PDF output or the Index tab is created for the WebHelp output. As this is not a requirement, we wanted to report this issue as an error. The end result looks like this:

    And the Schematron pattern looks like this:
    <pattern xmlns:sqf="http://www.schematron-quickfix.com/validator/process">
     <rule context="/*">
      <assert test="prolog/metadata/keywords/indexterm" role="warn" sqf:fix="addFragment">
          It is recommended to add an 'indexterm' in the current '<name/>' element.
      </assert>
      <!-- Quick fix to add the indexterm element element and its parents -->
      <sqf:fix id="addFragment">
          <sqf:description>
              <sqf:title>Add the 'indexterm' element</sqf:title>
          </sqf:description>      
          <sqf:add match="(title | titlealts | abstract | shortdesc)[last()]" position="after" use-when="not(prolog)">
             <prolog xmlns=""><metadata><keywords><indexterm> </indexterm></keywords> </metadata></prolog>
          </sqf:add>
      </sqf:fix>
     </rule>
    </pattern>
  2. The ID of each topic must be equal to the file name (minus the extension). One of the outputs we produce (I think CHM) had a limitation when building the context mapping between help IDs and actual HTML content so this was an important rule for us, thus an error is reported on this. Also a quick fix is added to auto-correct the topic ID based on the file name. The end result looks like this:

    and the Schematron pattern is:
    <!-- Topic ID must be equal to file name -->
    <sch:pattern>
     <sch:rule context="/*[1][contains(@class, ' topic/topic ')]">
      <sch:let name="reqId" value="replace(tokenize(document-uri(/), '/')[last()], '\.dita', '')"/>
      <sch:assert test="@id = $reqId" sqf:fix="setId">
       Topic ID must be equal to file name.
      </sch:assert>
      <sqf:fix id="setId">
       <sqf:description>
        <sqf:title>Set "<sch:value-of select="$reqId"/>" as a topic ID</sqf:title>
        <sqf:p>The topic ID must be equal to the file name.</sqf:p>
       </sqf:description>
       <sqf:replace match="@id" node-type="attribute" target="id" select="$reqId"/>
      </sqf:fix>
     </sch:rule>
    </sch:pattern>
  3. Report when plain links or related links to web resources have the same text inside them as the value of the @href attribute. We had cases in which writers would input web links like this:
    <xref href="http://www.google.com" format="html" scope="external">http://www.google.com</xref>
    which is redundant because when you set no text to the link, the publishing uses the @href attribute value as the link text. So we wanted to report such cases as warnings and to have a quick fix which removes the link text:

    The Schematron pattern looks like this:
    <sch:pattern>
     <sch:rule context="*[contains(@class, ' topic/xref ') or contains(@class, ' topic/link ')]">
      <sch:report test="@scope='external' and @href=text()" sqf:fix="removeText">
       Link text is same as @href attribute value. Please remove.
      </sch:report>
      <sqf:fix id="removeText">
       <sqf:description>
        <sqf:title>Remove redundant link text, text is same as @href value.</sqf:title>
       </sqf:description>
       <sqf:delete match="text()"/>
      </sqf:fix>
     </sch:rule>
    </sch:pattern>
  4. Avoid using the @scale attribute on images. We wanted to smooth scale images in an external image editor so it was prohibited to use the @scale attribute on images. The Schematron pattern for this:
    <pattern>
     <rule context="*[contains(@class, ' topic/image ')]">
      <assert test="not(@scale)">
       Dynamically scaled images are not properly displayed, you
       should scale the image with an image tool and keep it within
       the recommended with and height limits.
      </assert>
     </rule>
    </pattern>

We have an upcoming webinar dedicated to Schematron Quick Fixes. There is a W3C working group for XML Quick Fixes and you calso read the SQF Quick Fix specification if you want to become more familiar with the technology.
We also have a GitHub project which tries to combine the notion of a style guide for writing documentation inside a company with a very simple manner of defining checks which can be applied to impose the styleguide rules.

I would be interested in your feedback, especially if you have checks that you perform right now on your content and you consider that they might benefit others.

Thursday, May 14, 2015

Visual rendering CSS improvements in Oxygen 17.0

Share to Facebook Share to Twitter Email This Share on Google Plus Share on Tumblr
We had a lot of development time for Oxygen 17.0 and a lot of powerful extensions were added for the CSS-based visual author rendering. I will try to go through each of the improvements and give you a small example of how this would benefit you:
  1. CSS Inspector. Whenever you use CSS to style how XML or HTML looks like, as the CSS content increases in complexity at some point you tend to get lost. For example you will not know where the foreground color for a certain XML element is defined. Most Web Browsers have an inspector tool which allows you to inspect styles for a certain XML element. The CSS Inspector is a similar tool available in Oxygen 17 which can be used to debug your CSS selectors and see how they apply on specific elements. To show it, you can right click in the Author visual editing mode and choose Inspect Styles.

  2. Support for rendering using LESS. Less is more. LESS is a dynamic stylesheet language which extends CSS and adds mechanisms for supporting variables, nesting, mixins, operators, and functions. Besides support for editing and validating LESS content, Oxygen 17 can also directly render in the Author editing mode content using an associated LESS stylesheet.

    For example instead of repeating the same hard coded color in various places in your CSS like:
    link1 {
      color: #5B83AD;
    }
    /*..............*/
    link2 {
      color: #5B83AD;
    }
    you can define a variable for the color and refer to that variable instead:
    @special-blue: #5B83AD;
    
    link1 {
      color: @special-blue;
    }
    /*.............*/
    link2 {
      color: @special-blue;
    }
  3. Apply multiple CSS layers in the Author editing mode. Each framework customization can now include besides a main CSS additional CSS layers which can be toggled On or Off to enhance the display in various ways. So for example when editing DITA you can toggle on and off inline actions and inline hints:

  4. Responsive layout. The content can be rendered in certain ways depending on the available screen space. For example your CSS can add margins to the edited content if the screen width allows it:
    @media oxygen AND (min-width:25cm) {
        /* The white page.*/
        :root{      
          /* White page */
          border: 1px solid #c6c6c6;
          background-color:white;
          
          /* Centered*/
          margin-left: auto;
          margin-right: auto;
          margin-top: 0.25in;
          margin-bottom: 0.25in;
          
          /* Letter */
          width: 21.59cm; 
        }
        /* Padding inside the page. */
        :root {
              padding:1em;
        }    
    }
  5. New form control which can display HTML content. With a very simple CSS selector like:
    note:before{
         display:block;                    
         content : oxy_htmlContent(href, "hints.html", id, 
              oxy_concat("hints" , "-" , oxy_local-name(null)), width,100%);
    }
    you can show small HTML-styled annotations for each element by using specific content from a larger HTML document which can contain all available annotation:
  6. Directly define inline actions in the CSS content. With a combination of the new support for defining actions inline in the CSS and the predefined operation which can run Javascript code you can add buttons which perform complex actions directly from the CSS. As an example I can use the CSS snippet:
    comment:after{
        content:
        oxy_button(
       action,
        oxy_action(
              name, 'Remove Comment', 
              description, 'Remove the current Comment', 
              operation, 'ro.sync.ecss.extensions.commons.operations.JSOperation', 
              arg-script, "function doOperation(){authorAccess.getDocumentController().deleteNode(
                        authorAccess.getDocumentController().getNodeAtOffset(authorAccess.getEditorAccess().getCaretOffset()));}"),
              showText, true);
    }
    to add a button after the comment element which deletes it:
  7. Support for more CSS properties:
    • position

      You can now place elements out-of-sequence in the rendered content. For example if I have a list of parcels:
      <parcels>
      ….…....
          <parcel id="PID81199">
              <shipLocation>Greenwich</shipLocation>
              <comment>Should be shipped before the 9'th</comment>
              <shipDate>2015-12-12</shipDate>
              <details>This parcel belongs to Syncrosoft and should be handled with care.</details>
          </parcel>
      ….….…..
      </parcels>
      I can use CSS positioning to place the comment for each parcel on the right part of the display area:
      *{
          display:block;
      }
      *:before{
          color:gray;
      }
      parcel{
          position:relative;
      }
      shipLocation:before{
          content:"Ship to: ";
          width:100px;
      }
      shipDate:before{
          content:"Ship date: ";
          width:100px;
      }
      details:before{
          content:"Details: ";
          width:100px;
      }
      parcel > *{
       padding-left:1em;   
       width:60%;
      }
      parcel:before{
          content: "Parcel '" attr(id) "' :";
          display:block;
          color:gray;
      }
      comment{
          background-color:yellow;
          position:absolute;
          left: 70%;
          top: 1em;
          width:25%;
      }
      comment:before{
          content:"Comment: \A";
      }
      and display it like a hovering annotation over each parcel:
    • letter-spacing

      With small CSS snippets like:
      title{
          letter-spacing:0.2em;
          font-size:1em;
      }
      you can make your titles stand out:
    • ::before(n) and ::after(n) Pseudo Elements

      Your CSS layers can contribute multiple layers of static content without overwriting each other. For example if your main CSS specifies static content before an element:
      title:before{
        content: "Title: " !important;
      }
      your CSS layer can add a button before the title without removing its already set static content:
      title:before(2){
          content: oxy_button(actionID, 'paragraph', showIcon, true);
      }
      and obtain a result like:
    • font-family fallback fonts

      If you have documents containing mixed content (English + Asian for example) you can now specify an enumeration of fallback fonts which will be used for rendering text content inside paragraphs:
      p {
          font-family:Arial, Serif;
      }
      to render each range of characters with the font which supports it: