[ 
https://issues.apache.org/jira/browse/FOP-3345?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18124657#comment-18124657
 ] 

Jason Harrop commented on FOP-3345:
-----------------------------------

An FO, with Carlito as the distributions package it (OFL): attached 
FOP-3345-ligature-tounicode.fo and distro-fonts.xconf (adjust the font path).
{noformat}
fop -c distro-fonts.xconf -fo FOP-3345-ligature-tounicode.fo -pdf out.pdf
pdftotext out.pdf -
{noformat}
One block, {{soft title office}} in Carlito, 12pt. Carlito's liga feature makes 
ft, ti and ffi ligatures; its cmap maps the ffi glyph (U+FB03) and not the 
other two.
On main at 5be8c69b6 pdftotext gives {{so tle office}}: the ft and ti glyphs 
are published as U+E000 and U+E001, and the ToUnicode CMap has {{<0005> <0006> 
<e000>}} as a range. The ffi comes out as U+FB03 only because the font happens 
to map it.
With the pull request's change (branch FOP-3345, on top of 114 and 109) 
pdftotext gives {{soft title office}}, and the CMap has {{<0005> <00660074>}}, 
{{<0006> <00740069>}} and {{<000a> <006600660069>}}: each ligature publishes 
the characters it stands for.

> ToUnicode publishes a private-use code point for any glyph substitution 
> produced, so ligatures and Arabic contextual forms are not searchable, 
> copyable or readable aloud
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: FOP-3345
>                 URL: https://issues.apache.org/jira/browse/FOP-3345
>             Project: FOP
>          Issue Type: Bug
>          Components: font/opentype, renderer/pdf
>    Affects Versions: 2.11
>            Reporter: Jason Harrop
>            Priority: Minor
>         Attachments: FOP-3345-ligature-tounicode.fo, distro-fonts.xconf
>
>
> MultiByteFont.mapGlyphsToChars maps the substituted glyph sequence back to 
> characters one character per glyph. For a glyph substitution left alone it 
> finds the originating character. For a glyph substitution produced, whose 
> glyph index no character maps to, it falls through to 
> createPrivateUseMapping, which mints a code point from U+E000 upward. The 
> painter later hands that code point to CIDSubset.mapCodePoint, and 
> CIDSubset.getChars is what PDFToUnicodeCMap is built from. So the private-use 
> code point minted to give the glyph an identity becomes its published meaning.
> That is every ligature (fi, fl, ffi, ft, ti and so on wherever the font has 
> no cmap entry for the ligature glyph), and every single substitution whose 
> output glyph has no cmap entry of its own: Arabic initial, medial and final 
> forms in a font without presentation-form cmap entries, small capitals, 
> old-style figures, contextual alternates, and each glyph of a ccmp 
> decomposition.
> Measured with the 2.11 command line, read back with pdftotext:
> {noformat}
> Carlito, script="latn", "office affluent fifty flow ti fi"
>     -> "office affluent fiy flow  fi"
>        (ft and ti minted; fi, fl, ffi, ffl publish U+FB01..FB04 because 
> Carlito's
>        cmap happens to map those forms)
> Noto Sans Arabic, script="arab", "السلام عليكم"
>     -> the medial yeh extracts as U+E001 U+E000 (ccmp split it into 
> uni066E.medi.wide
>        and twodotshorizontalbelowar), the other letters as presentation forms
> {noformat}
> A docx4j corpus render of an Arabic probe through 2.11 counted 105 
> private-use characters against 308 base letters and 441 presentation forms.
> The page looks right, since the glyph and its advance are correct; the defect 
> shows only when someone selects, searches or reads the text.
> h3. Fix
> The information is already there: the GlyphSequence carries a CharAssociation 
> per glyph naming the characters that produced it, and 
> findUnsubstitutedCharacter reads it and discards it for exactly the 
> substituted case.
> # MultiByteFont.mapGlyphsToChars records, per glyph index, the characters of 
> the association whenever the glyph was substituted 
> (findUnsubstitutedCharacter returned nothing). A disjoint association (a 
> ligature with ignored marks between its components) contributes its 
> sub-intervals only. The character returned for layout does not change: it is 
> still the glyph's identity, which findGlyphIndex maps back to the glyph.
> # CIDSet gains getUnicodeSequences(): one String per selector, the recorded 
> characters where there are any and the code point otherwise. CIDSubset and 
> CIDFull implement it.
> # PDFFactory builds the ToUnicode CMap from that instead of getChars().
> # PDFToUnicodeCMap takes one destination String per selector. Only a 
> destination of one code point may join a bfrange; a multi-character 
> destination is written as a bfchar with a string, which the spec allows.
> Two cases a ToUnicode entry cannot express, and how the fix leaves them:
> * Several glyphs for one character (a ccmp decomposition replicates the 
> association onto each output glyph). The first glyph publishes the character; 
> the second and later keep the private-use code point. An empty destination 
> was tried and rejected: poppler reads it correctly, but mupdf prints U+FFFD, 
> pdf.js a space, and PDFium the raw selector as a control character. 
> Publishing the character twice makes poppler and mupdf insert a space. 
> ActualText per cluster is the mechanism for this case and is a content-stream 
> change outside this issue.
> * One glyph seen with two different meanings (a shared dotless base in a 
> decomposing Arabic font). It publishes neither, keeping the private-use code 
> point, rather than the wrong letter.
> After the fix, with the drawn content byte for byte unchanged:
> {noformat}
> Carlito         -> "office affluent fifty flow ti fi"   (pdftotext, mupdf, 
> pdf.js, PDFium)
> Noto Sans Arabic -> "السلام عليكم"                 (the dots glyph is the 
> one left)
> {noformat}
> Tests: PDFToUnicodeCMapTestCase and MultiByteFontTestCase extended; the 
> writer's existing output is pinned by ToUnicodeCharacterisationTestCase 
> before the change.
> Related: the same rewrite of PDFToUnicodeCMap fixes the selector drift after 
> a surrogate pair, reported separately (FOP-3346), because the positional 
> char\[] that caused it is what had to go.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to