program: read a symbolic TrueType font through its own character map - #7
Merged
Conversation
A simple TrueType font flagged symbolic, carrying an embedded FontFile2, with neither /Encoding nor /ToUnicode, yielded no text at all. /Users/Shared/pdfforms/us-dol/1999-schedule-i.pdf is one: pdftotext reads 1105 words off page 1 and this package read 10, although the page's codes are plain ASCII and the content stream literally says (Department of Labor) Tj. The guard is right and stays. pdffont does fall back to StandardEncoding when a font has no /Encoding, so names are assigned; Font.Text then refuses them for a symbolic font, because a mathematical symbolic font puts a capital gamma where the standard encoding puts an inverted exclamation mark and reading the name would give the wrong letter with nothing to say so. The answer has to come from the program, which is what the fallback installed here is for. It only knew one way to ask: the program's own code-to-name encoding, which only a PostScript program carries. Font.GlyphIndexByCode is documented to report nothing for every TrueType font, so the last resort returned nothing at the first step for all six embedded fonts of that file. So ask the other way, through the font's character map. The way in is the one poppler uses for a font the document gave no encoding (GfxFont.cc, Gfx8BitFont::getCodeToGIDMap, case 2): the Microsoft Symbol subtable if there is one, else the Macintosh Roman one, addressed by the raw code and then by 0xF000 + code, which is where such subtables are conventionally written. The way out is the Unicode subtable, inverted -- the font's own statement of which character that glyph is for. Without a Unicode subtable there is no way out and it says nothing: a glyph on its own says nothing about which character it is, and guessing would produce exactly the wrong letter the guard exists to refuse. Nor is a private-use or control character reported, which reads no better than silence and would wrongly look like a page that had been read. Order is unchanged: /ToUnicode, then a name the document chose in /Differences, then the program. The program stays the last resort. Measured. The four US Department of Labor forms, page 1, against pdftotext on the same page: schedule I 10 -> 1103 words (poppler 1105), 5500 schedule A 10 -> 244 (248), schedule D 10 -> 312 (314), schedule G 10 -> 227 (229). Across the 1633 real forms of the corpus, first three pages, four files gain text and none loses any; across 118843 arXiv documents not one byte of text changes, because 98% of the fonts in that shape there are PostScript programs the name route already answered for.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A simple TrueType font flagged symbolic, carrying an embedded
FontFile2, with neither/Encodingnor/ToUnicode, yielded no text at all./Users/Shared/pdfforms/us-dol/1999-schedule-i.pdfis one.pdftotextreads 1 105 words off page 1; this package read 10 — although the page's codes are plain ASCII and the content stream literally says(Department of Labor) Tj.Before (page 1, entire):
After (first lines):
Why it happened
The
namedByTheDocumentguard is right and stays.pdffontdoes fall back toStandardEncodingwhen a font has no/Encoding, so names are assigned;Font.Textthen refuses them for a symbolic font, because a mathematical symbolic font puts a capital gamma where the standard encoding puts an inverted exclamation mark, and reading the name would give the wrong letter with nothing to say so.So the answer has to come from the program — which is what the fallback installed here is for. It only knew one way to ask: the program's own code-to-name encoding. Only a PostScript program carries one;
Font.GlyphIndexByCodeis documented to report nothing for every TrueType font, so the last resort returned nothing at the first step for all six embedded fonts of that file. (Theposttable is beside the point: this package reads glyph names from CFF and Type 1 programs only.)What this does
Asks the other way as well, through the font's character map, using the new subtable API in
go-opentype/opentypev0.12.0.The way in is the one poppler uses for a font the document gave no encoding —
GfxFont.cc,Gfx8BitFont::getCodeToGIDMap, case 2: the Microsoft Symbol (3,0) subtable if there is one, else the Macintosh Roman (1,0) one, addressed by the raw code and then, failing that, by0xF000 + code, which is where such subtables are conventionally written.The way out is the Unicode subtable, inverted — the font's own statement of which character that glyph is for.
Order is unchanged:
/ToUnicode, then a name the document chose in/Differences, then the program. The program stays the last resort.What it refuses to say
NelcoSymbolsof that same file carries only (1,0) and (3,0) — stays silent, which is right.Note on the references
Neither poppler nor pdf.js consults the cmap for text at all: for a TrueType font with no
/Encodingpoppler defaults toWinAnsiEncodingnames and pdf.js toMacRomanEncodingwhen symbolic, then maps those names through the glyph list. That is the guess this package deliberately refuses. Going through the cmap is stricter — it reports the character of the glyph that actually gets drawn — and it is why the four forms land two words short of poppler rather than level with it.Measured
Four US Department of Labor forms, page 1, against
pdftotexton the same page:1999-schedule-i.pdf1999-5500-schedule-a.pdf1999-schedule-d.pdf1999-schedule-g.pdfCorpus, first three pages of every file, text digests compared before and after:
/Encodingnor/ToUnicode— but 98% are PostScript programs the name route already answered for, and the rest carry no usable subtable pair. Nothing regressed.Checks
go vet,gofmt,-raceclean; 100% statement coverage; the nine cross-compile targets build.