How to generate PDF output containing embedded html tags using xsl-fo

With the report capabilities of Imixs Workflow it is possible to generate PDF files from a process instance using the XML and Rest API. A description and examples of how to generate a PDF Template can be found here.

With the latest Version of the Imixs Rest API it is now also possible to embed HTML output into a PDF file generated using xsl-fo. The Report-Plugin from Imixs-Workflow now preserve embedded HTML and XML structures in a workitem property if the name of the property starts with ‘html’ or ‘xml’.

To transform the HTML block (which have to be well formed XHTML) inside xsl-fo for each html tag a fo template can be applied in the xsl template.

This is a short example how to use this template technique:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">

    <xsl:output encoding="UTF-8" method="xml" indent="yes" />
    <xsl:strip-space elements="*" />
    <xsl:param name="font-size" select="''" />
    <xsl:param name="font.symbol" select="'Arial Unicode MS'" />
    <xsl:template match="/">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
            <fo:layout-master-set>
                <fo:simple-page-master master-name="A4"
                    page-width="210mm" page-height="297mm">
                    <fo:region-body region-name="xsl-region-body"
                        margin="20mm" />
                </fo:simple-page-master>
                <fo:simple-page-master master-name="A4-landscape"
                    page-width="297mm" page-height="210mm">
                    <fo:region-body region-name="xsl-region-body"
                        margin="20mm" />
                </fo:simple-page-master>
            </fo:layout-master-set>

            <fo:page-sequence master-reference="A4">
                <fo:flow flow-name="xsl-region-body">
                    <fo:block space-after="20mm">
                        <fo:block text-align="end" font-size="16pt" font-weight="bold">
                            <fo:inline>
                                HTML - PDF - Example
                            </fo:inline>
                        </fo:block>
                    </fo:block>
                    <fo:block>
                        <fo:block font-weight="bold">Text Output:</fo:block>
                        <fo:block>
                            <xsl:value-of
                                select="/collection/entity[normalize-space(item[name = 'txtworkflowgroup']/value) = 'Frage']/item[name='txtname']/value" />
                        </fo:block>

                        <!-- format html output -->
                        <fo:block font-weight="bold">HTML Output:</fo:block>
                        <fo:block>
                            <xsl:apply-templates
                                select="/collection/entity[normalize-space(item[name = 'txtworkflowgroup']/value) = 'Frage']/item[name='htmlanswer']/value" />
                        </fo:block>
                    </fo:block>
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>

    <xsl:template
        match="/collection/entity[normalize-space(item[name = 'txtworkflowgroup']/value) = 'Frage']/item[name='htmlanswer']/value">
        <xsl:apply-templates />
    </xsl:template>
   
    <!-- html format -->
    <xsl:template match="p">
        <fo:block>
            <fo:inline>
                <xsl:text disable-output-escaping="yes">
                <xsl:apply-templates />
                </xsl:text>
            </fo:inline>
        </fo:block>
    </xsl:template>
    <xsl:template match="b|strong">
        <fo:inline font-weight="bold">
            <xsl:apply-templates />
        </fo:inline>
    </xsl:template>

    <xsl:template match="table">
        <fo:table table-layout="auto" width="100%">
            <fo:table-body>
                <xsl:apply-templates />
            </fo:table-body>
        </fo:table>
    </xsl:template>
    <xsl:template match="tr">
        <fo:table-row>
            <xsl:apply-templates />
        </fo:table-row>
    </xsl:template>

    <xsl:template match="td">
        <fo:table-cell>
            <fo:block>
                <xsl:apply-templates />
            </fo:block>
        </fo:table-cell>
    </xsl:template>

    <xsl:template match="address">
        <fo:block font-style="italic">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="blockquote">
        <fo:block space-before="1em" space-after="1em" start-indent="3em"
            end-indent="3em">

            <xsl:apply-templates />
        </fo:block>
    </xsl:template>

    <xsl:template match="caption">
        <fo:block keep-with-next="always" text-align="center">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="center">
        <fo:block text-align="center">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="div|multicol|noembed|noframes   |nolayer|noscript">
        <fo:block>
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>

    <xsl:template match="h1">
        <fo:block font-size="180%" font-weight="bold">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="h2">
        <fo:block font-size="160%" font-weight="bold">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="h3">
        <fo:block font-size="140%" font-weight="bold">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="h4">
        <fo:block font-size="120%" font-weight="bold">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>
    <xsl:template match="h5">
        <fo:block font-size="110%" font-weight="bold">
            <xsl:apply-templates />
        </fo:block>
    </xsl:template>

    <xsl:template match="br">
        <fo:block white-space="pre">
            <xsl:text disable-output-escaping="yes">&#10;</xsl:text>
        </fo:block>
    </xsl:template>

    <xsl:template match="ol">
        <fo:list-block provisional-label-separation=".2em"
            provisional-distance-between-starts="{string-length(count(li))*.9+.6}em">
            <xsl:apply-templates />
        </fo:list-block>
    </xsl:template>

    <xsl:template match="ol/li">
        <fo:list-item>
            <fo:list-item-label end-indent="label-end()">
                <fo:block text-align="end">
                    <xsl:variable name="value">
                        <xsl:choose>
                            <xsl:when test="@value">
                                <xsl:value-of select="@value" />
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:number />
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:variable>
                    <xsl:choose>
                        <xsl:when test="@type='I'">
                            <xsl:number format="I" value="$value" />
                        </xsl:when>
                        <xsl:when test="@type='A'">
                            <xsl:number format="A" value="$value" />
                        </xsl:when>
                        <xsl:when test="@type='i'">
                            <xsl:number format="i" value="$value" />
                        </xsl:when>
                        <xsl:when test="@type='a'">
                            <xsl:number format="a" value="$value" />
                        </xsl:when>
                        <xsl:when test="parent::ol/@type='I'">
                            <xsl:number format="I" value="$value" />
                        </xsl:when>
                        <xsl:when test="parent::ol/@type='I'">
                            <xsl:number format="A" value="$value" />
                        </xsl:when>
                        <xsl:when test="parent::ol/@type='I'">
                            <xsl:number format="i" value="$value" />
                        </xsl:when>
                        <xsl:when test="parent::ol/@type='I'">
                            <xsl:number format="a" value="$value" />
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:number format="1" value="$value" />
                        </xsl:otherwise>
                    </xsl:choose>
                    <xsl:text>.</xsl:text>
                </fo:block>
            </fo:list-item-label>
            <fo:list-item-body start-indent="body-start()">
                <fo:block>
                    <xsl:apply-templates />
                </fo:block>
            </fo:list-item-body>
        </fo:list-item>
    </xsl:template>

    <xsl:template match="ul|menu">
        <fo:list-block provisional-label-separation=".2em"
            provisional-distance-between-starts="1.6em">
            <xsl:apply-templates />
        </fo:list-block>
    </xsl:template>
    <xsl:template match="ul/li|menu/li">
        <fo:list-item>
            <fo:list-item-label end-indent="label-end()">
                <fo:block text-align="end">
                    <fo:inline font-family="{$font.symbol}">
                        <xsl:choose>
                            <xsl:when test="@type='square'">
                                <xsl:text disable-output-escaping="yes">&#x25AA;</xsl:text>
                            </xsl:when>
                            <xsl:when test="@type='circle'">
                                <xsl:text disable-output-escaping="yes">&#x25CB;</xsl:text>
                            </xsl:when>
                            <xsl:when test="parent::ul/@type='square'">
                                <xsl:text disable-output-escaping="yes">&#x25AA;</xsl:text>
                            </xsl:when>
                            <xsl:when test="parent::ul/@type='circle'">
                                <xsl:text disable-output-escaping="yes">&#x25CB;</xsl:text>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:text disable-output-escaping="yes">&#x2022;</xsl:text>
                            </xsl:otherwise>
                        </xsl:choose>
                    </fo:inline>
                </fo:block>
            </fo:list-item-label>
            <fo:list-item-body start-indent="body-start()">
                <fo:block>
                    <xsl:apply-templates />
                </fo:block>
            </fo:list-item-body>
        </fo:list-item>
    </xsl:template>

    <xsl:template match="font">
        <fo:inline>
            <xsl:choose>
                <xsl:when test="@size=1">
                    <xsl:attribute name="font-size">xx-small</xsl:attribute>
                </xsl:when>
                <xsl:when test="@size=2">
                    <xsl:attribute name="font-size">x-small</xsl:attribute>
                </xsl:when>
                <xsl:when test="@size=3">
                    <xsl:attribute name="font-size">small</xsl:attribute>
                </xsl:when>
                <xsl:when test="@size=4">
                    <xsl:attribute name="font-size">medium</xsl:attribute>
                </xsl:when>
                <xsl:when test="@size=5">
                    <xsl:attribute name="font-size">large</xsl:attribute>
                </xsl:when>
                <xsl:when test="@size=6">
                    <xsl:attribute name="font-size">x-large</xsl:attribute>
                </xsl:when>
                <xsl:when test="@size=7">
                    <xsl:attribute name="font-size">xx-large</xsl:attribute>
                </xsl:when>
            </xsl:choose>
            <xsl:if test="@face">
                <xsl:attribute name="font-family"><xsl:value-of select="@face" /></xsl:attribute>
            </xsl:if>
            <xsl:apply-templates />
        </fo:inline>
    </xsl:template>

    <xsl:template match="small">
        <fo:inline font-size="smaller">
            <xsl:apply-templates />
        </fo:inline>
    </xsl:template>

    <xsl:template match="span">
        <xsl:choose>
            <xsl:when test="self::node()[contains(@style,'underline')]">
                <fo:inline text-decoration="underline">
                    <xsl:apply-templates />
                </fo:inline>
            </xsl:when>
            <xsl:otherwise>
                <fo:inline>
                    <xsl:apply-templates />
                </fo:inline>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="sub">
        <fo:inline baseline-shift="sub">
            <xsl:apply-templates />
        </fo:inline>
    </xsl:template>
    <xsl:template match="sup">
        <fo:inline baseline-shift="super">
            <xsl:apply-templates />
        </fo:inline>
    </xsl:template>


    <xsl:template match="ins|u">
        <fo:inline text-decoration="underline">
            <xsl:apply-templates />
        </fo:inline>
    </xsl:template>
</xsl:stylesheet>

A complete example of a xsl-fo template for html tags can be found here.

Entity Encoding!

If you need to parse XML content make sure that this content did not contain HTML encoded characters like &auml; – this entities contained in the xml source can not be processed by the XSL processor. You need to work with raw utf-8 or unicode encoded characters. For example:

"ä" or "&#228;" works, but "&auml;" wont work!

As we are using TinyMCE Edtior in most of our web applications we need to set the “raw” format to initialize TinyMCE. Otherwise characters will be HTML encoded!

$('textarea.imixs-editor')            
            .tinymce(
                    { ....
                    .... // set entity encoding
                        entity_encoding: "raw"
                    });
        }

Building a Web application with Imixs-Workflow – Part III.

NOTE: Please see the latest tutorial how to run Imixs-Workflow with Jakarta EE.

In the first part of my tutorial I explained how to define a business model using the Imixs Workflow Modeler. The second part describes how to setup the Java EE Application server. Now its time to complete the tutorial and deploy the workflow application!

As you have seen I spent much time in explaining the modeling process and the server configuration. The reason is that the deployment of the Imixs Workflow application is really easy and did not take much time. I will explain two szenarious. First I will show how to dpeloy the Imixs Workflow Sample application. Next I explain how to checkout the sources and use the Sample Application as a scaffolder to build your own application. Continue reading “Building a Web application with Imixs-Workflow – Part III.”

Building a Web application with Imixs-Workflow – Part II.

NOTE: Please see the latest tutorial how to run Imixs-Workflow with Jakarta EE.

In the first part of my tutorial I explained how you create a workflow model using the Imixs Workflow Modeler. This part now concerns about the setup of your application server on which you can run the workflow application later. I will describe the setup of the Application Server Glassfish which is common running a JEE application. But you can also use a different JEE application servers (e.g. JBoss, WildFly). Continue reading “Building a Web application with Imixs-Workflow – Part II.”

Building a Web application with Imixs-Workflow – Part I.

NOTE: Please see the latest tutorial how to run Imixs-Worklfow with Jakarta EE.

This Tutorial shows how to build a web based workflow application using the Open Source framework Imixs-Workflow. Imixs-Workflow gives you a powerful technology building a business process management system (BPMS). It is mostly easy to setup a new web application without spending to much time into development. So you can focus on the business process and your customers needs.

The Tutorial consist of three parts.

  • Part I. – shows the creation of a workflow model. The workflow model is the blueprint where you describe how your business process should work. You can use the Eclipse based graphical Imixs-Workflow Modeler to create a Workflow model.
  • Part II. – concerns about the setup of your application server which is responsible to authenticate users and store workitems (a running process instance) into a database.
  • Part III. – will show you how to build and deploy the web application. This is mostly easy as you can use Maven to setup a scaffold in a few seconds which will provide you with a typical web application project based on the latest JEE/JSF Technology. You can use the scaffold to start further development or just start you own implementation. Its up to you.

The hole tutorial will take less than one hour. You can find the Example Application also on GitHub. Continue reading “Building a Web application with Imixs-Workflow – Part I.”