3

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

CrazyEight
  • 147
  • 1
  • 18

5 Answers5

3

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.

  • Wow if that works that’s gonna be great. I will try this as soon as possible. Thanks! – CrazyEight Feb 04 '20 at 10:08
  • Ok i tried now as you said. It sadly doesnt really fix the problem for me. my .fo output is the following: ```xml Die Entwicklung der folgenden Jahre bestätigte unsere Auffassung. Nicht nur erwiesen sich die T r von gestern... ``` – CrazyEight Feb 04 '20 at 22:02
  • 1
    I'll check your fo code as soon as possible. Hopefully I can find something obvious. – Daniel Norberg Feb 05 '20 at 05:43
  • I can unfortunately not post my whole fo code because of the character limitation... I will update my Post so u can see my fo and pdf output – CrazyEight Feb 05 '20 at 08:56
  • i updated my Original Post. Hopefully u have all necessary information – CrazyEight Feb 05 '20 at 09:32
  • 1
    I updated my comment with new XSLT. See "UPDATE XSLT 2.0". It seems to work better. Can you try? – Daniel Norberg Feb 05 '20 at 10:54
  • 1
    @CrazyEight Did you see that I updated the XSLT? I am new on Stack Overflow and do not have enough rep to add comments to your original question but you can see the new XSLT in the updated answer. – Daniel Norberg Feb 06 '20 at 08:24
  • I got a meesage that u updated your answer. And WOW it works!!! Thank you very much. Ive been struggeling with this so long! – CrazyEight Feb 06 '20 at 09:07
  • Glad to hear that :-) I am happy too since I needed this badly myself. I searched around for solutions first but found none. Now there is at least a workaround for this on the web if others have the same issue. – Daniel Norberg Feb 06 '20 at 11:17
  • I had a little different solution with adding a non-breaking space after each character. I updated my Question if u want to see the final solution that worked for me. As i tried out your solution i recognised that words connected by a "-" were still hyphenated. With non breaking space i could not find any hyphenations anymore. Thanks for your big help! – CrazyEight Feb 07 '20 at 08:27
  • Thanks! I will investigate that with my own stylesheet as soon as possible. – Daniel Norberg Feb 07 '20 at 11:00
1

The feature that should do the trick is:

<fo:block hyphenate="true">
  This text should be hyphenated 
  <fo:inline keep-together.within-line="always">This text shouldn’t be hyphenated</fo:inline>
</fo:block>

but was reported to be buggy some years ago. So, if you have a version of fop that is recent enough, you could try it.

Pierre François
  • 5,850
  • 1
  • 17
  • 38
1

I would think the only solution would be XSL FO markup that would be like this:

<fo:block hyphenate="true">
<fo:inline>I am some text that can be hyphenated. </fo:inline> 
<fo:inline keep-together.within-line="always">I</fo:inline> <fo:inline keep-together.within-line="always">am</fo:inline> <fo:inline keep-together.within-line="always">text</fo:inline> <fo:inline keep-together.within-line="always">that</fo:inline> <fo:inline keep-together.within-line="always">isn't</fo:inline> <fo:inline keep-together.within-line="always">hyphenated</fo:inline>.
</fo:block>

In theory, set hyphenation on the overall block and any child fo:inline that must be kept together in the line (word by word) specified. Now, how you get there with some template is likely not straight-forward considering what maybe are word boundaries and what are punctuation and such.

This would require some testing. You might also need to put breaking-space characters or additional inline elements inbetween the words with the space character.

Update 1

I used the above and created a simple FO. Formatting with RenderX I get what I would expect, even changing spome margins I can get hyphenation in the areas where it is not marked and none in the area where keeps are set.

RenderX output:

enter image description here

Using the exact same template (even adjusting margins several times because each formatter is different when it comes to line ending treatment), I cannot get any hyphenation at all in FOP.

FOP output:

enter image description here

Conclusion is that there seems to be an issue in using FOP and attempting to do what I said as a workaround.

Update 2

With RenderX as well as AHF as stated below, setting hyphenation on fo:inline works. Here is output from RenderX using this:

 <fo:block hyphenate="true">
    <fo:inline>I am some text that can be hyphenated. I am some text that can be hyphenated. I am some text that can be hyphenated. </fo:inline> 
    <fo:inline hyphenate="false">I am some text that cannot be hyphenated. I am some text that cannot be hyphenated. I am some text that cannot be hyphenated. </fo:inline>         
 </fo:block>

enter image description here

Kevin Brown
  • 8,805
  • 2
  • 20
  • 38
  • The problem is that as soon as fop-hyphenation decides to hypenate a word it doesn't matter if there is `keep-together.within-line="always"`. I think the only solution is to set a `fo:block` twhere u can set `hyphenate="false"` that behaves like a `fo:inline` – CrazyEight Jan 22 '20 at 08:00
  • Thanks for your trouble... is there any possibility to use the ability from RenderX or AHF of setting hyphenation property on a `fo:inline` with Apache fop2.3 ? – CrazyEight Jan 23 '20 at 08:46
  • Sure there is an option, but not "use the ability" as FOP is FOP and the other products are closed-source commercial. Apache FOP is open source. You could download the code or hire folks to do it or submit a request to the FOP team do it. – Kevin Brown Jan 23 '20 at 18:34
1

One trick is to set a "bogus" language for the inner <inline>:

Here, I've set the inner block to Ukrainian, so the formatter attempts to use Ukrainian hyphenation rules, which obviously are not applicable.

In fact, I've found this trick because I needed Ukrainian hyphenation for one of my documents and it did not apply unless you explicitly specify the language.

<fo:block hyphenate="true">
This text should be hyphenated This text should be hyphenated This text should be
hyphenated <fo:inline  color="green" language="uk">This text shouldn’t be
hyphenated This text shouldn’t be hyphenated This text shouldn’t be
hyphenated </fo:inline>
</fo:block>

Result using RenderX XEP:

enter image description here
left: as in OP's question; right: with "bogus" language set

Be Brave Be Like Ukraine
  • 7,596
  • 3
  • 42
  • 66
  • Tried this but still hyphenating... but i am also using `wrap-option="wrap"` and `text-align="justify"` on the outer `fo:block` but still hyphenating with inner ``... – CrazyEight Feb 04 '20 at 22:45
0

It should be possible to set hyphenate="false" on the fo:inline. hyphenate is an inherited property, and it applies to both fo:block and fo:character. (See https://www.w3.org/TR/xsl11/#fo_character and https://www.w3.org/TR/xsl11/#hyphenate.) Every character in the text of your XSL-FO document is interpreted as an fo:character, so it should just work.


For this XSL-FO:

<!-- Without fo:inline/@hyphenate: -->
<fo:block hyphenate="true" color="red">
This text should be hyphenated This text shouldn’t be hyphenated</fo:block>
<!-- With fo:inline/@hyphenate: -->
<fo:block hyphenate="true">
This text should be hyphenated <fo:inline hyphenate="false">This text shouldn’t be hyphenated</fo:inline>
</fo:block>

I get this result with AH Formatter V6.6:

enter image description here

Tony Graham
  • 7,306
  • 13
  • 20
  • I will try this for a different word in my text. Maybe it’s just buggy with that one word. I tried both: `hyphenate="false"` on the `fo:inline`, but also `keep-together.within-line`... none of them worked – CrazyEight Jan 21 '20 at 17:56
  • I do not think hyphenate is a valid option on fo:inline, it applies to block level elements only. – Kevin Brown Jan 21 '20 at 23:53
  • It does not apply to `fo:inline`, but it should be inherited by the nominal `fo:character` for each character in the text content of the `fo:inline`. – Tony Graham Jan 22 '20 at 09:44
  • ok thanks... with apache fop 2.3 and version 2.4 it doesnt work – CrazyEight Jan 22 '20 at 15:42
  • @TonyGraham, you may want to edit your sample for future records, someone looking at the result may be confused. Your first line which *can* be hyphenated has the sentence "This text shouldn't be ...". I knew what you meant but it could be confusing. – Kevin Brown Jan 23 '20 at 18:37
  • Thanks. I added comments in the XSL-FO instead. My first test for this had different text in each `fo:block`, but that made it hard to be sure that the difference wasn't the result of the ordinary line-breaking. This way, the only difference is the `hyphenate` property value. – Tony Graham Jan 25 '20 at 11:15