Search code examples
xsl-foapache-fophyphenation

No hyphenation in hyphenated block


Is there a possibility to disable hyphenation for a text snippet inside a fo:block? My problem is That it’s not possible to set hyphenate="false" on fo:inline because it’s not a property. And fo:block inside fo:block creates a new line...

Fo-Example:

<fo:block hyphenate="true">
This text should be hyphenated <fo:inline hyphenate="false">This text shouldn’t be hyphenated</fo:inline>
</fo:block>

UPDATE:

I tried the soulution Posted by @DanielNorberg because none of all solutions worked properly. It seems to be kind of a workaround but still not delivering the output i would like to get.

I use this template:

<xsl:template match="text()[ancestor::span[@class='nobreak']]">
    <xsl:param name="text"><xsl:value-of select="." /></xsl:param>
    <fo:inline hyphenate="false" color="red">
        <xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
        <fo:inline keep-with-next.within-line="always">
            <xsl:value-of select="."/>
            <fo:character font-size="0pt">
                <xsl:value-of select="' '" />
            </fo:character>
        </fo:inline>
        </xsl:for-each>
    </fo:inline>
</xsl:template>

The fo part looks like this

<fo:block xmlns:fn="http://www.w3.org/2005/xpath-functions" space-after="14pt">
    <fo:block text-align-last="left" font-size="10pt" color="black" text-align="justify"
        font-family="ITCFranklinGothicStd-Book" line-height="14pt" wrap-option="wrap">
        <fo:block hyphenate="true" xml:lang="de">
            <fo:block>Die Entwicklung der folgenden Jahre bestätigte unsere Auffassung. Nicht nur erwiesen
                sich die <fo:inline hyphenate="false" color="red">
                    <fo:inline keep-with-next.within-line="always">T<fo:character font-size="0pt"> </fo:character></fo:inline>
                    <fo:inline keep-with-next.within-line="always">r<fo:character font-size="0pt"> </fo:character></fo:inline>
                    <fo:inline keep-with-next.within-line="always">e<fo:character font-size="0pt"> </fo:character></fo:inline>
                    <fo:inline keep-with-next.within-line="always">i<fo:character font-size="0pt"> </fo:character></fo:inline>
                    <fo:inline keep-with-next.within-line="always">b<fo:character font-size="0pt"> </fo:character></fo:inline>
                    <fo:inline keep-with-next.within-line="always">e<fo:character font-size="0pt"> </fo:character></fo:inline>
                    <fo:inline keep-with-next.within-line="always">r<fo:character font-size="0pt"> </fo:character></fo:inline>
                </fo:inline>
            </fo:block>
        </fo:block>
    </fo:block>
</fo:block>

So the word "Treiber" should not be hyphenated. But the PDF output looks the following:

"Treiber" is still some sort of hyphenated

SOLUTION UPDATE: The final workaround that worked for me was similar to the template above but with adding a non-breaking space (⁠) between each character.

<xsl:template match="text()[ancestor::span[@class='nobreak']]">
    <xsl:param name="text"><xsl:value-of select="replace(., ' ', '&#160;')" /></xsl:param>
    <fo:inline>
        <xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
        <fo:inline>
            <xsl:value-of select="."/>
            <!-- non-breaking invisible space after each character-->
            <fo:inline>&#8288;</fo:inline>
        </fo:inline>
        </xsl:for-each>
    </fo:inline>
</xsl:template>

Much thanks to @DanielNorberg


Solution

  • I found a solution for this (verified for FOP 2.2). The code "disables" hyphenation and keeps the inline content on the same line. It's of course a hack with no guarantee to work for later releases but it works for my purpose, maybe it can help you as well.

    Sample XML:

    <block>Lorem ipsum <inline class='nobreak>This is neither hyphenated or line broken</inline>.</block>
    

    XSLT 2.0 solution:

    Create a parent inline element (XSLT 1.0 and 2.0):

    <xsl:template match="inline[@class='nobreak"]">
       <fo:inline>
          <xsl:apply-templates />
       </fo:inline>
    </xsl:template>
    

    "Disable" hyphenation and make the text content to be kept on the same line (XSLT 2.0):

    <xsl:template match="text()[ancestor::inline[@class='nobreak']]">
        <xsl:param name="text"><xsl:value-of select="." /></xsl:param>
        <xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
           <fo:inline keep-with-next.within-line="always">
              <xsl:value-of select="."/>
              <fo:character font-size="0pt">
                 <xsl:value-of select="' '" />
              </fo:character>
           </fo:inline>
        </xsl:for-each>
    </xsl:template>
    

    UPDATE XSLT 2.0, this seems to be better:

    <xsl:template match="text()[ancestor::phrase[@class='nobreak']]">
        <xsl:param name="text"><xsl:value-of select="replace(., ' ', '&#160;')" /></xsl:param>
        <xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')">
           <fo:inline>
              <xsl:value-of select="."/>
              <fo:inline font-size="0pt">
                 <xsl:value-of select="'$'" />
              </fo:inline>
           </fo:inline>
        </xsl:for-each>
    </xsl:template>
    

    If you want an XSLT 1.0 solution for this you can easily find code on the web that parses the text one character at the time.