STL definition syntax

Table of Contents

Overview

This document describes an XML syntax of STL definition file. The syntax can be validated programmatically against the following XSD schema.

Top level structure consists of a root <stl:stl> element and four optional sub-elements defining fixtures, data, styling and a document.

STL Root

One of the DocBuilder++ design goals was to unify its XML structure with the output format of the Page Layout Driver.

:exclamation: Note that the driver was implemented before we started the DocBuilder++ design, so there are some notable differences in syntax. Also the fact is that Page Layout Driver output represents a paginated/rendered form of a document while the DocBuilder++ format represents a document design structure so they have many high-level and low-level differences. That being said we still believe that it is possible to unify both formats to a single common syntax, which will ultimately cover all the existing usecases and even will generate new ones.

It means that we reused the http://developer.opentext.com/schemas/storyteller/layout XML namespace and stl alias (but renamed root element to stl) and so the root of the XML structure looks as follows:

There are three optional sub-elements under the stl:stl element, we will:

Data

To make it usable in even more use cases, we decided to integrate data aspect to the DocBuilder++ format. It means that it is possible to define a stl:data XML element with following subelements:

Any entity can be either directly included in the definition file, or referenced via uri attribute.

Top level of the data section can look as follows:

<stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:data>
    <stl:source>
      ...
    </stl:source>
    <stl:template>
      ...
    </stl:template>
    <stl:transformation>
      ...
    </stl:transformation>
  </stl:data>
</stl:stl>

stl:template as well as stl:source elements can contain arbitrary XML structure but stl:transformation must contain a valid TDT Definitions. For more details how a TDT Data Transformation is defined and implemented - see the TDT Documentation.

Following examples show a definition of Data Source, Data Template and Transformation a possibility to include or reference individual definitions as well as preview resulting Data Instances.

In all four case we also demonstrate a possibility to create an STL defining just TDT Data Transformation - it means that no stl:document definition is necessary for a meaningful STL definition.

Book Reviews

In this example user defines a Data Template representing a simple HTML hierarchy containing annotated values (represented as XDC tags). It is then possible to Transforma a Data Source and save resulting Data Instance to an XML or HTML file.

All three entities - stl:source, stl:template and stl:transformation are directly included in the definition file.

Dual Transformation

In this example we demonstrate a possibility of scenario when we process various formats of Data Sources and create a unified way to process them - create a single Document Design presenting Data Instances unified to a single format represented as a single Data Template.

The data part of the STL file can look as follows:

It is clearly visible that there is a single stl:template in the definition but two kinds of stl:source and stl:transformation pairs. This way we can process two formats of source data changing just the active TDT key.

TDT-based document creation

Some examples of TDT-based document creation are described in the STL/SSDX Framework Documentation, chapter DocBuilder utilization.

Whitespace handling

Generally we try not to mix elements with text data at a single level of the STL format. That way any inter-element whitespace (created for example when the XML document is pretty printed) can be treated as sequence of discardable entities and safely ignored.

A different situation is for Story definition format - it is represented as a sequence of Mixed Content Elements. It means that there are Elements and Content Data mixed at the same level of XML hierarchy and we have to provide some level of a clever normalization so we make the syntax more robust for cases like XML pretty-print but also provide users with powerful enough means to create content they need (including whitespaces).

Consider the following example:

<stl:story>
  <stl:p>
    This is a single-line text
  </stl:p>
</stl:story>

… user probably does not expect a newline followed by several spaces to be inserted to a Story.

On the other hand in the following case:

<stl:story>
  <stl:p>Author: <stl:field xpath="/data/author/name"/></stl:p>
</stl:story>

… it is most probable that user expects the space after the colon to be preserved.

That is why we need some clever XML content normalization to provide an intuitive and user friendly behavior.

To achieve such a goal we consider the following two algorithms:

For detailed discussion about whitespace treatment possibilities see the XML Whitespace article on the Text Encoding Initiative Wiki.

normalize-space algorithm

XPath normalize-space algorithm does basically the following:

This is relatively simple c++ implementation:

void normalize_data( 
    std::string& data, bool trim_left = true, bool trim_right = true, bool collapse = true )
{
    auto const not_a_space = [](char ch) { return ch != ' '; };
    // replace all whitespaces with spaces in-place
    std::transform( 
      std::begin(data), 
      std::end(data), 
      std::begin(data), 
      []( char ch ) { return std::isspace(ch) ? ' ' : ch; } );
    // trim all leading and trailing space characters
    if ( trim_right ) {
      data.erase( 
        std::find_if(std::rbegin(data), std::rend(data), not_a_space).base(),
        std::end(data) );
    }
    if ( trim_left ) {
      data.erase( 
        std::begin(data), 
        std::find_if(std::begin(data), std::end(data), not_a_space));
    }
    // collapse each space sequences to a single space
    if ( collapse ) {
      data.erase( std::unique( 
        std::begin(data),
        std::end(data),
        []( char c1, char c2 ){ return (c1==' ' && c2==' '); }),
        std::end(data) );
    }
}

The advantage is that the algorithm and easy to understand and well known and frequently used (available in any XPath and XSLT processor). On the other hand we believe that such significant input modification may not be necessary.

That is why we also consider and alternative approach.

split-trim-join algorithm

We call the algorithm split-trim-join and it works as follows:

This is a c++ implementation:

void normalize_data( std::string& data )
{
    auto const not_a_space = [](auto c) { return c != ' '; };
    auto const replace_with_space = [](auto chars) { 
        return [chars](auto ch) { 
          return contains_char(chars, ch) ? ' ' : ch; };
    };

    auto const trim_left = [not_a_space](auto begin, auto end) {
        return std::find_if(++begin, end, not_a_space);
    };

    auto const trim_right = [not_a_space](auto begin, auto end) {
        return std::find_if( 
            std::reverse_iterator<APIString::iterator>(begin), 
            std::reverse_iterator<APIString::iterator>(end), 
            not_a_space).base();
    };

    auto const next_split = [](auto begin, auto end) {
        return std::find(begin, end, '\n');
    };

    auto const limit = std::end(data);
    auto left = std::begin(data);
    std::transform(left, limit, left, replace_with_space("\t\r\f\v"));

    auto split = next_split(left, limit);
    // no trimming if there is just a single string
    if (split != limit) {
        // first token is not left-trimmed
        auto cursor = trim_right( split, left );
        auto right = cursor;
        while(true) {
            left = trim_left(split, limit);
            split = next_split(left, limit);
            if (split == limit) {
                // last token is not right-trimmed
                cursor = std::copy(left, limit, cursor);
                break;
            }
            right = trim_right(split, left);
            cursor = std::copy(left, right, cursor);
        }
        data.erase(cursor, limit);
    }
}

normalize-space-auto-trim

We believe that the normalization algorithm should better preserve users’ intention and so we are also experimenting with a slightly modified version of the XPath normalize-space.

The only difference is that we trim leading and trailing whitespace just in text-only elements. If there is a mixture of data and sub-elements at a single level then we are trying to preserve a single space where appropriate:

...
<stl:story>
  <stl:p> Both leading and trailing spaces are trimmed </stl:p>
  <stl:p> Just the leading space is trimmed here <stl:span/> and just the trailing space is trimmed here </stl:p>
</stl:story>
...

Consider the following encoding:

...
<stl:story>
  <stl:p>  The <stl:span style="font-style:italic"> cat </stl:span> ate  the <stl:span style="font-weight:bold">grande croissant</stl:span>. I didn't!
  </stl:p>
</stl:story>
...

By convention, it is presumed that this encodes a passage that could have been equivalently encoded as follows:

...
<stl:story>
  <stl:p>
    The 
    <stl:span style="font-weight:italic">cat</stl:span>
    ate the 
    <stl:span style="font-weight:bold">croissant</stl:span>. 
    I didn't!
  </stl:p>
</stl:story>
...

The algorithm to normalize space in mixed content is:

The result is as if the encoding had been

...
<stl:story>
  <stl:p>The <stl:span style="font-style:italic">cat</stl:span> ate the <stl:span style="font-weight:bold">grande croissant</stl:span>. I didn't!</stl:p>
</stl:story>
...

Note: The normalization process would have corrupted the text had the encoder put spaces inside the stl:span elements, like this:

...
<stl:story>
  <stl:p>The<stl:span style="font-style:italic"> cat </stl:span>ate the<stl:span style="font-weight:bold"> grande croissant </stl:span>. I didn't!</stl:p>
</stl:story>
...

The resulting text would be:

Thecatate thegrande croissant. I didn’t!

So an encoder should assume that an element that includes nothing but text will get trimmed.

Example

Following example demonstrates that such normalization works relatively well and is very intuitive:

STL Definition
Resulting Document

Try in STLEditor »

Styling

For load-time styling in Docbuilder++ we use a subset of CSS syntax.

CSS stands for *C*ascading *S*tyle *S*heets.

CSS describes how individual document elements are to be formatted and rendered.

CSS saves a lot of work. It can control the layout of multiple objects in multiple documents all at once.

CSS Application

CSS can be added to HTML elements in 3 ways:

The most common way to add CSS, is to keep the styles in separate CSS files. However, here we will start with inline and internal styling, because this is easier to demonstrate.

Inline CSS

Setting the style of a particular document element (like stl:ellipse, stl:block, stl:p or stl:span), can be done with the style attribute.

The style attribute has the following syntax:

<stl:p style="text-align:center;">
  Centered content ...
</stl:p>

Internal CSS

An internal CSS is used to define a style for a single STL definition.

An internal CSS is defined in the top-level section of a definition file, before stl:document element.

<stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:style>
    .centered {
      text-align:center;
    }
  </stl:style>
  <stl:document>
    <stl:story>
      <stl:p class="centered">
        Centered content ...
      </stl:p>
    </stl:story>
    ...
  </stl:document>
</stl:stl>

External CSS

An external style sheet is used to define the style for many DocBuilder++ documents.

With an external style sheet, you can change the look of an entire library of documents by changing a single file!

<stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:style src="wd:/default.css"/>
  <stl:document>
    <stl:story>
      <stl:p class="centered">
        Centered content ...
      </stl:p>
    </stl:story>
    ...
  </stl:document>
</stl:stl>

CSS Properties

While CSS is very rich and powerful language, only a very limited subset is supported by DocBuilder++.

It is possible that we’ll extend the support in future, but right now from the whole variety of CSS selectors only .class selectors are supported.

This way we can find out how far we get with such a limited functionality without closing some doors for future.

For example there may be an XSLT, TDT or other transformation converting STL definition file directly to HTML output. And if we limit our users to use only .class CSS selectors then we may be able to reuse a single CSS stylesheet between DocBuilder++ and HTML output without any additional changes. On the other hand if we add a support for element name selectors or parent-child selectors then we accidentally expose the structure of our document (element names and hierarchy) and make such stylesheets incompatible and inapplicable for HTML output (without a necessary stylesheet transformation).

Also the set of supported CSS properties is limited to the ones we are able to convert to StoryTeller document model. The list of currently supported properties is documented in following sections.

If there is an unsupported property in the associated stylesheet the DocBulder threats it as error. An alternative approach would be to ignore such properties, but for now we decided to be more strict and make possible extensions in future based on a feedback we get. Such narrow contract is typically easier to implement (we do not need to handle some cases like DocBuilder -> HTML conversion specially), is less surprising for users (user gets the error at loading time instead of delivering thousands of incorectly formatted documents because of a typo in a stylesheet) and is easily extensible in future (without worrying that we break backward compatibility).

Length Units

CSS offers a number of different units for expressing length. Some of them are currently supported in DocBuilder++ and so can be used throughout STL definition markup:

For more detailed CSS unit description see the W3C documentation.

Color Definition

Right now a six-digit (#rrggbb) and three-digit (#rgb) forms of hex format RGB color are implemented:

<stl:text x="20pt" y="20pt" w="260pt" h="180pt" style="fill:#0effff; stroke:#4b0082;">
  <stl:story>
    This is a
    <stl:span style="background-color:#d2691e; color:#0f0;">
      colored
    </stl:span>
    text.
  </stl:story>
</stl:text>

… and also CSS3 Extended Color Keywords are supported:

<stl:text x="20pt" y="20pt" w="260pt" h="180pt" style="fill:lightcyan; stroke:indigo;">
  <stl:story>
    This is a
    <stl:span style="background-color:chocolate; color:lime">
      colored
    </stl:span> text.
  </stl:story>
</stl:text>

… and finally decimal rgb() and rgba() notations are supported:

<stl:text x="20pt" y="20pt" w="260pt" h="180pt" style="fill:rgb(224,255,255); stroke:rgba(75,0,130,1.0);">
  <stl:story>
    This is a
    <stl:span style="background-color:rgba(210,105,30,1.0); color:rgb(0,255,0);">
      colored
    </stl:span>
    text.
  </stl:story>
</stl:text>

For genericity and user convenience we should implement a support for full CSS color in future (e.g. decimal hsl and hsla notations).

Pen Definition

Brush definition

Drawing Style Definition

Any layout item (stl:text, stl:input, stl:table, stl:rect,stl:circle, …) defining a style attribute can use following optional CSS properties modifying item’s drawing style:

Right now only a solid Fill (Brush) and Stroke (Pen) is generated for given color.

In future we are going to support more advanced Fill and Stroke definitions, via inline CSS Style:

<stl:line x1="40pt" x2="120pt" y1="100pt" y2="100pt"
          style="stroke: black; stroke-width: 20pt; stroke-linecap: round;"/>

There are also following additional drawing style related properties with no direct mapping to HTML CSS, so we used a CSS extension with -stl- “vendor” prefix:

Paragraph Style Definition

Content elements defining a style attribute for paragraphs (stl:p, stl:list and stl:block) can use following optional CSS properties modifying current paragraph style:

There are also following additional paragraph style related properties with no direct mapping to HTML CSS, so we used a CSS extension with -stl “vendor” prefix:

Character Style Definition

Content elements defining a style attribute (stl:p, stl:list, stl:block and stl:span) can use following optional CSS properties modifying current character style:

Then there is an attribute lang definition language for whole element subtree

CSS Parser

As an initial implementation of the CSS parser we used regex-based CCSS library based on css-parse and css-stringify libraries, all released under MIT licence.

The parsing code was very much rewritten, but we still consider a possibility to use more powerfull engine like LibCSS.

Document Design

Document

In principle it would be possible to define several documents and for example share a single data definition among them.

But right now user can define just a single stl:document element in the STL file. It’s structure consists of a list of (shared) Stories represented as stl:story elements and a sequence of Page Types represented as stl:page elements.

Following listing shows how the top level XML structure can look like:

<stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  ...
  <stl:document>
    <stl:story>
      ...
    </stl:story>
    <stl:page w="300pt" h="500pt">
      ...
    </stl:page>
  </stl:document>
</stl:stl>

There can be a support for multiple stl:document definitions in future but right now only none or a single stl:document is supported.

Document Structure

Right now there is no DocBuilder++ support for advanced document structure.

Currently users can create individual stl:page elements (representing Page Types and Page References at the same time) and add an optional occurrence attribute to each such page.

The occurrence attribute value can be one of the following:

If there is no occurrence attribute then the page will be instantiated exactly once.

Page

The stl:page represents a definition of a single Page Type. Right now the sequence of Page Types leads to a flat Document Structure referencing each individual Page Type with appropriate Occurrence. It is possible that in future we extend the format with an optional explicit Document Structure definition.

Single (repeatable) Page Type definition looks as follows:

...
  <stl:page w="300pt" h="500pt" occurrence="repeatable">
    ...
  </stl:page>
...

Each page typically contains a hierarchy of various Layout Items (like texts, images, fragments etc).

Following sections describe various Layout Items which user can include in stl:page hierarchy as page objects or inside native stl:story sequence as inline or paragraph objects.

Story

A single Story represents a linear content sequence sometimes containing one or more inline of paragraph layout objects (texts, tables, …) as well as data objects (switches, repeaters, substitutions) or other content objects (commands, hyperlinks, …).

Story Format

Story can be specified in various formats, right now a Native DocBuilder++ and XHTML format is supported. It is possible that other content formats (like RTF) will be supported in future.

Following listing shows a very simple example of a native content:

...
  <stl:story>
    <stl:p>This is a private (and native) story...</stl:p>
  </stl:story>
...

And here is an example of an XHTML content:

...
  <stl:story format="XHTML">
    <body>
      <p>This is a <b>shared</b> <i>story</i>...</p>
    </body>
  </stl:story>
...

Current XHTML filter implementation has many bugs and limitations but it is still a usable piece of software and integrating it with DocBuilder++ can fulfill at least some possible usecases.

Hopefully we’ll manage to replace the XHTML filter with more decent implementation in future.

Story Ownership

Just like in StoryTeller Document a Story can be either shared or private.

Shared Stories are owned directly by Document and so are defined at the stl:document level of the STL definition.

A Shared Story has no effect on the formatted document until it gets referenced by some formatting object (be it a text or a story reference from the same or a different document).

Shared stories can be referenced via index or a name:

...
  <stl:document>
    <stl:story name="Shared">
      <stl:p>This is a shared story...</stl:p>
    </stl:story>
    ...
    <stl:page w="300pt" h="500pt">
      <stl:text x="120pt" y="20pt" w="60pt" h="60pt" story="Shared" style="stroke:#00ff00"/>
      ...
    </stl:page>
  </stl:document>
...

Some objects (e.g. texts, switches, repeaters) can define their Private Story right inside their corresponding XML element:

...
  <stl:text x="10pt" y="10pt" w="100pt" h="100pt" style="stroke:#0000ff">
    <stl:story>
      <stl:p>This is a private story...</stl:p>
    </stl:story>
  </stl:text>
...

Example

Following example demonstrates various Story ownership modes and formats:

STL Definition
Resulting Document

Try in STLEditor »

Semantic Tags & Styling

It is possible to associate a Semantic Tag with any stl:story (even one inside an stl:scope) and use the consequent associations for styling.

User can associate a Stylesheet with a StoryTeller Document via stl:stylesheet directive. The associated Stylesheet is identified via a Resource URI representing either an external or embedded XML stream.

An External Stylesheet can be associated as follows:

<stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  ...
  <stl:document>
    <stl:stylesheet key="VW" src="wd:/stylesheets/vw.xml"/>
    <stl:stylesheet key="AUDI" src="wd:/stylesheets/audi.xml"/>
    <stl:stylesheet key="SKODA" src="wd:/stylesheets/skoda.xml"/>
    <stl:story tag="LetterBody">
      ...
    </stl:story>
    ...
  </stl:document>
</stl:stl>

And Embedded Stylesheet associations can look like this:

<stl:stl xmlns:xp="http://developer.opentext.com/schemas/storyteller/xmlpreprocessor" 
         xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:fixtures>
    <xp:fixture key="link:/stylesheets/vw.xml">
      <stylesheet name="VW Brand Stylesheet">
        <style selector="LetterBody">
          <character>
            <font>
              <name>Trebuchet MS</name>
            </font>
          </character>
          ...
        </style>    
        ...
      </stylesheet>
    </xp:fixture>
    <xp:fixture key="link:/stylesheets/audi.xml">
      <stylesheet name="Audi Brand Stylesheet">
        ...
      </stylesheet>
    </xp:fixture>
    <xp:fixture key="link:/stylesheets/skoda.xml">
      <stylesheet name="Skoda Brand Stylesheet">
        ...
      </stylesheet>
    </xp:fixture>
  </stl:fixtures>
  <stl:document>
    <stl:stylesheet key="VW" src="link:/stylesheets/vw.xml"/>
    <stl:stylesheet key="AUDI" src="link:/stylesheets/audi.xml"/>
    <stl:stylesheet key="SKODA" src="link:/stylesheets/skoda.xml"/>
    <stl:story tag="LetterBody">
      ...
    </stl:story>
    ...
  </stl:document>
</stl:stl>

Example

Following example demonstrates an embedded stylesheet and it’s application to a story:

STL Definition
Resulting Document

Try in STLEditor »

Content Entities

Paragraph

Users can use stl:p element to create a paragraph.

Any stl:p element can optionally re-define any /paragraph style/ attribute.

/Paragraph style/ properties are specified for every single paragraph and do not get propagated to potentially nested paragraphs (e.g. paragraphs inside a nested switch).

Example

This example demonstrates a variety of stl:p attributes:

STL Definition
Resulting Document

Try in STLEditor »

Span

Users can use stl:span element to change style for enclosed content. Style changes are propagated even to stories associated with nested repeaters or switches.

The fact is that there is no support for hierachical Span entities inside the StoryTeller Document Model (story content contains just special style-change commands changing the current style for all subsequent content in that particular story), but we believe that supporting more HTML-like style support is more user friendly than exposing the internal representation.

Any stl:span element can optionally re-define any character style attribute.

You can change style of a text span as follows:

...
<stl:story>
  <stl:p>This is a <stl:span style="font-weight:bold">bold</stl:span> text</stl:p>
</stl:story>
...

Spans can be nested and associated styles can be arbitrarily combined:

...
<stl:story>
  <stl:p>
    This one contains a span with custom
    <stl:span style="font-family:Arial">
      font and
      <stl:span style="font-size:20pt">
        size
      </stl:span>
    </stl:span>
    .
  </stl:p>
</stl:story>
...

and toggled:

...
<stl:story>
  <stl:p>
    <stl:span style="font-weight:bold; font-style:italic">
      Style properties can be
      <stl:span style="font-weight:normal">individually</stl:span>
      <stl:span style="font-style:normal">toggled</stl:span>.
    </stl:span>
  </stl:p>
</stl:story>
...

Block

A stl:block can span over several stl:p paragraphs as follows:

...
<stl:story>
  <stl:block style="font-family:Courier New">
    <stl:p>This is a monospace styled paragraph</stl:p>
    <stl:p>And this one is monospace too</stl:p>
  </stl:block>
</stl:story>
...

DocBuilder++ Engine makes sure that changed styles get propagated to nested stories:

...
<stl:story>
  <stl:block style="font-family:Courier New">
    <stl:p>
      Span can <stl:span style="font-style:italic">span</stl:span> over several paragraphs and it's style propagates to nested sub-stories:
    </stl:p>
    <stl:p>
      <stl:switch xpath="true">
        <stl:case>
          <stl:story>a) <stl:span style="font-weight:bold">native</stl:span> story</stl:story>
        </stl:case>
      </stl:switch>
    </stl:p>
    <stl:p>
      <stl:repeater xpath="/data">
        <stl:story format="XHTML"><html><body>b) <b>XHTML</b> story</body></html></stl:story>
      </stl:repeater>
    </stl:p>
    <stl:p>
      <stl:content xpath="'&lt;html&gt;&lt;body&gt;c) &lt;b&gt;rich&lt;/b&gt; substitution&lt;/body&gt;&lt;/html&gt;'"/>
    </stl:p>
  </stl:block>
</stl:story>
...

Example

This example demonstrates the usage of a Span:

STL Definition
Resulting Document

Try in STLEditor »

Lists

A relatively frequent use-case is to create a bulleted or numbered list. STL definition provides an stl:list element and also several -stl-list- prefixed CSS properties for that purpose.

The fact is that there is no List object implemented internally in StoryTeller Document Model (list definitions are internally represented as ListStyleSpec resources and referenced from individual paragraphs), but we believe that supporting more HTML-like list creation is more user-friendly than exposing the internal StoryTeller representation (a nice size-effect of such approach is that it also significantly simplifies a direct STL -> HTML conversion).

It means that users can generally create an arbitrary hierarchy of nested stl:list elements (each element representing a definition of the whole list) containing stl:p sub-elements with or without associated bullet/numbering style definition (each sub-element represents either a single list item or it is a plain paragraph with no bullet/numbering).

The Bullets & Numbering syntax is described in a separate document.

Example

This example demonstrates the usage of Lists:

STL Definition
Resulting Document

Try in STLEditor »

Breaks

Users can use an stl:break command to explicitly break formatting of a current line or area. Line breaks can be created as follows:

...
<stl:story>
  <stl:p>
    This is a text
    <stl:break type="line"/>mixed with line-breaks
    <stl:break/>and so broken to several
    <stl:break/>lines.
  </stl:p>
  <stl:p>
    Here we even create
    <stl:break/>several
    <stl:break/>
    <stl:break/>empty
    <stl:break/>
    <stl:break/>lines.
  </stl:p>
</stl:story>
...

Area breaks can help users to create a single paragraph spanning across several areas:

...
<stl:story>
  <stl:p>
    This is another paragraph - this time it contains an area break. 
    The break comes right here: {<stl:break type="area"/>}
    ... and so this text comes to the next area and because 
    of another area break: {<stl:break type="area"/>} 
    ... this text comes to yet another one.
  </stl:p>
</stl:story>
...

Example:

This example demonstrates various kinds of Break commands:

STL Definition
Resulting Document

Try in STLEditor »

Tab Stops

There is another type of command - stl:tab, which can be used for creation of Tab Stops.

Users can simply mix individual stl:tab commands with rest of the text and utilize default Tab Stop definitions as follows:

...
<stl:story>
  <stl:p>
    (left)<stl:tab/>(middle)<stl:tab/>(right)
  </stl:p>
</stl:story>
...

Or they can define their own set of tab definitions for each individual paragraph. Tab stop definition is a plain string with the following syntax:

The tabs attribute colon separated list of tab definitions: <tdef>:...:<tdef> where single definition is a semicolon list: <indent>;<alignment>;<leader>:

This is an example of custom Tab Stop definition:

...
<stl:story>
  <stl:p tabs="100pt:200pt">
    (left)<stl:tab/>(middle)<stl:tab/>(right)
  </stl:p>
</stl:story>
...

Users can even utilize an stl:list for a common Tab Stop definition:

...
<stl:story>
  <stl:list list-mask="%0!1.&#9;" tabs="20pt:250pt;decimal;.">
    <stl:p>Item<stl:tab/> 25,- CZK</stl:p>
    <stl:p>Item<stl:tab/> 138,- CZK</stl:p>
    <stl:p>Item<stl:tab/> 11 324,- CZK</stl:p>
    <stl:p>Item<stl:tab/> 15,- CZK</stl:p>
    <stl:p>Item<stl:tab/> 123 344,- CZK</stl:p>
  </stl:list>
</stl:story>
...

Example:

This example demonstrates usage of Tab Stops:

STL Definition
Resulting Document

Try in STLEditor »

Commands

TBD

TOC styles

TBD

Content Items

Any Content Item can have following optional properties:

Mapping overview

There is untrivial mapping between STL elements and internal DocModel entities. Here is the overview:

Substitution

A substitution serves as an item to be substituted with some externally retrieved content or data. There are various kinds and flavors of substitutions, each behaving bit differently from users’ perspective. Instead of presenting all kinds of substitutions as a single stl:substitution element, we expose individual kinds of substitutions as separate elements:

Data Source

Data Substitution evaluates a given XPath and retrieves a string value from Data Instance:

...
<stl:story>
  <stl:p>
    <stl:field xpath="concat( 'Mr. ', /data/page[@number=2]/heading )"/>
  </stl:p>
</stl:story>
...

Field

Field Substitution evaluates a special value like current page number or number of pages in current document.

...
<stl:story>
  <stl:p>
    <stl:field type="page-number"/>
    <stl:field type="number-of-pages"/>
  </stl:p>
</stl:story>
...

External Content & Repository

External Content Substitution loads an external document (e.g. SSD, DocBuilder++ or RTF) into a cache and evaluates a specific story based on a specified src attribute content a Moniker selector:

...
<stl:fixtures>
  <xp:fixture key="link:/fragments/content.xml">
    <stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
      <stl:document>
        <stl:story name='Main'>
          <stl:p>[ Published <stl:span style="font-style:italic">story</stl:span> inside an embedded fragment ]</stl:p>
        </stl:story>
      </stl:document>
    </stl:stl>
  </xp:fixture>
</stl:fixtures>
...
<stl:story>
  <stl:p>
    <stl:content uri='wd:/Fragment.rtf' selector='/story[1]'/>
    <stl:content uri='wd:/Fragment.ssd' selector='/story[3]'/>
    <stl:content uri='link:/fragments/content.xml' selector='/story[@name="Main"]'/>
  </stl:p>
</stl:story>
...

Repository Substitution directly converts comlete content of an external document (e.g. HTML) with no caching:

...
<stl:story>
  <stl:p>
    <stl:content uri="wd:/Fragment.html"/>
  </stl:p>
</stl:story>
...

Translation

Translation Substitution realizes special phrase -> value lookup.

...
<stl:story>
  <stl:p>
    <stl:translation phrase="Label1"/>
  </stl:p>
</stl:story>
...

Examples

Kitchen sink

This example demonstrates various kinds of Substitutions:

STL Definition
Resulting Document

Try in STLEditor »

External section references

So far we kept the tools for creating design and filling external content separate. Users can use the StoryTeller Editor to create a document design and put any number of Section References to it and then a (possibly different) user can open the StoryBoeard web application and fill individual Sections with actual content (a mixture of HTML text and images).

But what if the business user wants to reorder the existing Section References, delete existing ones or even create new ones from scratch? Would not it be nice if there was an easy way to extend the StoryBoard web editor with such functionality?

One possible approach is to learn the StoryBoard (at least a subset of) STL file syntax. Imagine we provide a set of predefined DocBuilder++ templates containing all the layout but not the main story. Such designs could look like the following:

<stl:stl xmlns:xp="http://developer.opentext.com/schemas/storyteller/xmlpreprocessor" 
         xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:document>
    <xp:include src="wd:/sections-story.xml" />
    <stl:page w="370pt" h="500pt">
    <!-- Two Text Frames (observe a shared story) -->
      <stl:text x="20pt" y="50pt" w="150pt" h="430pt" style="fill:#ffddcc" story="Sections"/>
      <stl:text x="200pt" y="50pt" w="150pt" h="430pt" style="fill:#ffddcc" story="Sections"/>
    </stl:page>
  </stl:document>
</stl:stl>

… note that the main story is not present, but rather externally included.

Let’s say we have the following Section definitions (section_html, section_tables, section_image and section_mixed):

<definition name="DocDefSections" templateVersion="1" templateName="DocDefSections">
  <section id="section_html">
    <resource displayName="Text" path="Fragment.html" type="html" uri="www.example.com/text">
      ...
    </resource>
  </section>
  <section id="section_tables">
    <resource displayName="Tables" path="tables.html" type="html" uri="www.example.com/tables">
      ...
    </resource>
  </section>
  <section id="section_image">
    <resource displayName="Image" path="ducks.png" type="image" uri="www.example.com/image">
      ...
    </resource>
  </section>
  <section id="section_mixed">
    ...
  </section>
</definition>

Then the StoryBoard web editor could let user edit the sequence of the sections and based on his choice generate the actual content of the main story, for example as follows:

<stl:story name="Sections" xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:content uri="strssec:section_html" />
  <stl:content uri="strssec:section_tables" />
  <stl:content uri="strssec:section_image" />
  <stl:content uri="strssec:section_mixed" />
</stl:story>

… and DocBuilder interpreting the STL definition file would simply do the right thing - it would generate a Document Design containing main Story with appropriate sequence of Section References. Document Formatter would then retrieve the corresponding content for each Section Reference and generate the resulting Document Instance.

There is also an alternative approach - utilizing Extenal Content Substitution and postponing interpretation of the dynamic story to runtime. The top level design could look as follows:

<stl:stl xmlns:xp="http://developer.opentext.com/schemas/storyteller/xmlpreprocessor" 
         xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
  <stl:fixtures>
    <xp:fixture key="link:/story-fragment.xml">
      <stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
        <stl:document>
          <xp:include src="wd:/sections-story.xml" />
        </stl:document>
      </stl:stl>
    </xp:fixture>
  </stl:fixtures>

  <stl:document>
    <stl:story name="ExternContent">
      <stl:content uri="link:/story-fragment.xml" selector="/story[1]"/>
    </stl:story>

     <stl:page w="370pt" h="500pt">
      <!-- Two Text Frames (observe a shared story) -->
      <stl:text x="20pt" y="50pt" w="150pt" h="430pt" style="fill:#ffddcc" story="ExternContent"/>
      <stl:text x="200pt" y="50pt" w="150pt" h="430pt" style="fill:#ffddcc" story="ExternContent"/>
    </stl:page>
  </stl:document>
</stl:stl>

There is a disadvantage that the dynamic story is created in runtime with each formatting invoke (obviously there can be some caching involved, but in principle it can be slower than xp:include based solution). On the other hand there is a big advantage that the main document definition does not have to be DocBuilder-based, it is possible to have an SSD created in StoryTeller editor and reference a single DocBuilder-based story via external content substitution. This way we can gradually extend existing documents without supporting full DocBuilder syntax

See the following links for details:

Repeater

Repeater can repeat it’s content several times based on an associated data query.

Data query is specified as xpath attribute.

There are two kinds of a repeater:

Paragraph Repeater

...
<stl:story>
  <stl:p>
    Paragraph Repeater:
    <stl:repeater xpath="/data/character">
      <stl:story>
        <stl:p>Character Name: <stl:field xpath="name"/></stl:p>
      </stl:story>
    </stl:repeater>
  </stl:p>
</stl:story>
...

Inline Repeater

...
<stl:story>
  <stl:p>
    Inline Repeater:
    <stl:repeater xpath="/data/character">
      <stl:story><stl:field xpath="substring(name, 0, 2)"/>, </stl:story>
    </stl:repeater>
  </stl:p>
</stl:story>
...

Example

This example demonstrates the usage of a Repeater:

STL Definition
Resulting Document

Try in STLEditor »

Switch & Scope

Switch

Switch can select a single variant of a sub-content based on an associated data query.

Data query is specified as xpath attribute. There is a stl:switch element with nested stl:case elements. Each nested stl:case element has a unique key attribute which is matched to a result of the data query. There can be a single stl:case element with no key attribute

...
<stl:story>
  <stl:p>
    There 
    <stl:switch xpath="count(/data/character)">
      <stl:case key="0">
        <stl:story>were no characters</stl:story>
      </stl:case>
      <stl:case key="1">
        <stl:story>was one character</stl:story>
      </stl:case>
      <stl:case>
        <stl:story>were <stl:field xpath="count(/data/character)"/> characters</stl:story>
      </stl:case>
    </stl:switch>
    at the party.
  </stl:p>
</stl:story>
...

Any stl:case can reference a shared referencable story which has been previously defined, this was a single content definition can be shared several times as in the following example:

...
<stl:story name="Shared #1" type="referencable">
  was just a handful of characters
</stl:story>
...
<stl:story>
  <stl:p>
    There
    <stl:switch xpath="count(/data/character)">
      <stl:case key="0">
        <stl:story>were no characters</stl:story>
      </stl:case>
      <stl:case key="1" story="Shared #1"/>
      <stl:case key="2" story="Shared #1"/>
      <stl:case key="3" story="Shared #1"/>
      <stl:case key="4" story="Shared #1"/>
      <stl:case>
        <stl:story>were <stl:field xpath="count(/data/character)"/> characters</stl:story>
      </stl:case>
    </stl:switch>
    at the party.
  </stl:p>
</stl:story>
...

Scope

Scope is internally represented as a switch but is slightly different from user standpoint because it has just a single (default) case. Thus the whole stl:case indirection is not necessary and associated sub-content can be nested directly under the stl:scope element.

A scope can be used for nesting a separate story (native or XHTML) inside a hosting story:

...
<stl:story>
  <stl:p>
    This is a scope containing
    <stl:scope>
      <stl:story>
        <stl:span style="font-style:italic">inline native content</stl:span>
      </stl:story>
    </stl:scope>
    or
    <stl:scope>
      <stl:story format="XHTML"><body><i>inline XHTML content.</i></body></stl:story>
    </stl:scope>
  </stl:p>
</stl:story>
...

An stl:scope can also be used as a hyperlink:

...
<stl:story>
  <stl:p>
    And this is a scope with associated
    <stl:scope hyperlink="http://www.python.org" screentip="Python Web">
      <stl:story>
        <stl:span style="text-decoration:underline; color:#0000ff">hyperlink</stl:span>
      </stl:story>
    </stl:scope>.
  </stl:p>
</stl:story>
...

… or as a form if it associates content with a data relation:

...
<stl:story>
  <stl:p>
    This scope associates
    <stl:scope relation="/data/relation">
      <stl:story>
        <stl:span style="font-style:italic">/data/relation</stl:span>
      </stl:story>
    </stl:scope>
    relation.
  </stl:p>
</stl:story>
...

Scope can be also used as a direct story reference:

<stl:story name="Shared #2" type="referencable">
  <stl:p>
    { This is a 
    <stl:span style="font-style:italic">shared</stl:span>
    story }
  </stl:p>
</stl:story>
...
<stl:story>
  <stl:p>
    And this is a scope representing story reference: 
    <stl:scope story="Shared #2"/>
  </stl:p>
</stl:story>
...

And finally - users can use scope to associate semantic tags with content ranges. If a stylesheet is associated with the document, then semantic tags can be utilized for dynamic styling:

<stl:stylesheet>
  <stylesheet name="First set of tests">
    <style selector="hilighted">
      <character>
        <foreground>RGB(255,64,255)</foreground>
        <background>RGBA(128,255,128)</background>
      </character>
    </style>    
  </stylesheet>
</stl:stylesheet>
...
<stl:document>
  <stl:story>
    <stl:p>
      Here we reference styles via
      <stl:scope>
        <stl:story tag="hilighted">semantic tags</stl:story>
      </stl:scope>.
    </stl:p>
  </stl:story>
  ...
</stl:document>
...

Example

This example demonstrates the usage of a Switch:

STL Definition
Resulting Document

Try in STLEditor »

Layout Items

Any Layout Item can have following optional properties:

Text

The stl:text element can represent either a Text Box or a Text Frame depending on the ownership of associated story (it can either define it’s own story or be associated with an existing shared story).

Following listing demonstrates a Text Box defining it’s own private story:

...
<stl:text x="20pt" y="20pt" w="60pt" h="60pt" style="fill:#aabbcc">
  <stl:story>
    <stl:p>This is a private (and native) story...</stl:p>
  </stl:story>
</stl:text>
...

And this listing demonstrates a Text Frame referending a shared story:

...
<stl:text x="120pt" y="20pt" w="60pt" h="60pt" story="Main" style="stroke:#00ff00"/>
...

Example

For Text Box and Text Frame demonstration we can use identical example like for stories:

STL Definition
Resulting Document

Try in STLEditor »

Image

The stl:image element allows to create an Image Item on a Page (inside an stl:page element) or as an inline item inside a story (stl:story element).

Following code creates an absolutely positioned and sized Image on a Page:

...
<stl:page>
  <stl:image x="20pt" y="120pt" w="60pt" h="60pt" src="wd:/ducks.png" style="fill:#aaffff"/>
  ...
</stl:page>
...

Following code creates a relatively positioned and auto-sized inline Image inside a Story:

...
<stl:story>
  <stl:p>
    <stl:image src="wd:/ducks.png" style="stroke:#ff0000"/>
  </stl:p>
</stl:story>
...

Following code creates an inline Image from XHTML embedded Data URI:

...
<stl:story format="XHTML">
  <body style="font-family: Arial">
    <p>
      HTML with embedded image: 
      <img alt="alt text" src="data:image/png;base64,iVBORw0K ... K5CYII="/>
    </p>
  </body>
</stl:story>
...

Following code creates an Image showing 3rd page from a multipage TIFF:

...
<stl:page>
  <stl:image x="20pt" y="20pt" w="100pt" h="50pt" src="wd:/multipage.tiff" page="2"/>
  ...
</stl:page>
...

Example

For demonstration of Image item variants we can look at the following example:

STL Definition
Resulting Document

Try in STLEditor »

Fragment

The stl:fragment element creates a Fragment Item on a Page (inside an stl:page element) or as an inline item inside a story (stl:story element).

Fragments can be either external referenced via an Absolute Moniker like in this case:

...
<stl:fragment x="20pt" y="220pt" w="80pt" h="80pt" transform="scale(10)" category="load-time" src="wd:/fragment.lxf!/item[1]" style="stroke:#ff0000"/>
...

… or embedded using symlink moniker:

...
<stl:fixtures>
  <xp:fixture key="link:/fragments/embedded.xml">
    <stl:stl xmlns:stl="http://developer.opentext.com/schemas/storyteller/layout" version="0.1">
      <stl:document>
        <stl:page w="120pt" h="120pt">
          ...
        </stl:page>
      </stl:document>
    </stl:stl>
  </xp:fixture>
</stl:fixtures>
...
<stl:document>
  ...
  <stl:fragment x="120pt" y="220pt" category="load-time" src="link:/fragments/embedded.xml!/item[1]" style="fill:#ffaaff"/>
</stl:document>
...

Fragments can be absolute positioned on a page:

...
<stl:page w="120pt" h="120pt">
  <stl:fragment x="20pt" y="20pt" category="load-time" src="link:/fragments/embedded.xml!/item[1]" style="fill:#ffaaff"/>
</stl:page>
...

… or relatively positioned as inline object inside a story:

...
<stl:story>
  <stl:p>
    <stl:fragment category="load-time" src="link:/fragments/embedded.xml!/item[1]" style="fill:#ffaaff"/>
  </stl:p>
</stl:story>
...

Example

Folowing example demonstrates definition of Referenced and Embedded Fragments on a Page and inside a Story:

STL Definition
Resulting Document

Try in STLEditor »

Shape

Line & Polyline

Following open shapes are supported:

...
<stl:page w="600pt" h="800pt">
  <stl:line style="stroke:#ff0000" x1="420pt" y1="450pt" x2="550pt" y2="400pt" />
  <stl:polyline style="stroke:#0000ff" points="200,340 240,340 240,380 280,380 280,420 320,420 320,460" />
</stl:page>
...

Rectangle, Circle, Ellipse, Polygon

A variety of closed shapes is also supported:

User can optionally associate a story with any closed shape. Then the story (either shared or private) gets formatted inside the closed shape.

...
<stl:page w="600pt" h="800pt">
  <stl:rect style="fill:#aaffff" x="20pt" y="20pt" w="200pt" h="100pt" story="Main"/>
  <stl:ellipse style="fill:#ffaaff" cx="350pt" cy="70pt" rx="100pt" ry="50pt" story="Main"/>
  <stl:polygon style="fill:#ccffcc" points="250 150 450 200 550 300 300 350" story="Main"/>
  <stl:circle style="fill:#ccccff" cx="120pt" cy="450pt" r="80pt" story="Main"/>
</stl:page>
...

Example:

For demonstration of various shape variants we can look at the following example:

STL Definition
Resulting Document

Try in STLEditor »

Path

There is also a support for very powerfull and generic SVG-like path.

The stl:path element has data attribute containing a SVG Path Data sequence containing commands like moveto, lineto and curveto and can look as follows:

...
<stl:page w="600pt" h="800pt">
  <stl:path fill="#ff0000" data="M10 610 H 100 V 700 H 10 Z"/>
</stl:page>
...

The following commands are available for path data:

All of the commands above can also be expressed with lower letters. Capital letters means absolutely positioned, lower cases means relatively positioned.

References:

Example:

For demonstration of SVG-like path variants we can look at the following example. It also demonstrates how easy it is to migrate an SVG vector image to DocBuilder++ (either manually or programmatically):

SVG Pumpkin

SVG

STL Definition
Resulting Document

Try in STLEditor »

Note that some of the arc handling code has been inspired by the Paul LeBeau’s androidsvg library (which has Apache-2 licence), which borrowed some of the arc-handling code from the Apache Batik library (Apache-2 license as well). Both libraries are implemented in Java programming language.

Table

For Table Item creation there are several docbuilder elements available, namely stl:table, stl:row and stl:cell.

Page Table

User can create an absolutely positioned table as a page item:

The following table has implicit dimensions - it means that it’s width and height are not explicitly specified, but computed based on it’s rows heights and cell/column widths instead:

...
<stl:page w="600pt" h="800pt">
  <!-- Table with implicit dimensions -->
  <stl:table x="20pt" y="20pt">
    <stl:story>
      <stl:row>
        <stl:cell><stl:p>top-left</stl:p></stl:cell>
        <stl:cell><stl:p>top-right</stl:p></stl:cell>
      </stl:row>
      <stl:row>
        <stl:cell><stl:p>bottom-left</stl:p></stl:cell>
        <stl:cell><stl:p>bottom-right</stl:p></stl:cell>
      </stl:row>
    </stl:story>
  </stl:table>
</stl:page>
...

In the example above the rows have zero height by default and are set as growable with its’ content. All cells have 72.0 point width by default (if not stated otherwise).

Alternatively users can explicitly specify height of each individual row and/or specify a width of the whole table:

...
<stl:page w="600pt" h="800pt">
  <!-- Table with explicit width -->
  <stl:table x="300pt" y="20pt" w="250pt">
    <stl:story>
      <stl:row h="30pt">
        <stl:cell><stl:p>top-left</stl:p></stl:cell>
        <stl:cell><stl:p>top-right</stl:p></stl:cell>
      </stl:row>
      <stl:row>
        <stl:cell><stl:p>bottom-left</stl:p></stl:cell>
        <stl:cell><stl:p>bottom-right</stl:p></stl:cell>
      </stl:row>
    </stl:story>
  </stl:table>
</stl:page>
...

Or users can specify column width with specifying w attribute of the cells inside the very first row and also specify a height of the whole table:

...
<stl:page w="600pt" h="800pt">
  <!-- Table with explicit height -->
  <stl:table x="20pt" y="70pt" h="40pt">
    <stl:story>
      <stl:row>
        <stl:cell w="150pt"><stl:p>top-left</stl:p></stl:cell>
        <stl:cell><stl:p>top-right</stl:p></stl:cell>
      </stl:row>
      <stl:row>
        <stl:cell><stl:p>bottom-left</stl:p></stl:cell>
        <stl:cell><stl:p>bottom-right</stl:p></stl:cell>
      </stl:row>
    </stl:story>
  </stl:table>
</stl:page>
...

Or users can combine both concepts - specify all or some of the row heights and cell widths and explicitly specify table width, height or both. The resulting table has the specified table dimensions and row and column dimensions get rescaled accordingly:

...
<stl:page w="600pt" h="800pt">
  <!-- Table with explicit width & height -->
  <stl:table x="300pt" y="70pt" w="250pt" h="40pt">
    <stl:story>
      <stl:row h="30pt">
        <stl:cell w="150pt"><stl:p>top-left</stl:p></stl:cell>
        <stl:cell><stl:p>top-right</stl:p></stl:cell>
      </stl:row>
      <stl:row>
        <stl:cell><stl:p>bottom-left</stl:p></stl:cell>
        <stl:cell><stl:p>bottom-right</stl:p></stl:cell>
      </stl:row>
    </stl:story>
  </stl:table>
</stl:page>
...

Paragraph Table

So far we have seen only simple tables placed absolutely on a page and containing just a body content with no headers and footers.

A content of a table is stored in a private body story by default, but users can create other kinds of stories to achieve various overflow event effects.

It is also possible to repeat one or more rows with a stl:repeater.

Following example demonstrates a paragraph table containing header, footer and a body story with a repeatable row:

...
<stl:document>
  <stl:story name="Main">
    <!-- Paragraph story with header, footer and repeatable body row -->
    <stl:table style="stroke:red; fill:lightyellow">
      <stl:story occurrence="header">
        <stl:row>
          <stl:cell><stl:p>header-left</stl:p></stl:cell>
          <stl:cell><stl:p>header-middle</stl:p></stl:cell>
          <stl:cell><stl:p>header-right</stl:p></stl:cell>
        </stl:row>
      </stl:story>
      <stl:story>
        <stl:repeater xpath="/data/row">
          <stl:row>
            <stl:cell><stl:p><stl:field xpath="position()" />. left</stl:p></stl:cell>
            <stl:cell><stl:p><stl:field xpath="position()" />. middle</stl:p></stl:cell>
            <stl:cell><stl:p><stl:field xpath="position()" />. right</stl:p></stl:cell>
          </stl:row>
        </stl:repeater>         
      </stl:story>
      <stl:story occurrence="footer">
        <stl:row>
          <stl:cell><stl:p>footer-left</stl:p></stl:cell>
          <stl:cell><stl:p>footer-middle</stl:p></stl:cell>
          <stl:cell><stl:p>footer-right</stl:p></stl:cell>
        </stl:row>
      </stl:story>
    </stl:table>
  </stl:story>
  <stl:page w="580pt" h="220pt">
    <!-- Two text frames consuming a paragraph table -->
    <stl:text x="20pt" y="150pt" w="260pt" h="60pt" style="fill:lightcyan" story="Main" />
    <stl:text x="300pt" y="150pt" w="260pt" h="60pt" style="fill:lightcyan" story="Main" />
  </stl:page>
</stl:document>
...

Column span

User can modify a number of columns a cell should span with colspan attribute.

Consider the following example:

...
<stl:table>
  <stl:story>
    <stl:row>
      <stl:cell><stl:p>Name</stl:p></stl:cell>
      <stl:cell colspan="2"><stl:p>Telephone</stl:p></stl:cell>
    </stl:row>
    <stl:row>
      <stl:cell><stl:p>Bill Gates</stl:p></stl:cell>
      <stl:cell><stl:p>55577854</stl:p></stl:cell>
      <stl:cell><stl:p>55577855</stl:p></stl:cell>
    </stl:row>
    ...
  </stl:story>
</stl:table>

… the cell containing text Telephone actually spans across two table columns.

Example

This example demonstrates the usage of various kinds of Tables:

STL Definition
Resulting Document

Try in STLEditor »

Interactive Item

User can use stl:input element to create an interactive item.

The following attributes can be specified:

Then also generic Layout Item, Character Style and Paragraph Style attributes are available.

Interactive items must be always associated with corresponding data hierarchy. For documentation of data structure expected for various kinds of interactive items see:

Following definition creates a simple text input field:

...
<stl:data>
  <stl:template>
    <data xmlns:ddi="http://developer.opentext.com/schemas/storyteller/layout/ddi/v1">
      <ddi:form>
        <plain_input>
          <ddi:input name="drink">Order a drink</ddi:input>
          <ddi:label>Drink</ddi:label>
          <ddi:tooltip>Pick a drink you like, please</ddi:tooltip>
        </plain_input>
        ...
      </ddi:form>
    </data>
  </stl:template>
  <stl:transformation>
    <tdt:transformation xmlns:tdt="http://developer.opentext.com/schemas/storyteller/transformation/tdt" version="1.0"/>
  </stl:transformation>
</stl:data>
<stl:document>
  <stl:page w="300pt" h="150pt">
    <stl:input x="20pt" y="20pt" w="100pt" h="20pt" type="text" xpath="/data/ddi:form/plain_input" 
               style="stroke:lightgray; font-family:Arial; font-size:10pt"/>
    ...
  </stl:page>
</stl:document>
...

And following definition creates a single-select (radio button) choice and submit button:

...
<stl:data>
  <stl:template>
    <data xmlns:ddi="http://developer.opentext.com/schemas/storyteller/layout/ddi/v1">
      <ddi:form>
        <group_input>
          <ddi:input-group multiselect="false" name="Fruits">
            <choice>
              <ddi:choice selected="true">apple</ddi:choice>
              <ddi:label>Apple</ddi:label>
            </choice>
            <choice>
              <ddi:choice selected="false">banana</ddi:choice>
              <ddi:label>Banana</ddi:label>
            </choice>
            <choice>
              <ddi:choice selected="false">plum</ddi:choice>
              <ddi:label>Plum</ddi:label>
            </choice>
          </ddi:input-group>
          <ddi:label/>
          <ddi:tooltip/>
        </group_input>
        <submit>
          <ddi:input name="submit-button">X</ddi:input>
          <ddi:label>Submit</ddi:label>
          <ddi:tooltip>Send form X</ddi:tooltip>
        </submit>
      </ddi:form>
    </data>
  </stl:template>
  <stl:transformation>
    <tdt:transformation xmlns:tdt="http://developer.opentext.com/schemas/storyteller/transformation/tdt" version="1.0"/>
  </stl:transformation>
</stl:data>
<stl:document>
  <stl:page w="300pt" h="150pt">
    <stl:text x="140pt" y="20pt" w="150pt" h="115pt" style="fill:#ffddcc">
      <stl:story name="Shared">
        <stl:p>This is a story containing interactive form:</stl:p>
        <stl:block style="font-family:Arial; font-size:10pt">
          <stl:p>
            <stl:scope relation="/data/ddi:form">
              <stl:story>
                <stl:repeater xpath="/data/ddi:form/group_input/ddi:input-group/choice">
                  <stl:story>
                    <stl:p>
                      <stl:input w="12pt" h="12pt" type="radio" xpath="."/>
                      <stl:field xpath='string(ddi:label)'/>
                    </stl:p>
                  </stl:story>
                </stl:repeater>
                <stl:p style="text-align:center;">
                  <stl:input w="60pt" h="20pt" type="submit" xpath="/data/ddi:form/submit"/>
                </stl:p>
              </stl:story>
            </stl:scope>
          </stl:p>
        </stl:block>
      </stl:story>
    </stl:text>
    ...
  </stl:page>
</stl:document>
...

Example

Following example demonstrates some types of interactive items:

STL Definition
Resulting Document

Try in STLEditor »

Group

User can use stl:group element for page object grouping:

...
<stl:page w="300pt" h="150pt">
  <stl:group transform="rotate(-5)">
    <stl:text x="110pt" y="30pt" w="80pt" h="100pt" style="fill:#ffddcc">
      ...
    </stl:text>
    <stl:rect x="-200pt" y="-200pt" w="600pt" h="600pt" style="fill:yellow" />
  </stl:group>
</stl:page>
...

as well as inline object grouping:

...
<stl:text x="110pt" y="30pt" w="80pt" h="100pt" style="fill:#ffddcc">
  <stl:story>
    <stl:p style="text-align:center;">
      This is a rotated inline group containing text boxes:
      <stl:group transform="rotate(-25)">
        <stl:text x="0pt" y="0pt" w="20pt" h="20pt" style="fill:lightgreen">
          <stl:story><stl:p style="text-align:center;">A</stl:p></stl:story>
        </stl:text>
        <stl:text x="25pt" y="0pt" w="20pt" h="20pt" style="fill:lightblue">
          <stl:story><stl:p style="text-align:center;">B</stl:p></stl:story>
        </stl:text>
      </stl:group>
    </stl:p>
  </stl:story>
</stl:text>
...

Groups can be used for coordinate space transformations (scaling, rotation and translation) or just for organizing the design to collapsable and named groups.

Example

Following example demonstrates the grouping in action:

SVG

SVG

STL Definition
Resulting Document

Try in STLEditor »

Barcode

Users can use stl:barcode element for creating an instance of page or inline barcode.

Following barcode specific attributes are supported:

User can create a barcode right on a page as follows:

...
<stl:page w="300pt" h="150pt">
  <stl:group transform="rotate(-5)">
    <stl:barcode x="20pt" y="20pt" w="50pt" h="50pt" type="Royal Mail" data="123456" />
  </stl:group>
</stl:page>
...

If data must be dynamic (for example if we repeat barcodes based on input data), then it is possible to use stl:script as follows:

...
<stl:story>
  <stl:p>
    This is a sequence of invoice barcodes:
    <stl:repeater xpath="/data/invoice">
      <stl:story>
        <stl:barcode w="30pt" h="20pt" type="Code 39" data=".">
          <stl:attribute name="modulesize">0.3</stl:attribute>
          <stl:attribute name="widetonarrow">2.0</stl:attribute>
          <stl:attribute name="printtext">true</stl:attribute>
          <stl:attribute name="textlocation">Above</stl:attribute>
          <stl:attribute name="checkchar">true</stl:attribute>
          <stl:script language="js">require('layout').item().Data = require('data').js('string(@number)');</stl:script> 
        </stl:barcode>
        , 
      </stl:story>
    </stl:repeater>
  </stl:p>
</stl:story>
...

Example

For demonstration of barcodes we can look at the following example:

STL Definition
Resulting Document

Try in STLEditor »

Charts

Chart syntax documetation is described in a separate document.

Runtime Behavior

Another level of power lies in a possibility to associate runtime elements with individual design items and modify them based on runtime criteria.

Script

Users can associate custom scripts with various items via the stl:script element. Even in a single document design there can be a mixture of languages, each stl:script must have it’s language explicitly specified via the language attribute. Currently supported variants are:

While for Content items there is just a single script, for Layout Items there are two script events Before and After formatting the item. Users can specify the event via the when attribute (either before or after).

...
<stl:image src="wd:/logo.png">
  <stl:script language="js" when="before">
    console.log('Before image');
  </stl:script>
  <stl:script language="py" when="after">
    print('After image');
  </stl:script>
</stl:image>
...

One annoying thing is that you cannot use <, > and & characters in XML PCDATA (which is by default everything in XML). So for example a simple javascript for loop must be escaped as folllows:

...
<stl:script language="js">
 for ( var i=0; i &lt; item.PageCount; ++i )
   console.log(i);
</stl:script>
...

If users find this encoding annoying, they can explicitly switch to CDATA mode as follows:

...
<stl:script language="js"><![CDATA[
 for ( var i=0; i < item.PageCount; ++i )
   console.log(i);
]]></stl:script>
...

Another alternative is to keep scripts separate and use xp:include directive to include them:

...
<stl:script language="js">
  <xp:include src="wd:/scripts/printPageCounts.js" parse="text" />
</stl:script>
...

Modification (deprecated since 16.4)

For simple property modifications users can use the stl:modification element.

Following snipped repeats an image reference and modifies it’s Page property in order to visualize all individual pages of an multipage TIFF image:

...
<stl:repeater xpath="$pages">
  <stl:story>
    <stl:image w="100pt" h="50pt" src="wd:/multipage.tiff">
      <stl:modification key="@IImageItem/Image/Page">.</stl:modification>
    </stl:image>
  </stl:story>
</stl:repeater>
...

Example

For demonstration of runtime modifications we can look at the following example enumerating all pages of an multipage TIFF image:

STL Definition
Resulting Document

Try in STLEditor »