Friday, July 17, 2015

Controlled Attribute Values (Part 2 - Advanced)

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

As already presented in Controlled Attribute Values for your DITA Project, Oxygen allows you to add or replace possible values for attributes or elements based on a simple configuration file. A more complex scenario is one in which in order to decide which values to provide, you need more context information. Let's take this DITA fragment:

    <othermeta name="name" content="value"/>

What we want is to offer proposals for @content but the possible values for @content depend on the value of @name. We will see how we can solve this dependency.

The configuration file

The configuration file (cc_value_config.xml) allows calling an XSLT stylesheet and that's just what we will do:
<match elementName="othermeta" attributeName="content">
    <xslt href="meta.xsl" useCache="false"/>

As you can see, we can't express the dependency between @content and @name inside the configuration file . I also want to mention that because the values for @content are dynamic, we want the XSLT script to execute every time the values are requested (we shouldn't cache the results). We enforce this by setting @useCache to false.

The XSLT script

The XSLT script has access to the XML document (through the documentSystemID parameter) but it lacks any context information, we can't really tell for which othermeta element was the script invoked. To counter this limitation, we will use Java extension functions and we will call Oxygen's Java-based API from the XSLT. Here how it looks:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl=""
    exclude-result-prefixes="xs xd"
    <xsl:param name="documentSystemID" as="xs:string"></xsl:param>
    <xsl:template name="start">
        <xsl:variable name="workspace" select="prov:getPluginWorkspace()"/>
        <xsl:variable name="editorAccess" select="work:getEditorAccess($workspace, xs:anyURI($documentSystemID), 0)"/>
        <xsl:variable name="pageID" as="xs:string" select="editorAccess:getCurrentPageID($editorAccess)"/>
        <xsl:variable name="name" as="xs:string">
                <xsl:when test="$pageID='Text'">
                    <xsl:variable name="textpage" select="editorAccess:getCurrentPage($editorAccess)"/>
                    <!-- In the text page, the context is the @content attribute -->
                    <xsl:value-of select="textpage:evaluateXPath($textpage, 'xs:string(./parent::node()/@name)')"/>
                <xsl:when test="$pageID='Author'">
                    <xsl:variable name="authorPage" select="editorAccess:getCurrentPage($editorAccess)"/>
                    <xsl:variable name="caretOffset" select="authorPage:getCaretOffset($authorPage)"/>
                    <xsl:variable name="ctrl" select="authorPage:getDocumentController($authorPage)"/>
                    <xsl:variable name="contextNode" select="ctrl:getNodeAtOffset($ctrl, $caretOffset)"/>
                    <!-- In the author page, the context is the "othermeta" element -->
                    <xsl:value-of select="ctrl:evaluateXPath($ctrl, 'xs:string(@name)', $contextNode, false(), false(), false(), false())[1]"/>
                <xsl:when test="$name = 'temperatureScale'">
                    <item value="Celsius" annotation="(symbol C)"/>
                    <item value="Fahrenheit" annotation="(symbol F)"/>
                <xsl:when test="$name = 'measurement'">
                    <item value="Metric" annotation="Metric system"/>
                    <item value="Imperial" annotation="Also known as British Imperial"/>

It is also worth mentioning that in the next Oxygen version (17.1) we will provide a more elegant solution to this situation. The XSLT script will receive a new parameter, an XPath expression that will identify the element for which content completion was invoked. But maybe we will talk about that in a future post...

Wednesday, July 15, 2015

Controlled Attribute Values for your DITA Project

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

Frequently when editing DITA content you will feel the need to enforce a controlled set of values when editing certain attributes. For example you may want to impose that the values for the @outputclass attribute on the element codeblock are either language-xml or language-css. This is useful in order to remind writers that any other value will not be interpreted by the build process in a significant manner.

Oxygen has a couple of easy ways in which controlled values can be imposed for certain attributes:
  1. You can edit the XML configuration file OXYGEN_INSTALL_DIR/frameworks/dita/resources/cc_value_config.xml and provide additional entries. In the case of our small example for providing controlled values for the @attribute the configuration file should contain an additional entry:
    <match elementName="codeblock" attributeName="outputclass">
     <items action="addIfEmpty">
      <item value="language-xml" annotation="XML Syntax Highlight"/>
      <item value="language-css" annotation="CSS Syntax Highlight"/>
    Besides providing a hard-coded list of values the content completion configuration file is flexible enough to allow calling an XSLT stylesheet which could retrieve those values from other sources (for example via HTTP from an Exist database).
  2. Provide those controlled values via a Subject Scheme Map (my favorite). Coming back to our example, you can create a small Subject Scheme map with the file name controlledValues.ditamap and the content:
    <!DOCTYPE subjectScheme PUBLIC "-//OASIS//DTD DITA Subject Scheme Map//EN""map.dtd"> 
                <navtitle>Provide controlled attributes</navtitle>
            <subjectdef keys="languageTypeKey">
                <subjectdef keys="language-xml">
                        <navtitle>XML Syntax Highlight</navtitle>
                <subjectdef keys="language-css">
                        <navtitle>CSS Syntax Highlight</navtitle>
            <elementdef name="codeblock"/>
            <attributedef name="outputclass"/>
            <subjectdef keyref="languageTypeKey"/>
    then you can refer to it from your main DITA Map like:
    <topicref href="controlledValues.ditamap" format="ditamap" type="subjectScheme"/>
  3. If the attributes on which you want to impose certain values are DITA profiling attributes, you can go to the Oxygen Preferences->Editor / Edit modes / Author / Profiling/Conditional Text page and define the set of allowed values for them.

The only problem with the first approach is the fact that validation will not impose those values and writers will not receive validation error messages if they set another value for the specific attribute. So you will probably need to add a Schematron check in order to signal errors when a certain attribute's value does not match the list of controlled attribute values. For both the second and third approaches, validation will warn the writers if certain attribute values do not match values in the controller values list.

Monday, June 22, 2015

DITA OT PDF Customization - Tables with Alternate Row Background Colors

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

Based on this forum thread I will try to give some steps in which you can create your own DITA Open Toolkit PDF customization folder for customizing a table to have alternate row background colors.

  1. First of all you need to know the XSLT template that you need to override.
    • You can open a DITA topic which has a table inside it and select in the Outline view the table row. The Attributes view will show you its @class attribute value which is - topic/row .
    • Use the Find/Replace in Files tool to search in the PDF plugin folder (for example DITA-OT-DIR/plugins/org.dita.pdf2/) for the string topic/row.
    • In the XSLT stylesheet DITA-OT-DIR/plugins/org.dita.pdf2/xsl/fo/tables.xsl you will find a template which matches all rows from a table body:
          <xsl:template match="*[contains(@class, ' topic/tbody ')]/*[contains(@class, ' topic/row ')]">
              <fo:table-row xsl:use-attribute-sets="tbody.row">
                  <xsl:call-template name="commonattributes"/>
      That is the template which you will need to overwrite in your customization.
  2. Copy the entire folder DITA-OT-DIR/plugins/org.dita.pdf2/Customization to an external location. For example in my case I copied it to my Desktop.
  3. Renamed in that copied folder the catalog.xml.orig file to catalog.xml, edit it and uncomment the line:
      <uri name="cfg:fo/xsl/custom.xsl" uri="fo/xsl/custom.xsl"/>
    This custom catalog file will be automatically used to contribute in the PDF publishing process with high priority the XSLT stylesheet located in Customization/fo/xsl/custom.xsl.
  4. Rename in the Customization/fo/xsl folder the custom.xsl.orig file to custom.xsl. This stylesheet will contain all your template overrides.
  5. Overwrite in the custom.xsl the original template like:
        <xsl:template match="*[contains(@class, ' topic/tbody ')]/*[contains(@class, ' topic/row ')]">
            <fo:table-row xsl:use-attribute-sets="tbody.row">
                    <xsl:when test="(count(preceding-sibling::*[contains(@class, ' topic/row ')]) mod 2) = 0">
                        <!-- Even row, light blue -->
                        <xsl:attribute name="background-color">rgb(210, 222, 253)</xsl:attribute>
                        <!-- Odd row, white -->
                        <xsl:attribute name="background-color">white</xsl:attribute>
                <xsl:call-template name="commonattributes"/>
  6. If you want the table frame border colors to have a custom color you can override some attribute sets defined in the DITA-OT-DIR/plugins/org.dita.pdf2/cfg/fo/attrs/tables-attr.xsl:
        <xsl:attribute-set name="table__tableframe__top" use-attribute-sets="common.border__top">
            <xsl:attribute name="border-top-color">blue</xsl:attribute>
        <xsl:attribute-set name="table__tableframe__bottom" use-attribute-sets="common.border__bottom">
            <xsl:attribute name="border-bottom-color">blue</xsl:attribute>
        <xsl:attribute-set name="table__tableframe__right" use-attribute-sets="common.border__right">
            <xsl:attribute name="border-right-color">blue</xsl:attribute>
        <xsl:attribute-set name="table__tableframe__left" use-attribute-sets="common.border__left">
            <xsl:attribute name="border-left-color">blue</xsl:attribute>
  7. Edit your PDF transformation scenario and set the parameter customization.dir to point to your customization folder.
  8. Publish and enjoy :)

If you want to create a DITA Open Toolkit plugin to achieve the same result you can use the dita.xsl.xslfo plugin extension to contribute your own XSLT stylesheet to the publishing process:

Wednesday, June 03, 2015

How to Host a Webinar

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

We periodically host webinars using the GotoWebinar platform. We have two main actors, the host and the presenter and for any other company or consultant who wants to host webinars I'm attaching below a general set of things the host needs to do during the webinar:

  • Half an hour before the start time the host starts the webbinar from the Go To Webinar panel → My Webinars

  • The host (main organizer) and the secondary actors (presenter and members of staff) log in, they are all made organizers.
  • The organizer and presenter test the sound.
  • All presenters need to:
    • Set resolution to 1280 x 800. (Good for recording the webinar).
    • The windows start-up menu needs to be in auto hide mode.
    • The Desktop needs to be cleaned of clutter in order not to distract watchers.
    • All useless applications need to be closed. For example the email application could show email notifications during the presentation.
    • If you are presenting things in the web browser, please make sure you have no extra tabs opened in it.
    • The host opens the power point and goes to full screen.
  • Exactly at the scheduled hour the host presses the Start Broadcast button and presses the Show my screen option. The staff member in charge for recording will start it on his computer.
  • The first presentation slide can have the names of the host and the presenter and the title of the webinar.
  • Host starts speaking: "Hello everybody, my name is X and I will be this webinar's host. I invited my coleague Y to present this webinar covering Z support in Oxygen. This webinar will focus mainly on ... "
  • The second slide is the Agenda.
  • Host continues to speak: "Our agenda today starts with an introduction followed by Y's presentation then we will have a dedicated Q&A section at the end followed by a quick overview of upcoming events, webinars and conferences. "
  • The third slide is presented, the Questions slide.
  • Host speaks: "You do not need to wait until the dedicated Q&A section to ask questions. You can ask questions at any time during the webinar using the Questions panel from the Go to Webinar interface. Some of my coleagues who have already joined the webinar will answer your questions. At the end we'll go over some of the relevant questions. This webinar is recorded and a recording as well as sample files used during the webinar will be made available in a couple of days. "

  • Fourth slide. Host continues to speak: "Before Y will start his presentation I would like you yo answer the following questions..."
  • And the host releases the pre-created polls. He presses Start on a poll, talks about choices, waits a little then closes the poll and shares the results, closes the poll and then shares the results. Then the results are discusses and then the poll is hidden. And so on for each poll.
  • The fifth slide, the host makes Y the presenter, Y will start presenting for about 45 minutes.
  • During the presentation the host looks over questions, he answers some of them.
  • Y has finished the presentation and thanks for the attendance.
  • The host un-mutes himself and starts speaking without sharing his screen: "Thank you Y for this very nice presentation on Z. We covered a lot of ground today. Let's go over some of the questions.... "
  • Host takes the screen and talks over the last two slides, the one about future seminars and the one about future conferences.
  • Host asks Y if he has anything else to add, then they both thank the audience, then mute all. The webinar is left opened a little bit more in order for attendees to be able to ask additional questions.
  • End webinar. File -> Leave Webinar.

  • Eat a slice of pizza and relax, talk about what went wrong, something always does :)

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="">
     <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.
      <!-- Quick fix to add the indexterm element element and its parents -->
      <sqf:fix id="addFragment">
              <sqf:title>Add the 'indexterm' element</sqf:title>
          <sqf:add match="(title | titlealts | abstract | shortdesc)[last()]" position="after" use-when="not(prolog)">
             <prolog xmlns=""><metadata><keywords><indexterm> </indexterm></keywords> </metadata></prolog>
  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: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.
      <sqf:fix id="setId">
        <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:replace match="@id" node-type="attribute" target="id" select="$reqId"/>
  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="" format="html" scope="external"></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: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.
      <sqf:fix id="removeText">
        <sqf:title>Remove redundant link text, text is same as @href value.</sqf:title>
       <sqf:delete match="text()"/>
  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:
     <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.

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.*/
          /* White page */
          border: 1px solid #c6c6c6;
          /* Centered*/
          margin-left: auto;
          margin-right: auto;
          margin-top: 0.25in;
          margin-bottom: 0.25in;
          /* Letter */
          width: 21.59cm; 
        /* Padding inside the page. */
        :root {
  5. New form control which can display HTML content. With a very simple CSS selector like:
         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:
              name, 'Remove Comment', 
              description, 'Remove the current Comment', 
              operation, 'ro.sync.ecss.extensions.commons.operations.JSOperation', 
              arg-script, "function doOperation(){authorAccess.getDocumentController().deleteNode(
              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:
          <parcel id="PID81199">
              <comment>Should be shipped before the 9'th</comment>
              <details>This parcel belongs to Syncrosoft and should be handled with care.</details>
      I can use CSS positioning to place the comment for each parcel on the right part of the display area:
          content:"Ship to: ";
          content:"Ship date: ";
          content:"Details: ";
      parcel > *{
          content: "Parcel '" attr(id) "' :";
          left: 70%;
          top: 1em;
          content:"Comment: \A";
      and display it like a hovering annotation over each parcel:
    • letter-spacing

      With small CSS snippets like:
      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:
        content: "Title: " !important;
      your CSS layer can add a button before the title without removing its already set static content:
          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: