Preliminary announcement of results for year ended 31 December 2018

14 March 2019


Stilo International plc (“Stilo”, the “Group” or the “Company”) today announces its results for the year ended 31 December 2018. The Company develops software tools and cloud services that help organisations create and process structured content in XML format so that it can be more easily stored, managed, reused, translated and published to multiple print and digital channels.


  • Sales revenues of £1,487,000 (2017: £1,894,000).
  • Profit after tax of £177,000 (2017: £313,000).
  • Reduction in operating costs, net of capitalised development costs, to £1,358,000 (2017: £1,591,000), primarily due to favourable currency exchange rates.
  • Investment in total product development of £583,000 (2017: £656,000) of which £213,000 capitalised (2017: £213,000).
  • Cash of £1,271,000 as at 31 December 2018 (2017: £1,621,000), with reduction largely due to continued investment in development projects and dividend payments to shareholders.
  • Final dividend proposed of 0.06 pence per Ordinary Share, providing a total dividend of 0.12 pence for the year (2017: total 0.10 pence).


  • Total sales revenues for the period decreased significantly, principally due to an expected reduction in OmniMark-related revenues from one major customer.
  • Migrate revenues held up well given the expiry of a significant contract from earlier years. Customers during the period included Edwards Lifesciences, Visa, Viewpoint, ARRIS, Synopsys, Deltek, Varian and TIBCO.
  • AuthorBridge beginning to get traction with new customers including Kaplan Professional, Intel and Coriolis.
  • Initiated the development of OptimizeR – a new tool to help customers deduplicate their DITA content, improve content consistency and maximise the opportunity for content reuse.

David Ashman, Chairman, commenting on the Company’s performance, stated:

“Total sales revenues for the period decreased significantly, principally due to an expected reduction in OmniMark-related revenues from one major customer. However, it was encouraging that our Migrate revenues held up well, as new customer wins compensated for the expiry of a major contract. We were also successful in making some initial breakthroughs with sales of AuthorBridge to new customers.

Given our size, we continue to incur significant financial overheads associated with being a public listed company, but notwithstanding this we were able to generate a post-tax profit for the period of £177,000.

The Company continues to invest in the development of leading technologies for the structured content market and in so doing build long-term value for shareholders. As we look forward to growing future sales, supported by healthy cash reserves and a strong balance sheet, I am pleased to announce the payment of an increased final dividend of 0.06 pence per share, providing a total dividend for the year of 0.12 pence”.

Download a PDF of the full 2018 preliminary results announcement.

This announcement contains inside information for the purposes of Article 7 of EU Regulation 596/2014.


Stilo develops software tools and cloud services that help organisations create and process structured content in XML format, so that it can be more easily stored, managed, reused, translated and published to multiple print and digital channels.

Over recent years, many organisations have adopted industry specific XML standards e.g. Publishing (DocBook), Aerospace & Defence (S1000D), Finance (XBRL), Life Sciences (SPL), Scientific and Scholarly Publishing (JATS), Software and High Tech (DITA). Stilo made the decision some years ago to focus new product development and marketing efforts on the emerging DITA standard. This standard originated within IBM to support the publishing of its technical documentation and has been increasingly adopted by other software and high tech companies. DITA is now beginning to make inroads into additional market sectors including Manufacturing, Life Sciences and Publishing.

In early 2018 we undertook some exploratory development and marketing efforts for the JATS market but these were put on hold mid-year in order to focus resources on the major release of AuthorBridge v3 for the DITA market.

In the medium term, given limited resources, we will look to diversify beyond the DITA market through potential partnering opportunities and the incremental development of AuthorBridge, Migrate and OptimizeR.

We continue to build upon our strong reputation for excellent products and supporting technical expertise, resulting from many years of experience in the structured content marketplace. With offices in the UK and Canada, we support clients throughout North America, Europe and Japan.



Stilo’s core technology is OmniMark, a long-established development platform used to build high-performance content processing applications integral to enterprise publishing solutions.

Users include Pratt and Whitney, Airbus Defence & Space, Clarivate Analytics and Wolters Kluwer.

Sales for the year included orders from the European Parliament, Japan Patent Office, Qantas and Gulfstream.


Migrate is the world’s first cloud XML content conversion service and utilises OmniMark technology. Through advanced levels of automation, it enables organisations to improve turnaround times, reduce operating costs and take direct control of their work schedules, providing an attractive alternative to traditional outsourced conversion services.

Migrate sales for the period include orders from Edwards Lifesciences, Visa, Viewpoint, ARRIS, Synopsys, Deltek, Varian and TIBCO.

Using Migrate, we have helped our customers convert over one million pages of content to the DITA standard.


Complementing Migrate, OptimizeR is a tool that we are developing to help automate the deduplication of DITA content, improve content consistency and help maximise the opportunity for content reuse. This can be particularly important in highly regulated or hazardous environments.

In 2018 the tool was in the research and early development phase with all expenses recognised in the profit or loss. Initial customer testing is due to start in the coming months and we expect to make OptimizeR generally available to customers mid-year 2019.


AuthorBridge is a web-based XML authoring tool, designed for content contributors who have no knowledge of XML or its complexities. It is currently targeted at large enterprises, which are looking to extend the use of DITA across different business units and potentially support hundreds of users.

The development of AuthorBridge has been a major effort over several years, culminating in the release of AuthorBridge v3 in early 2019. This release helps position AuthorBridge at the forefront of web-based DITA editing tools, albeit there are some additional developments that we need to undertake to round it off. Its initial deployment in production at IBM, following extensive co-operation and testing by the central IBM Information Developer Tools team, serves as a good foundation upon which we can build future sales. Other early stage customers include the Nuclear Regulatory Commission, Intel, Kaplan Professional and Coriolis.

Sales analysis by geographic region

Our customers typically comprise large organisations, and are spread globally. Geographic sales revenues were derived as follows:

Region 2018 2017
UK 5% 2%
Rest of Europe 12% 12%
North America 62% 51%
South America 4% 3%
Asia 17% 32%

North America continues to represent a significant proportion of sales revenues as adoption of the DITA standard has been primarily led by corporations with their headquarters based in the USA. It is anticipated that adoption of the DITA standard will spread internationally over the coming years.

Technical expertise

Our technical team includes leading experts in the development of XML content processing technologies and along with our support services, are very highly regarded by customers.

There is a high level of synergy between our products which results in very efficient integrated development and support activities.


Stilo operates from offices located in Swindon, UK and Ottawa, Canada. The technical team is based in our Ottawa office.

As at 31 December 2018, there were 18 permanent employees in the Group, complemented by the use of contractors. It is not anticipated that we will be growing headcount significantly, as we look to contain our costs and scale the business through technology sales.


The results for the year ended 31 December 2018 have been prepared in accordance with the recognition and measurement principles of International Financial Reporting Standards as adopted by the European Union.

In 2018, the results for Stilo show a decrease in EBITDA to £148,000 (2017: £315,000). Post tax profits were £177,000 (2017: £313,000).

Total sales revenues for the year decreased to £1,487,000 (2017: £1,894,000), principally due to an expected reduction in OmniMark-related revenues from one major customer.

The Group continued to benefit from recurring revenue from software maintenance contracts of £816,000 (2017: £930,000) which represents 55% (2017: 49%) of annual sales revenue.

The Group continues to maintain careful control over operating costs. Operating expenses, excluding capitalised development costs, were reduced to £1,358,000 (2017: £1,591,000). This decrease has been driven by a fall in staff costs and favourable currency exchange rates in the current year.

Investment in research and development continued in 2018, with total expenditure for the year of £583,000 (2017: £656,000). As a result of this investment, Stilo continues to benefit from research and development tax credits. Of this expenditure, £213,000 (2017: £213,000) relating to the development of AuthorBridge has been capitalised, and the total accumulated capitalised costs will be depreciated over a 10 year period, commencing in 2019.

There was a cash balance of £1,271,000 as at 31 December 2018 (31 December 2017: £1,621,000), with the reduction being largely due to continued investment in development projects and dividend payments to shareholders. Stilo remains entirely un-geared. This Statement of Financial Position stability provides a sound financial base for the Group and will support continued investment in product development, sales and marketing. Costs will continue to be carefully managed in order to maintain cash reserves at a satisfactory level.

Total trade receivables were £224,000 (2017: £126,000), equating to 55 days (2017: 24 days). Overdue amounts are closely monitored.

The directors monitor the performance of the Group based on the above key performance indicators.


The Board recommends the payment of a final dividend for the year of 0.06 pence per Ordinary Share which, if approved by the shareholders at the AGM on 23 May 2019, will be paid on 30 May 2019 to shareholders on the register on the Record Date of 23 April 2019. The shares will be marked ex-dividend on 18 April 2019. If approved, payment of the final dividend will bring the total dividends paid to shareholders for the year to 0.12 pence per Ordinary Share.

The Board’s policy is to maintain payment of a steady and progressive dividend, well covered and paid subject to maintaining sufficient funds within the business with regard to prudent forecasts of future capital requirements, without the need for debt funding.


The long-awaited release of AuthorBridge v3 in 2019 provides for a best-in-class, web authoring tool for the DITA market. However, the DITA market has well-established competitors and it will likely take some time to build significant new revenue streams with customers and technology partners.
In the short term, growth will be primarily driven by sales of Migrate and OptimizeR solutions to new customers and we will be stepping up our sales, marketing and development efforts accordingly. 2019 is going to be a challenging year for the Company, with potential demand, as always, difficult to predict at the current time.


Stilo International plc
Les Burnham, Chief Executive
T +44 1793 441 444

SPARK Advisory Partners Limited (Nominated Adviser)
Neil Baldwin T +44 203 368 3554
Mark Brady  T +44 203 368 3551

SI Capital (Broker)
Nick Emerson
T +44 1483 413500

Breaking lines on your own with OmniMark

By Jacques Légaré, Senior Software Developer and Mario Blažević, Senior Software Developer
1. Motivation

OmniMark has had line-breaking functionality built-in ever since the XTRAN days. This functionality can be used to provide rudimentary text formatting capabilities. The language-level support for line-breaking is described quite thoroughly in the language documentation.

OmniMark’s language-level line-breaking support is very simple to use, and aptly supports the use-case where all the output of a program needs to be similarly formatted. Where the performance is less stellar, however, is when line-breaking needs to be activated and deactivated on a fine-grained level. The reason for this is simple: when line-breaking is disabled (say, using the h modifier), OmniMark cannot predict when it might be reactivated. As a result, it still needs to compute possible line-breaking points, just in case. As efficient as OmniMark might be, this can cause a significant reduction in performance, sometimes by as much as 15%.

As of version 8.1.0, the OmniMark compiler can detect some cases when line-breaking functionality is not being used, and thereby optimize the resulting compiled program to by pass line-breaking computations. However, the compiler cannot make this determination in general: this is an undecidable problem. For instance, consider the following somewhat contrived example:

replacement-break ” “ “%n”process do sgml-parse document scan #main-input output “%c” doneelement #implied output “%c” element “b” local stream s set s with (break-width 72) to “%c” output s

Note that line-breaking is only activated in the element rule for b, and so line-breaking will only be activated if the input file contains an element b. The OmniMark compiler cannot be expected to predict what the input files might contain when the program is executed!

Another issue with OmniMark’s built-in line-breaking is that it does not play well with referents. Specifically, consider the following program:

replacement-break ” “ “%n”process local stream sopen s as buffer with break-width 32 to 32 using output as s do xml-parse scan #main-input output “%c” done close s element #implied output “%c”  || “.” ||* 64

This program puts a hard limit of 32 characters on the maximum length of lines output to s. When this program is executed, a run-time error is triggered in the body of the element rule, where we attempt to output 64 periods. On the other hand, consider the following similar program:

replacement-break ” “ “%n”process local stream sopen s as buffer with (referents-allowed & break-width 32 to 32) using output as s do xml-parse scan #main-input output “%c” done close s set referent “a” to “.” ||* 64 output s element #implied output “%c”  || referent “a”

This program accomplishes virtually the same task, but instead uses a referent to output the string of periods. In this case, no run-time error is triggered: the line-breaking constraints have been silently violated.

Because of these issues, it is better to use OmniMark’s built-in line-breaking only when necessary, whereas in other cases to implement line-breaking using other language constructs.

The remainder of this article discusses how to simulate line-breaking on PCDATA using string sink functions.

2. string sink functions

string sink function is a function that can be used as the destination for strings. In a very real sense, a string sink function is the complement of a string source function, which is used as the source of strings. While a string source function outputs its strings to #current-output, a string sink function reads its strings from #current-input.

string sink function is defined much like any other function in OmniMark, the only difference being that the return type is string sink: for example,

define string sink function dev-null as void #current-input

This is a poor man’s #suppress, soaking up anything written to it.

string sink function can have any of the properties normally used to define functions in OmniMark: e.g., it can be  overloadeddynamicetc …. The argument list of a string sink function is unrestricted. However, in the body of a string sink function, #current-output is unattached.The form of OmniMark’s pattern matching and markup parsing capabilities makes string sink functions particularly convenient for writing filters, taking their #current-input, processing it in some fashion, and writing the result out to some destination. However, since #current-output is unattached inside the function, we need to pass the destination as an argument. For this, we use a value string sink argument. For example, a string sink function that indents its input by a given amount might be written

define string sink function
 indent (value integer     i,
 value string sink s)
 using output as s
 output ” “ ||* i       repeat scan #current-input
 match “%n”
 output “%n” || ” “ ||* i      match any-text* => t
 output t

The function indent could then be used like any other string sink:

; …
 using output as indent (5, #current-output)
 do sgml-parse document scan #current-input
 output “%c”

(The ability to pass #current-output as a value string sink argument is new in OmniMark 8.1.0.)

You can find out more about string sink functions in the language documentation.

3.  Line-breaking in OmniMark

We can use a pair of string sink functions to simulate to some extent OmniMark’s built-in line-breaking functionality. The benefit of this approach is that it impacts the program’s performance only where it is used.

3.1. Simulating insertion-break

To simulate the effect of insertion-break on PCDATA we need to scan the input and grab as many characters as we can up to a specified width. If we encounter a newline in the process, we stop scanning. Otherwise, we output the characters we found, and append a line-breaking sequence provided by the user.

define string sink function
 insertion-break       value string      insertion
 width value integer     target-width
 into value string sink destination

We can start by sanitizing our arguments:

assert insertion matches any ** “%n”
 message “The insertion string %”” || insertion
 || “%” does not contain a newline character %”%%n%”.”

This assertion is not strictly necessary. However, OmniMark insists that the line-breaking sequence contain a line-end character, and so we do the same.

We can grab a sufficient number of characters from #current-input by using OmniMark’s counted occurrence pattern operator:

using output as destination
 repeat scan #current-input
 match any-text{1 to target-width} => l (lookahead “%n” => n)?
 output l

The use of lookahead at the end of the pattern allows us to verify if a %n is upcoming: we should only output the line-breaking sequence if the characters we grabbed are not followed by a %n.

output insertion
 unless n is specified   match “%n”
 output “%n”

We can then use this to break the text output from the markup parser: for example,

 using output as insertion-break “%n” width 20 into #current-output
 do sgml-parse document scan #main-input
 output “%c”

3.2. Simulating replacement-break

Simulating insertion-break on PCDATA is straightforward, because it can insert a line-breaking sequence whenever it sees fit. On the other hand, replacement-break is slightly more complex, since it must scan its input for a breakable point. For clarity, the characters between two breakable points will be referred to aswords; if the breakable points are defined by the space character, they are effectively words.

define string sink function
 replacement-break       value string      replacement
 width value integer     target-width
 to value integer     max-width    optional
 at value string      original     optional initial { ” “}
 into value string sink destination

The argument original is used to specify the character that delimits words; the argument is optional, as a space character seems like a reasonable default. target-width specifies the desired width of the line. max-width, if specified, gives the absolute maximum acceptable line width; if a line cannot be broken within this margin, an error is thrown. Finally, the argument replacement gives the line-breaking sequence.

As before, we start by ensuring our arguments have reasonable values:

assert length of original = 1
 message “Expecting a single character string,”
 || ” but received %”” || original || “%”.”   assert replacementmatches any ** “%n”
 message “The replacement string %”” || replacement
 || “%” does not contain a newline character %”%%n%”.”

The second assertion is repeated from above, for the same reasons as earlier: OmniMark insists that the replacement string contain a newline, and so we will do the same. The first assertion insists that breakable points be defined by a single character; again, this is a carry-over from OmniMark’s implementation.

For replacement-break, the pattern is very different from that of insertion-break: in that case, we could consume everything with a single pattern, using a counted occurrence. This does not suffice with replacement-break: rather, we have to consume words until we reach target-width.

using output as destination
 local stream line initial { “” }

The stream line will be used to accumulate text from one iteration to another.

repeat scan #current-input
 match ((original => replaceable)? any-text
 ** lookahead (original | “%n” | value-end)) => t

The pattern in the match clause picks up individual words. If the line length is still below target-width, we can simply append the word to the current line and continue with the next iteration:

do when length of line + length of t < target-width
 set line with append to t

If this is not the case, we can output the text we have accumulated thus far, so long as it does not surpassmax-width

else when max-width isnt specified
 | length of line < max-width
 output line
 output replacement
 when replaceable is specified            set line to t droporiginal?

If all else fails, we could not find an acceptable breakable point in the line: OmniMark throws an error in this case, so we will do the same.

 not-reached message “Exceeded maximum line width”
 || ” of %d(max-width) characters.%n”
 || “The line is %”” || line || “%”.%n”         done

Our string sink function needs a few more lines to be complete. For one, our previous pattern does not consume any %n that it might encounter. In this case, we should flush the accumulated text, and append a%n:

match “%n”
 output line || “%n”
 set line to “”

Lastly, when the repeat scan loop finishes, there may be some text left over in line, which needs to be emitted:

output line

Just as was the case previously in Section 3.1, “Simulating insertion-break”, we can use our function to break text output from the markup parser: for example,

 using output as replacement-break “%n” width 10 to 15 into #main-output
 do sgml-parse document scan #main-input
 output “%c”
4.  Going further

We demonstrated in Section 1, “Motivation” that referents and line-breaking did not play well together: in fact, a referent could be used to silently violate the constraints stated by a break-width declaration. In the case of our string sink simulations, referents are a non-issue: a referent cannot be written to an internal string sink function, which effectively closes the loophole.

OmniMark’s built-in line-breaking functionality can be manipulated using the special sequences %[ and %]: by embedding one of these in a string that is output to a stream, we can activate or deactivate (respectively) line-breaking. The easiest way of achieving this effect with our string sink functions would be to add a read-only switch argument called, say, enabledviz

define string sink function
 insertion-break         value     string      insertion
 width value     integer     target-width
 enabled read-only switch      enabled      optional
 into value     string sink destination

and similarly for replacement-break. We could then use the value of this shelf item to dictate whether the functions should actively break their input lines, or pass them through unmodified.

Breaking lines using string sink functions in this fashion is really only the beginning. For instance, we could envision a few simple modifications to replacement-break that would allow it to fill paragraphs instead of breaking lines: it would attempt to fill out a block of text so that all the lines are of similar lengths.

The code for this article is available for download.

How to prepare your content for conversion to DITA

Presented by Helen St. Denis, Conversion Services Manager | Stilo

So, the decision is made to implement DITA and the content audit is done, now you need to get your content into DITA. So, what do you really need to do to your content before you start the conversion process? Maybe not as much as you may think!

In this webinar we discussed …

  • What is the most useful thing to do pre-conversion?
  • What kind of things influence what else you might want to do pre-conversion?
  • What are the common trouble areas in the different source formats?
  • What is best left for post-conversion?

View recording (registration required)


Meet the presenter

Helen St. Denis

Helen originally joined Stilo as a technical editor in the documentation team, and now works closely with Stilo Migrate customers, helping to analyse their legacy content and configure appropriate mapping rules. She also provides Migrate customer training and support.
Over a period of several years, Helen has helped Migrate customers to convert tens of thousands of pages of content to DITA and custom XML.

Helen holds a BA in English from St. Francis Xavier University in Antigonish, Nova Scotia and has pursued graduate studies at Queen’s University in Kingston Ontario.

Directorate Change

28 July 2016


By mutual agreement, Richard Alsept will resign from his position as Director, CFO and Company Secretary of Stilo International plc (“Stilo” or “the Company”) as of 12 September 2016, subsequent to the anticipated announcement on 1 September 2016 of its interim results for the 6 months ended 30 June 2016. The management of the Company’s finance and accounting activities will in future be managed primarily out of Stilo’s Canadian office, located in Ottawa, from which the majority of the operations emanate and staff are located. The Board anticipates that a UK-based Company Secretary will be appointed in the coming days, and will be the subject of a separate announcement

The Board would like to thank Richard for his contribution, over many years, to the ongoing success of Stilo.


Stilo International plc
Les Burnham, Chief Executive
T +44 1793 441 444

SPARK Advisory Partners Limited (Nominated Adviser)
Neil Baldwin T +44 203 368 3554
Mark Brady  T +44 203 368 3551

SI Capital (Broker)
Andy Thacker
Nick Emerson
T +44 1483 413500