[ 
https://issues.apache.org/jira/browse/FOP-3339?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Joao Goncalves resolved FOP-3339.
---------------------------------
    Resolution: Duplicate

> Subsetting a font whose last glyph is empty throws EOFException, and the font 
> is then not embedded in the PDF
> -------------------------------------------------------------------------------------------------------------
>
>                 Key: FOP-3339
>                 URL: https://issues.apache.org/jira/browse/FOP-3339
>             Project: FOP
>          Issue Type: Bug
>          Components: font/opentype
>    Affects Versions: 2.11
>            Reporter: Jason Harrop
>            Priority: Major
>
> {{GlyfTable.isComposite(int)}} reads a 2-byte {{numberOfContours}} at 
> {{tableOffset + mtxTab\[i].getOffset()}} without checking the glyph's length. 
> A glyph whose {{loca}} entry gives it a length of zero has no glyph 
> description at all, so those two bytes belong to the next glyph — and when 
> the empty glyph is the *last* in the font and {{glyf}} is the *last* table in 
> the file, they are past the end of the file:
> {noformat}
> ERROR org.apache.fop.pdf.PDFFactory - Failed to embed font [...{Carlito 
> Regular}
>   
> {metrics-url=null,embed-url=.../Carlito-Regular.ttf,kerning=true,advanced=true}]
>  Carlito-Regular
> java.io.EOFException: Reached EOF, file size=593908
>     at 
> org.apache.fop.fonts.truetype.FontFileReader.read(FontFileReader.java:100)
>     at 
> org.apache.fop.fonts.truetype.FontFileReader.readTTFUByte(FontFileReader.java:124)
>     at 
> org.apache.fop.fonts.truetype.FontFileReader.readTTFShort(FontFileReader.java:140)
>     at 
> org.apache.fop.fonts.truetype.FontFileReader.readTTFShort(FontFileReader.java:184)
>     at org.apache.fop.fonts.truetype.GlyfTable.isComposite(GlyfTable.java:206)
>     at 
> org.apache.fop.fonts.truetype.GlyfTable.scanGlyphsRecursively(GlyfTable.java:156)
>     at 
> org.apache.fop.fonts.truetype.GlyfTable.populateGlyphsWithComposites(GlyfTable.java:126)
>     at 
> org.apache.fop.fonts.truetype.TTFSubSetFile.scanGlyphs(TTFSubSetFile.java:572)
>     at 
> org.apache.fop.fonts.truetype.TTFSubSetFile.readFont(TTFSubSetFile.java:481)
>     at org.apache.fop.pdf.PDFFactory.getFontSubsetBytes(PDFFactory.java:1482)
>     at org.apache.fop.pdf.PDFFactory.makeFontFile(PDFFactory.java:1406)
>     at org.apache.fop.pdf.PDFFactory.makeFontDescriptor(PDFFactory.java:1330)
>     at org.apache.fop.pdf.PDFFactory.makeFont(PDFFactory.java:970)
>     at org.apache.fop.pdf.PDFResources.addFonts(PDFResources.java:137)
>     at 
> org.apache.fop.render.pdf.PDFDocumentHandler.endDocument(PDFDocumentHandler.java:208)
> {noformat}
> {{PDFFactory.makeFontFile}} catches the exception, logs it and returns null, 
> so the font descriptor is written with no {{/FontFile2}}. The PDF then *names 
> a font it does not embed*: it fails PDF/A, and a reader or printer without 
> that font substitutes another, so the document does not look as it was 
> written. Nothing else fails, and the text is still drawn, so the defect is 
> easy to miss.
> Fonts of this shape are not exotic. The last glyph of *Arimo* and of 
> *Carlito* — the metric-compatible Arial and Calibri substitutes in Google's 
> croscore and crosextra families, which most Linux distributions package — is 
> U+00A0 NO-BREAK SPACE, it is empty, and {{glyf}} is the last table in the 
> file. Whether the read runs off the end then depends only on how many pad 
> bytes follow, and for five of the eight faces there are fewer than two:
> ||file||bytes after the last glyph's offset||{{TTFSubSetFile}} with that 
> glyph||
> |Arimo-Regular.ttf|2|ok|
> |Arimo-Bold.ttf|0|*EOFException*|
> |Arimo-Italic.ttf|1|*EOFException*|
> |Arimo-BoldItalic.ttf|1|*EOFException*|
> |Carlito-Regular.ttf|0|*EOFException*|
> |Carlito-Bold.ttf|2|ok|
> |Carlito-Italic.ttf|0|*EOFException*|
> |Carlito-BoldItalic.ttf|3|ok|
> So any document with a non-breaking space in an Arial or Calibri run loses 
> that font from the PDF. Measured over a 449-document real-document corpus, 4 
> documents were affected; in one of them the font that went missing was the 
> body font of the whole document.
> *Reproducer* — subset any font whose last glyph is empty and whose {{glyf}} 
> table ends the file, with that glyph in the subset:
> {code:java}
> FontFileReader in = new FontFileReader(new 
> FileInputStream("Carlito-Regular.ttf"));
> Map<Integer, Integer> glyphs = new HashMap<>();
> glyphs.put(0, 0);
> glyphs.put(2531, 1);          // the last glyph, U+00A0, empty
> new TTFSubSetFile().readFont(in, "probe", null, glyphs);   // EOFException
> {code}
> (Carlito 1.104: 593,908 bytes, numGlyphs 2532, {{glyf}} at 121,352 of length 
> 472,556 — so the last glyph begins at exactly the end of the file. With glyph 
> 2530 instead of 2531 the same call returns a 4,908-byte subset.)
> *Fix* — an empty glyph is never composite, so return false without reading. 
> The glyph's length is the next {{mtxTab}} entry's offset, or the {{glyf}} 
> table's own length for the last glyph; {{GlyfTable}}'s constructor is already 
> handed the {{OFDirTabEntry}} that carries it, so no signature changes and the 
> PCL callers are unaffected. {{TTFFile.readGlyf}} already guards its own read 
> of these bytes the same way ({{if (lastLoca != 0 && lastLoca == 
> mtxTab\[i].getOffset()) break;}}).
> The same guard also fixes an empty glyph in the *middle* of the table, which 
> was read as the next glyph's {{numberOfContours}} — so an empty glyph 
> immediately before a composite one was reported composite and then walked as 
> one.
> Patch and two unit tests in the PR.



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

Reply via email to