package pdf import "core:mem" import "core:slice" import "core:unicode/utf8" // Fonts (ISO 32000-2, 9.5 - 9.10). // // A Font ties a PDF font dictionary to a glyph source. The three font-program // readers (font_cff.odin, font_truetype.odin, font_type1.odin) know nothing // about PDF: they turn bytes into outlines and are separately testable. This // file is where the format's encoding, width and CID machinery lives. // // A font that fails to load still produces a Font with widths, so that text // lays out and extracts correctly even when no glyph can be drawn. Missing // glyphs draw nothing; they never abort a page. // // THE THREE SHAPES A PDF FONT COMES IN // // simple one byte per code, 256 codes, an encoding of glyph NAMES, and // /Widths indexed from /FirstChar. Type1, MMType1 and TrueType. // composite a CMap turns a variable-width code into a CID, the descendant // font turns the CID into a glyph, and /W gives the widths. // Type3 glyphs are content streams, so this file only records where // they are; the interpreter draws them. Its /FontMatrix is the // font's own and is not 1/1000. // // Everything a Font points at is allocated from the Document's arena and dies // with it. Fonts are cached on the Document keyed by the address of their // dictionary, because a page that sets the same font two hundred times must // build it once. // // WHAT IS PRECOMPUTED AND WHY // // A simple font's code -> glyph and code -> width tables are built once, at // load: 256 entries each, resolved through the whole encoding cascade. Text // showing then costs one array index per character rather than a walk through // the encoding, the charset and two cmap subtables. A composite font cannot do // the same -- its CID space is 65536 wide -- so its lookups stay on the hot // path and are binary searches. // Codes a simple font has, and the width of the two precomputed tables. @(private) FONT_CODES :: 256 // Entries kept from a /W or /W2 array. The arrays are attacker-controlled and // the `c [w ...]` form costs one entry per width, so both are capped well // above any real CJK font and far below anything that hurts. @(private) MAX_CID_WIDTHS :: 1 << 16 // Items read from one /W or /W2 array before the walk gives up. Two per // entry in the range form, plus the array elements themselves. @(private) MAX_W_ITEMS :: 1 << 17 // u16 entries kept from a /CIDToGIDMap stream. CIDs are 16-bit, so this is // the format's own ceiling rather than an invented one. @(private) MAX_CID_TO_GID :: 65536 // Entries read from a /Differences array. Each is one code, so more than a // few hundred is either damage or an attack. @(private) MAX_DIFFERENCES :: 4096 // /MissingWidth and every width from a file are clamped to this in absolute // value. A width of 1e18 turns a text matrix into an infinity three // multiplications later, and the geometry below the device line may not see // one. 1000 is an em; ten thousand ems is already absurd. @(private) MAX_GLYPH_WIDTH :: 10_000_000.0 Font_Kind :: enum u8 { Type1, TrueType, Type3, Type0, // composite; the descendant carries the program } // Cid_Width is one run of the /W array: CIDs `lo` through `hi` are `width` // wide, in glyph space. Cid_Width :: struct { lo, hi: u32, width: f64, } // Cid_Vmetric is one run of the /W2 array: CIDs `lo` through `hi` advance by // `w1y` vertically and have their origin displaced by (`v1x`, `v1y`) from the // horizontal origin. ISO 32000-2, 9.7.4.3. Cid_Vmetric :: struct { lo, hi: u32, w1y: f64, v1x, v1y: f64, } // Font_Code is one decoded character code, ready to draw and to extract. Font_Code :: struct { code: u32, // the raw code taken from the string cid: u32, // equal to `code` for a simple font gid: u32, // glyph index in the program; 0 is .notdef width: f64, // glyph space, so 1/1000 text space for everything but Type3 unicode: rune, // -1 when the font offers none nbytes: int, // bytes of the string this code consumed is_space: bool, // a single-byte code 32: word spacing applies // Position vector, glyph space: where the glyph's horizontal origin sits // relative to its vertical one. Zero unless the font is vertical, in which // case `width` is the VERTICAL displacement w1y (normally negative) and // this is what moves the glyph off the vertical baseline. 9.7.4.3. vx, vy: f64, } Font :: struct { doc: ^Document, dict: ^Dict, kind: Font_Kind, base_font: string, // -- simple fonts (9.6) ---------------------------------------------- first_char: int, last_char: int, // /Widths, indexed from first_char, in glyph space. widths: []f64, missing_width: f64, // The resolved glyph name for each code, or "" where the encoding gives // none. This is the whole of a simple font's encoding after /BaseEncoding // and /Differences have been applied over the program's built-in one. enc_names: [256]string, // Code to glyph and code to width, resolved once at load. See the header. code_gid: [FONT_CODES]u32, code_width: [FONT_CODES]f64, // -- composite fonts (9.7) ------------------------------------------- // Code to CID. cmap: ^Cmap, // CID to GID: /CIDToGIDMap, empty when it is /Identity. cid_to_gid: []u16, cid_widths: []Cid_Width, default_width: f64, vertical: bool, // /DW2 and /W2, for vertical writing. default_vy: f64, default_w1: f64, cid_vmetrics: []Cid_Vmetric, // The descendant font dictionary, kept for callers that need /CIDSystemInfo. descendant: ^Dict, // -- the glyph program ------------------------------------------------ cff: ^Cff_Font, ttf: ^TrueType_Font, t1: ^Type1_Font, // Glyph space to text space. 1/1000 for Type1 and CFF, 1/unitsPerEm for // TrueType, and whatever /FontMatrix says for Type3. mtx: Matrix, embedded: bool, // -- Type3 (9.6.4) ---------------------------------------------------- // Type3 glyphs are content streams, so drawing one re-enters the // interpreter rather than the rasteriser. char_procs: ^Dict, t3_resources: ^Dict, // -- text extraction -------------------------------------------------- to_unicode: ^Cmap, // -- descriptor flags (9.8.2) ----------------------------------------- symbolic: bool, serif: bool, fixed_pitch: bool, italic: bool, // Set when the program could not be read at all; widths still work. program_failed: bool, // The standard-14 metrics this /BaseFont resolves to, or -1 for a font // that is not one of them. A code with no /Widths entry is measured with // these before it falls back to /MissingWidth. std_index: int, } // --------------------------------------------------------------------------- // Loading // --------------------------------------------------------------------------- // font_load builds a Font from a font dictionary. The result belongs to the // Document and is cached on it, so loading the same dictionary twice is one // map lookup. // // An error comes back only when there is no dictionary at all. Everything // else degrades: a font program that will not parse leaves `program_failed` // set and the widths and encodings intact, because a page whose glyphs cannot // be drawn must still lay its text out and extract it. font_load :: proc(doc: ^Document, d: ^Dict) -> (^Font, Error) { if doc == nil || d == nil { return nil, .Invalid_Argument } key := uintptr(rawptr(d)) if cached, ok := doc.font_cache[key]; ok { return cached, .None } f := new(Font, doc.allocator) f.doc = doc f.dict = d f.mtx = Matrix{0.001, 0, 0, 0.001, 0, 0} f.std_index = -1 f.default_width = 1000 // ISO 32000-2, 9.7.4.3: /DW2 defaults to [880 -1000]. f.default_vy = 880 f.default_w1 = -1000 // Cached before the body runs. Nothing here recurses into font_load // today, but a /Resources cycle reaching back to this font would be the // file's choice, not ours, and this makes it a lookup rather than a loop. if doc.font_cache == nil { doc.font_cache = make(map[uintptr]^Font, 16, doc.allocator) } doc.font_cache[key] = f f.base_font = dict_name(doc, d, "BaseFont") f.kind = font_kind(doc, d) if idx, ok := std_font_lookup(f.base_font); ok { f.std_index = idx } if tu := dict_get(doc, d, "ToUnicode"); tu != nil { if _, is_stream := tu.(^Stream); is_stream { f.to_unicode, _ = cmap_load(doc, tu) } } switch f.kind { case .Type0: font_load_type0(doc, f, d) case .Type3: font_load_type3(doc, f, d) case .Type1, .TrueType: font_load_simple(doc, f, d) } return f, .None } // font_matrix maps glyph space to text space. // // Every font but Type3 measures glyphs in thousandths of a text space unit, // whatever the program's own units are -- the readers hand back outlines in // their own units and this is the matrix that reconciles them. A Type3 font // carries its own /FontMatrix and may use any units at all. font_matrix :: proc(f: ^Font) -> Matrix { if f == nil { return Matrix{0.001, 0, 0, 0.001, 0, 0} } return f.mtx } // font_is_type3 reports whether drawing a glyph means running a content // stream rather than filling an outline. font_is_type3 :: proc(f: ^Font) -> bool { return f != nil && f.kind == .Type3 } // font_glyph_path appends one glyph's outline, in the program's glyph space, // to `out`. `font_matrix` maps that space to text space. // // A font with no usable program, and a Type3 font (whose glyphs are content // streams), report .No_Glyph. So does a glyph index the program does not // have. None of those are page-level failures: the caller draws nothing and // advances the pen. font_glyph_path :: proc(f: ^Font, gid: u32, out: ^Path) -> Error { if f == nil || out == nil { return .Invalid_Argument } if f.kind == .Type3 { return .No_Glyph } if gid > 0xFFFF { // Every one of the three programs indexes glyphs with a u16. return .No_Glyph } g := u16(gid) // A font may hold two programs at once: an OpenType wrapper whose // outlines are CFF is parsed by both readers, and only the CFF can draw. if f.ttf != nil && tt_has_glyf(f.ttf) { return tt_glyph_path(f.ttf, g, out) } if f.cff != nil { return cff_glyph_path(f.cff, g, out) } if f.t1 != nil { return t1_glyph_path(f.t1, g, out) } return .No_Glyph } // font_kind reads /Subtype, and guesses when it is missing or nonsense. // // The guess matters: a font dictionary with no /Subtype is malformed but // perfectly readable, and its other keys say unambiguously what it is. @(private) font_kind :: proc(doc: ^Document, d: ^Dict) -> Font_Kind { switch dict_name(doc, d, "Subtype") { case "Type0": return .Type0 case "Type3": return .Type3 case "TrueType": return .TrueType case "Type1", "MMType1": return .Type1 } if dict_has(d, "DescendantFonts") { return .Type0 } if dict_has(d, "CharProcs") || dict_has(d, "FontMatrix") { return .Type3 } return .Type1 } // --------------------------------------------------------------------------- // Simple fonts (9.6) // --------------------------------------------------------------------------- @(private) font_load_simple :: proc(doc: ^Document, f: ^Font, d: ^Dict) { desc := dict_dict(doc, d, "FontDescriptor") font_read_descriptor(doc, f, desc) font_load_program(doc, f, desc) font_set_matrix(f) font_build_encoding(doc, f, d) font_read_widths(doc, f, d) for code in 0 ..< FONT_CODES { f.code_gid[code] = font_simple_gid(f, u8(code)) f.code_width[code] = font_simple_width(f, u8(code)) } } // font_read_descriptor takes the flags and /MissingWidth off the descriptor. // // A font with no descriptor at all is one of the standard 14 (9.8.1 excuses // them), and the two symbolic ones are symbolic whatever a missing dictionary // does not say. @(private) font_read_descriptor :: proc(doc: ^Document, f: ^Font, desc: ^Dict) { if desc == nil { if f.std_index >= 0 { f.symbolic = std_font_is_symbolic(f.std_index) } return } flags := u32(dict_int(doc, desc, "Flags", 0)) // 9.8.2, table 121. Bit positions are 1-based in the table. f.fixed_pitch = flags & (1 << 0) != 0 f.serif = flags & (1 << 1) != 0 f.italic = flags & (1 << 6) != 0 // Producers set both Symbolic and Nonsymbolic often enough that the pair // has to be read as a whole: a font that claims both is claiming nothing, // and a Latin text font is the safer reading of nothing. f.symbolic = flags & (1 << 2) != 0 && flags & (1 << 5) == 0 f.missing_width = font_clamp_width(dict_num(doc, desc, "MissingWidth", 0)) } // font_read_widths reads /FirstChar, /LastChar and /Widths. // // /LastChar is recorded and is deliberately NOT used to bound the lookup: the // array's own length is. A /LastChar that disagrees with the array is far // commoner than an over-long array, and honouring it would throw away widths // that are sitting right there. @(private) font_read_widths :: proc(doc: ^Document, f: ^Font, d: ^Dict) { f.first_char = int(dict_int(doc, d, "FirstChar", 0)) f.last_char = int(dict_int(doc, d, "LastChar", -1)) a := dict_array(doc, d, "Widths") if a == nil { return } // Only 256 codes exist, so nothing past the 256th entry can ever be // indexed however long the array is. n := min(array_len(a), FONT_CODES) if n <= 0 { return } w := make([]f64, n, doc.allocator) for i in 0 ..< n { w[i] = font_clamp_width(array_num(doc, a, i, 0)) } f.widths = w } // font_simple_width is the width of one code, in glyph space. // // The order is the format's, with one addition. /Widths first; then the // standard-14 metrics, which are what a font dictionary with no /Widths at // all is relying on (9.6.2.2 allows omitting them for the fourteen); then // /MissingWidth, but only when the descriptor actually carries one; and only // then the embedded program's own advance. The last step is not in the // format, and is what saves a subset font that shipped no /Widths and no // descriptor: without it every glyph on the page would advance by zero and // the line would collapse into one spot. @(private) font_simple_width :: proc(f: ^Font, code: u8) -> f64 { i := int(code) - f.first_char if i >= 0 && i < len(f.widths) { return f.widths[i] } name := f.enc_names[code] if f.std_index >= 0 { if w, ok := std_font_width(f.std_index, name); ok { return w } } if f.missing_width != 0 { return f.missing_width } if w, ok := font_program_advance(f, f.code_gid[code]); ok { return w } return f.missing_width } // font_program_advance is the advance the embedded program declares for a // glyph, converted into the 1/1000 units every width outside a Type3 font is // in. ok is false when there is no program, or when it declares nothing. @(private) font_program_advance :: proc(f: ^Font, gid: u32) -> (f64, bool) { if gid > 0xFFFF { return 0, false } g := u16(gid) switch { case f.ttf != nil && tt_has_glyf(f.ttf): upem := tt_units_per_em(f.ttf) if upem <= 0 || !is_finite(upem) { return 0, false } a := tt_glyph_advance(f.ttf, g) * 1000 / upem return font_clamp_width(a), a != 0 case f.cff != nil: // A CFF's advances are in charstring units, which its FontMatrix maps // to text space; scaling by 1000 puts them back in the thousandths // every PDF width is written in. a := cff_glyph_advance(f.cff, g) * cff_font_matrix(f.cff).a * 1000 return font_clamp_width(a), a != 0 case f.t1 != nil: a := t1_glyph_advance(f.t1, g) * t1_font_matrix(f.t1).a * 1000 return font_clamp_width(a), a != 0 } return 0, false } // --------------------------------------------------------------------------- // The encoding of a simple font (9.6.5) // // Four sources, each overriding the one before it: // // 1. the font program's own built-in encoding, // 2. the standard-14 built-in encoding, when there is no program, // 3. /BaseEncoding, or a bare name in /Encoding, // 4. /Differences. // // A non-symbolic font that still has nothing after (2) uses StandardEncoding; // a symbolic one deliberately does not, because inventing Latin names for a // dingbat font would map every code to a glyph it does not have. A TrueType // program contributes nothing to (1) at all: its built-in encoding is a cmap // keyed by character, not by name, and it is consulted in font_tt_gid rather // than here. // --------------------------------------------------------------------------- @(private) font_build_encoding :: proc(doc: ^Document, f: ^Font, d: ^Dict) { base: ^[256]string if f.t1 != nil { base = t1_builtin_encoding(f.t1) } else if f.cff != nil { base = cff_builtin_encoding(f.cff) } if base == nil && f.std_index >= 0 { base = std_font_builtin_encoding(f.std_index) } enc := dict_get(doc, d, "Encoding") named: ^[256]string diffs: ^Array #partial switch v in enc { case Name: named = encoding_by_name(string(v)) case ^Dict: if bn := dict_name(doc, v, "BaseEncoding"); bn != "" { named = encoding_by_name(bn) } diffs = dict_array(doc, v, "Differences") } if named != nil { base = named } if base == nil && !f.symbolic { base = encoding_standard() } if base != nil { f.enc_names = base^ } if diffs == nil { return } // 9.6.5.1: a /Differences array is numbers and names interleaved. A // number sets the code the next name applies to; each name advances it. code := 0 n := min(array_len(diffs), MAX_DIFFERENCES) for i in 0 ..< n { #partial switch v in array_get(doc, diffs, i) { case i64: code = int(v) case f64: // A real where an integer belongs is malformed, and is how at // least one generator writes "1.0". if is_finite(v) && v >= -1e6 && v <= 1e6 { code = int(v) } case Name: if code >= 0 && code < FONT_CODES { f.enc_names[code] = string(v) } code += 1 } } } // font_simple_gid resolves one character code to a glyph index. @(private) font_simple_gid :: proc(f: ^Font, code: u8) -> u32 { if f.kind == .Type3 { // A Type3 glyph is found by name in /CharProcs; there is no program // to index. The code stands in for a glyph id so that the rest of the // pipeline has something to carry. return u32(code) } name := f.enc_names[code] // A TrueType-shaped program is character-keyed and has its own rules. if f.ttf != nil { if g, ok := font_tt_gid(f, code, name); ok { return g } } if name != "" { if f.cff != nil { if g, ok := cff_gid_for_name(f.cff, name); ok { return u32(g) } } if f.t1 != nil { if g, ok := t1_gid_for_name(f.t1, name); ok { return u32(g) } } // The same character often has two names -- "nbspace" and "space", // "uni0041" and "A" -- and an encoding and a charset that disagree // about which to use would otherwise lose the glyph entirely. if r := glyph_name_to_rune(name); r >= 0 { if alt := rune_to_glyph_name(r); alt != "" && alt != name { if f.cff != nil { if g, ok := cff_gid_for_name(f.cff, alt); ok { return u32(g) } } if f.t1 != nil { if g, ok := t1_gid_for_name(f.t1, alt); ok { return u32(g) } } } } } // A CFF's own Encoding maps codes to glyphs directly, which reaches // glyphs its charset does not name -- the shape a symbolic Type1C subset // usually has. if f.cff != nil { if g, ok := cff_gid_for_code(f.cff, code); ok { return u32(g) } // A CID-keyed CFF embedded as a SIMPLE font's program has neither // names nor an encoding (CFF 12), so both routes above are dead ends // and every glyph would be lost. Producers do emit this -- a // CIDFontType0C reused under a /Type1 dictionary -- and reading the // code as a CID through the charset is what recovers it. if f.cff.is_cid { if g, ok := cff_gid_for_cid(f.cff, u16(code)); ok { return u32(g) } } } return 0 } // font_tt_gid is the TrueType half of a simple font's code -> glyph mapping. // It is the most error-prone rule in PDF text, so it is written out in full. // // ISO 32000-2, 9.6.5.4: // // A SYMBOLIC font's /Encoding is ignored. The code is looked up in the // font's (3,0) Microsoft Symbol cmap, whose keys are conventionally 0xF000 // plus the byte -- but only conventionally, so the bare code is tried too // (tt_gid_for_symbol does both). Failing that, the (1,0) Macintosh Roman // cmap is tried with the raw code. This is the rule that makes Wingdings, // Symbol and every icon-font subset come out right, and applying the // /Encoding to one of them instead maps every code to a glyph the font does // not have: the page comes out blank or, worse, full of the wrong glyphs. // // A NON-SYMBOLIC font goes the long way round: the code selects a glyph // NAME through the encoding, and the name is then resolved by three routes // in turn. Through the AGL to a Unicode value and that value through the // (3,1) cmap; through the `post` table, which names this font's own glyphs // and so answers a subsetter's "g42" that the AGL never will; and through // MacRomanEncoding to a Mac Roman CODE and that code through the (1,0) // cmap, which is the only route a font carrying nothing but a (1,0) table // has. All three are indirections, and all three are needed: the cmaps are // keyed by character or by Mac code, and the /Differences are written in // names. // // Two departures from the letter of that, both of which every viewer makes: // // * a code that the symbolic route does not resolve still tries the name // route, and the other way about. A font's flags are frequently wrong, // and a fallback that only fires when the primary route found nothing // cannot make a correct font worse. // * a font with NO usable cmap at all -- which a CIDFontType2 subset // re-used as a simple font, and a stripped TrueType, both are -- treats // the code as a glyph index. It is the only remaining interpretation, and // it is what the producers of those files expect. @(private) font_tt_gid :: proc(f: ^Font, code: u8, name: string) -> (u32, bool) { tt := f.ttf if f.symbolic { if g, ok := tt_gid_for_symbol(tt, code); ok { return u32(g), true } if g, ok := tt_gid_for_mac_code(tt, code); ok { return u32(g), true } } if name != "" { if r := glyph_name_to_rune(name); r >= 0 { if g, ok := tt_gid_for_unicode(tt, r); ok { return u32(g), true } } if g, ok := tt_gid_for_name(tt, name); ok { return u32(g), true } // The middle route of 9.6.5.4: the name's position in // MacRomanEncoding is the key the (1,0) cmap is written in. It is the // only route a Mac-authored or subsetted font -- one cmap, (1,0), and // a format 3 `post` with no names in it -- has above ASCII, and // without it such a code falls through to the raw-code fallback // below, which over 0x80-0xFF returns a real but different glyph: the // page renders and is silently wrong. // // The clause lists this before `post`; it is tried after because // `post` names a glyph in THIS font directly while this is an // indirection through a legacy encoding, so when the two disagree the // font's own names are the better answer. The order only matters when // both can answer, and this route exists for the case where `post` // cannot. if mac_code, ok := encoding_macroman_code(name); ok { if g, gok := tt_gid_for_mac_code(tt, mac_code); gok { return u32(g), true } } } if !f.symbolic { if g, ok := tt_gid_for_symbol(tt, code); ok { return u32(g), true } if g, ok := tt_gid_for_mac_code(tt, code); ok { return u32(g), true } } // The code read as a character. Over ASCII every Latin text encoding // agrees with Unicode, so this is what a font whose /Encoding was omitted // means -- and it is also the only route left for the very common subset // that sets the Symbolic flag but ships a (3,1) cmap and nothing else. if g, ok := tt_gid_for_unicode(tt, rune(code)); ok { return u32(g), true } if len(tt.cmap_unicode) == 0 && len(tt.cmap_symbol) == 0 && len(tt.cmap_mac) == 0 { if int(code) < tt_glyph_count(tt) { return u32(code), true } } return 0, false } // --------------------------------------------------------------------------- // Type3 (9.6.4) // --------------------------------------------------------------------------- // font_load_type3 records where a Type3 font's glyphs are. It does not run // them: a Type3 glyph is a content stream, and drawing one re-enters the // interpreter with this font's /Resources in scope. @(private) font_load_type3 :: proc(doc: ^Document, f: ^Font, d: ^Dict) { f.char_procs = dict_dict(doc, d, "CharProcs") f.t3_resources = dict_dict(doc, d, "Resources") // 9.6.4: /FontMatrix is required and is emphatically not 1/1000 -- it is // how a Type3 font declares its own glyph space, and a font drawn in // 1-unit coordinates has [0.001 0 0 0.001 0 0] nowhere in it. A matrix // that is missing, unreadable or singular would make every glyph // infinite or invisible, so it degrades to the usual thousandths. f.mtx = Matrix{0.001, 0, 0, 0.001, 0, 0} if a := dict_array(doc, d, "FontMatrix"); array_len(a) >= 6 { m := Matrix{ a = array_num(doc, a, 0, 0), b = array_num(doc, a, 1, 0), c = array_num(doc, a, 2, 0), d = array_num(doc, a, 3, 0), e = array_num(doc, a, 4, 0), f = array_num(doc, a, 5, 0), } if matrix_is_finite(m) && matrix_determinant(m) != 0 { f.mtx = m } else { add_repair(doc, .Bad_Font_Object, 0) } } font_read_descriptor(doc, f, dict_dict(doc, d, "FontDescriptor")) font_build_encoding(doc, f, d) font_read_widths(doc, f, d) for code in 0 ..< FONT_CODES { f.code_gid[code] = u32(code) // A Type3 width is in the font's own glyph space, so there is no // standard-14 metric and no program advance to fall back on: a code // with no /Widths entry is genuinely zero wide. i := code - f.first_char f.code_width[code] = i >= 0 && i < len(f.widths) ? f.widths[i] : f.missing_width } } // font_type3_proc is the content stream that draws one code, or nil. // // Not part of the five procedures the contract names; the interpreter needs // it, and it is a lookup that belongs to the encoding rather than to the // content stream. font_type3_proc :: proc(f: ^Font, code: u32) -> ^Stream { if f == nil || f.kind != .Type3 || f.char_procs == nil || code >= FONT_CODES { return nil } name := f.enc_names[code] if name == "" { return nil } return dict_stream(f.doc, f.char_procs, name) } // --------------------------------------------------------------------------- // Composite fonts (9.7) // --------------------------------------------------------------------------- @(private) font_load_type0 :: proc(doc: ^Document, f: ^Font, d: ^Dict) { // The encoding CMap turns bytes into CIDs. A Type0 font without one is // undrawable, so Identity-H stands in: two-byte codes taken as CIDs is // what the overwhelming majority of real Type0 fonts use anyway. if enc := dict_get(doc, d, "Encoding"); enc != nil { cm, err := cmap_load(doc, enc) if err == .None && cm != nil { f.cmap = cm } } if f.cmap == nil { add_repair(doc, .Bad_Font_Object, 0) f.cmap = cmap_identity(false, doc.allocator) } f.vertical = f.cmap != nil && f.cmap.wmode == 1 cid := font_descendant(doc, d) if cid == nil { // No descendant: nothing maps a CID to a glyph and nothing gives a // width. The font still decodes text, which is what /ToUnicode needs. add_repair(doc, .Bad_Font_Object, 0) return } f.descendant = cid desc := dict_dict(doc, cid, "FontDescriptor") font_read_descriptor(doc, f, desc) font_load_program(doc, f, desc) font_set_matrix(f) f.default_width = font_clamp_width(dict_num(doc, cid, "DW", 1000)) if a := dict_array(doc, cid, "W"); a != nil { font_parse_w(doc, f, a) } if f.vertical { if a := dict_array(doc, cid, "DW2"); array_len(a) >= 2 { vy := array_num(doc, a, 0, 880) w1 := array_num(doc, a, 1, -1000) if is_finite(vy) && is_finite(w1) { f.default_vy = font_clamp_width(vy) f.default_w1 = font_clamp_width(w1) } } if a := dict_array(doc, cid, "W2"); a != nil { font_parse_w2(doc, f, a) } } font_load_cid_to_gid(doc, f, cid) } // font_descendant is /DescendantFonts[0]. The format requires a one-element // array; a producer that writes the dictionary directly means the same thing // and is common enough to accept. @(private) font_descendant :: proc(doc: ^Document, d: ^Dict) -> ^Dict { o := dict_get(doc, d, "DescendantFonts") #partial switch v in o { case ^Dict: return v case ^Array: return array_dict(doc, v, 0) } return nil } // font_load_cid_to_gid reads /CIDToGIDMap: /Identity, or a stream of // big-endian u16 glyph indices indexed by CID (9.7.4.2). @(private) font_load_cid_to_gid :: proc(doc: ^Document, f: ^Font, cid: ^Dict) { o := dict_get(doc, cid, "CIDToGIDMap") stm, is_stream := o.(^Stream) if !is_stream { return } data, err := stream_data(doc, stm) if err != .None || len(data) < 2 { add_repair(doc, .Bad_Font_Object, 0, stm.ref) return } n := min(len(data) / 2, MAX_CID_TO_GID) m := make([]u16, n, doc.allocator) for i in 0 ..< n { m[i] = u16(data[2 * i]) << 8 | u16(data[2 * i + 1]) } f.cid_to_gid = m } // font_cid_gid maps a CID to a glyph index. @(private) font_cid_gid :: proc(f: ^Font, cid: u32) -> u32 { if len(f.cid_to_gid) > 0 { // A /CIDToGIDMap that stops short is not saying "identity from here // on"; it is saying those CIDs have no glyph. 9.7.4.2. if int(cid) < len(f.cid_to_gid) { return u32(f.cid_to_gid[cid]) } return 0 } if f.cff != nil { // A CID-keyed CFF's charset is the CID -> GID map; a name-keyed one // reached through Identity-H has the CID as its glyph index, which is // what cff_gid_for_cid answers for it. if cid > 0xFFFF { return 0 } if g, ok := cff_gid_for_cid(f.cff, u16(cid)); ok { return u32(g) } return 0 } return cid } // font_parse_w reads the /W array (9.7.4.3), which has two forms that alternate // freely inside one array: // // c [w1 w2 ... wn] CIDs c through c+n-1 // c_first c_last w every CID in the range @(private) font_parse_w :: proc(doc: ^Document, f: ^Font, a: ^Array) { out := make([dynamic]Cid_Width, 0, 16, doc.allocator) i := 0 n := array_len(a) fuel := MAX_W_ITEMS for i < n && fuel > 0 && len(out) < MAX_CID_WIDTHS { fuel -= 1 first, ok := font_cid_operand(doc, a, i) if !ok { // Not a number where one belongs. Stepping over one item rather // than abandoning the array keeps the widths after the damage. i += 1 continue } i += 1 if i >= n { break } #partial switch v in array_get(doc, a, i) { case ^Array: i += 1 m := array_len(v) for k in 0 ..< m { if len(out) >= MAX_CID_WIDTHS { break } fuel -= 1 if fuel <= 0 { break } c := u64(first) + u64(k) if c > 0xFFFF { break } font_push_width(&out, u32(c), u32(c), font_clamp_width(array_num(doc, v, k, 0))) } case: last, ok2 := font_cid_operand(doc, a, i) i += 1 if i >= n || !ok2 { break } w := font_clamp_width(array_num(doc, a, i, 0)) i += 1 lo, hi := first, last if hi < lo { // A backwards range is meaningless as written; read either // way round it names the same glyphs. lo, hi = hi, lo } font_push_width(&out, lo, hi, w) } } if len(out) == 0 { return } f.cid_widths = font_flatten(out[:], doc.allocator) } // font_push_width appends a run, merging it into the previous one when they // are adjacent and equally wide. The `c [w w w ...]` form is one entry per // CID as written, and a CJK font writes tens of thousands of them; merging // turns the common runs of equal widths into single entries and costs one // comparison. @(private) font_push_width :: proc(out: ^[dynamic]Cid_Width, lo, hi: u32, w: f64) { if len(out) > 0 { last := &out[len(out) - 1] if last.width == w && last.hi + 1 == lo && last.hi < hi { last.hi = hi return } } append(out, Cid_Width{lo = lo, hi = hi, width = w}) } // font_parse_w2 reads /W2, the vertical counterpart of /W. Its entries are // triples rather than single numbers: // // c [w1y v1x v1y w1y v1x v1y ...] // c_first c_last w1y v1x v1y @(private) font_parse_w2 :: proc(doc: ^Document, f: ^Font, a: ^Array) { out := make([dynamic]Cid_Vmetric, 0, 8, doc.allocator) i := 0 n := array_len(a) fuel := MAX_W_ITEMS for i < n && fuel > 0 && len(out) < MAX_CID_WIDTHS { fuel -= 1 first, ok := font_cid_operand(doc, a, i) if !ok { i += 1 continue } i += 1 if i >= n { break } #partial switch v in array_get(doc, a, i) { case ^Array: i += 1 m := array_len(v) / 3 for k in 0 ..< m { if len(out) >= MAX_CID_WIDTHS { break } fuel -= 1 if fuel <= 0 { break } c := u64(first) + u64(k) if c > 0xFFFF { break } append(&out, Cid_Vmetric{ lo = u32(c), hi = u32(c), w1y = font_clamp_width(array_num(doc, v, 3 * k, 0)), v1x = font_clamp_width(array_num(doc, v, 3 * k + 1, 0)), v1y = font_clamp_width(array_num(doc, v, 3 * k + 2, 0)), }) } case: last, ok2 := font_cid_operand(doc, a, i) i += 1 if !ok2 || i + 2 >= n { break } lo, hi := first, last if hi < lo { lo, hi = hi, lo } append(&out, Cid_Vmetric{ lo = lo, hi = hi, w1y = font_clamp_width(array_num(doc, a, i, 0)), v1x = font_clamp_width(array_num(doc, a, i + 1, 0)), v1y = font_clamp_width(array_num(doc, a, i + 2, 0)), }) i += 3 } } if len(out) == 0 { return } f.cid_vmetrics = font_flatten(out[:], doc.allocator) } // font_cid_operand reads array element `i` as a CID. Anything that is not a // number in range fails rather than being coerced: a /W array that has gone // out of step must not manufacture a range covering the whole CID space. @(private) font_cid_operand :: proc(doc: ^Document, a: ^Array, i: int) -> (u32, bool) { v, ok := to_number(array_get(doc, a, i)) if !ok || !is_finite(v) || v < 0 || v > 0xFFFF { return 0, false } return u32(v), true } // font_flatten turns a set of CID runs into an equivalent set that is sorted // and disjoint, so that a lookup is a binary search and nothing else. // // /W and /W2 are meant to name disjoint ranges, and real files overlap them: // the shape it always takes is a broad run with narrower corrections written // over parts of it. The rule applied here is that where two runs share a CID, // the one that STARTS LATER owns it -- the correction is always the later // start -- and the earlier run keeps what is left, which may be a piece on // each side of the correction. // // The sweep below is a stack of the runs covering the CID it has reached, most // recently started on top. That top is the run that owns the CIDs from here // until either it ends or a new run starts, which is exactly one output entry; // runs that ended while they were shadowed are dropped from the stack when // they surface. Two runs that start at the same CID are a contradiction the // file has to answer for, and which of them wins is not defined. // // Doing this once, at load, is what lets the lookup be exact. Resolving the // overlap at lookup time instead means scanning backwards from where the // binary search lands, and the distance is unbounded: a file that writes one // run over the whole CID space and then a hundred corrections inside it puts // the answer a hundred entries back. Either that scan is bounded and sometimes // wrong, or it is unbounded and sometimes very slow. This is neither. // // The result holds at most twice as many runs as it was given: every entry // emitted ends either at some run's `hi` or at some run's `lo`, and there are // only so many of each. @(private) font_flatten :: proc(src: []$T, allocator: mem.Allocator) -> []T { if len(src) <= 1 { return src } slice.sort_by(src, proc(x, y: T) -> bool { return x.lo < y.lo }) out := make([dynamic]T, 0, len(src) + 1, allocator) // Scratch, and freed here: the depth is the file's to choose and the // Document's arena never gives anything back. stack := make([dynamic]T, 0, 16, context.allocator) defer delete(stack) cur: u32 = 0 for e in src { if e.hi < e.lo { continue } if e.lo > cur { font_flatten_emit(&out, &stack, &cur, e.lo - 1) } append(&stack, e) } // Each pass either pops a run or emits one, and the counts of both are // bounded by the input, so the drain has fuel rather than a proof that a // `hi` of 0xFFFFFFFF cannot wrap `cur` back to zero. for fuel := 2 * len(src) + 2; fuel > 0 && len(stack) > 0; fuel -= 1 { top := stack[len(stack) - 1] if top.hi < cur { pop(&stack) continue } font_flatten_emit(&out, &stack, &cur, top.hi) } return out[:] } // font_flatten_emit writes out the CIDs from `cur` through `limit`, taking // each one's value from the innermost run still covering it, and leaves `cur` // just past `limit`. CIDs no run covers produce no entry at all, which is what // makes the caller fall back to /DW. // // Every iteration either pops the stack or reaches `limit`, so the loop is // bounded by the stack's depth without needing a counter to say so. @(private) font_flatten_emit :: proc(out, stack: ^[dynamic]$T, cur: ^u32, limit: u32) { for cur^ <= limit { if len(stack) > 0 && stack[len(stack) - 1].hi < cur^ { pop(stack) continue } if len(stack) == 0 { break } e := stack[len(stack) - 1] e.lo = cur^ e.hi = min(e.hi, limit) append(out, e) cur^ = e.hi + 1 } cur^ = max(cur^, limit + 1) } // font_find_range is a binary search over disjoint, sorted runs. @(private) font_find_range :: proc(a: []$T, cid: u32) -> (int, bool) { lo, hi := 0, len(a) for lo < hi { mid := lo + (hi - lo) / 2 if a[mid].lo <= cid { lo = mid + 1 } else { hi = mid } } if lo > 0 && cid <= a[lo - 1].hi { return lo - 1, true } return 0, false } // font_cid_width is the horizontal advance of one CID, in glyph space. @(private) font_cid_width :: proc(f: ^Font, cid: u32) -> f64 { if i, ok := font_find_range(f.cid_widths, cid); ok { return f.cid_widths[i].width } return f.default_width } // font_cid_vmetric is the vertical displacement and position vector of one // CID. `w0` is the horizontal width, which the default position vector needs. @(private) font_cid_vmetric :: proc(f: ^Font, cid: u32, w0: f64) -> (w1y, vx, vy: f64) { if i, ok := font_find_range(f.cid_vmetrics, cid); ok { e := f.cid_vmetrics[i] return e.w1y, e.v1x, e.v1y } // 9.7.4.3: with no /W2 entry the position vector is (w0/2, DW2[0]) and // the displacement is (0, DW2[1]). return f.default_w1, w0 / 2, f.default_vy } // --------------------------------------------------------------------------- // Embedded font programs (9.9) // --------------------------------------------------------------------------- // font_load_program reads /FontFile, /FontFile2 or /FontFile3 out of the // descriptor. // // A program that will not parse is recorded and dropped: `program_failed` is // set, no error is returned, and the font goes on to build its widths and its // encoding as if it had never had a program. Text that cannot be drawn must // still lay out and extract, and a subsetter that truncated its own font file // is not a reason to lose the page. @(private) font_load_program :: proc(doc: ^Document, f: ^Font, desc: ^Dict) { if desc == nil { return } kind := Font_Kind.Type1 stm := dict_stream(doc, desc, "FontFile") if stm == nil { if s2 := dict_stream(doc, desc, "FontFile2"); s2 != nil { stm = s2 kind = .TrueType } } sub := "" if stm == nil { if s3 := dict_stream(doc, desc, "FontFile3"); s3 != nil { stm = s3 sub = dict_name(doc, s3.dict, "Subtype") // /OpenType is an sfnt wrapper whose outlines may be either kind; // the sniff below settles it from the bytes. kind = sub == "OpenType" ? .TrueType : .Type1 } } if stm == nil { return } data, err := stream_data(doc, stm) if err != .None || len(data) == 0 { f.program_failed = true add_repair(doc, .Bad_Font_Object, 0, stm.ref) return } if font_parse_program(doc, f, data, kind, sub) { f.embedded = true return } f.program_failed = true add_repair(doc, .Bad_Font_Object, 0, stm.ref) } // font_parse_program turns the bytes of an embedded program into a reader. // // The declared subtype is a hint, not a fact. /FontFile3 /Type1C files that // are really OpenType, /FontFile2 entries holding a bare CFF and /FontFile // entries holding a PFB are all in circulation, and every one of them is // unambiguous from its first four bytes -- so the magic decides and the // dictionary only breaks ties. @(private) font_parse_program :: proc(doc: ^Document, f: ^Font, data: []byte, kind: Font_Kind, sub: string) -> bool { switch { case font_is_sfnt(data): ttf, err := tt_parse(data, doc.allocator) if err != .None || ttf == nil { return false } f.ttf = ttf if tt_has_glyf(ttf) { return true } // An OpenType font with no `glyf` keeps its outlines in a `CFF ` // table. The sfnt wrapper still carries the cmap and hmtx, so both // readers stay live: the TrueType one answers character lookups and // the CFF one draws. cff_data := tt_cff_data(ttf) if len(cff_data) == 0 { return false } cff, cerr := cff_parse(cff_data, doc.allocator) if cerr != .None || cff == nil { return false } f.cff = cff return true case font_is_cff(data): cff, err := cff_parse(data, doc.allocator) if err != .None || cff == nil { return false } f.cff = cff return true case font_is_type1(data): t1, err := t1_parse(data, doc.allocator) if err != .None || t1 == nil { return false } f.t1 = t1 return true } // No magic matched. Believe the dictionary, which is all that is left. if kind == .TrueType || sub == "OpenType" { ttf, err := tt_parse(data, doc.allocator) if err == .None && ttf != nil { f.ttf = ttf return true } return false } if sub == "Type1C" || sub == "CIDFontType0C" { cff, err := cff_parse(data, doc.allocator) if err == .None && cff != nil { f.cff = cff return true } return false } t1, err := t1_parse(data, doc.allocator) if err == .None && t1 != nil { f.t1 = t1 return true } return false } @(private) font_is_sfnt :: proc(data: []byte) -> bool { if len(data) < 4 { return false } tag := u32(data[0]) << 24 | u32(data[1]) << 16 | u32(data[2]) << 8 | u32(data[3]) return tag == TT_TAG_V1 || tag == TT_TAG_V2 || tag == TT_TAG_TRUE || tag == TT_TAG_OTTO || tag == TT_TAG_TTCF } // font_is_cff matches a bare CFF header: major version 1, minor 0, and a // header size that is at least the four bytes of the header itself. The // offSize byte that follows must be 1..4. CFF 6. @(private) font_is_cff :: proc(data: []byte) -> bool { return len(data) >= 4 && data[0] == 1 && data[1] == 0 && data[2] >= 4 && data[3] >= 1 && data[3] <= 4 } // font_is_type1 matches the two containers a Type1 program arrives in: a PFB // segment header (0x80 then a segment type) or the "%!" of a PFA/PostScript // clear-text header. @(private) font_is_type1 :: proc(data: []byte) -> bool { if len(data) < 2 { return false } if data[0] == 0x80 && data[1] >= 1 && data[1] <= 3 { return true } return data[0] == '%' && data[1] == '!' } // font_set_matrix fixes the glyph space the program's outlines are in. @(private) font_set_matrix :: proc(f: ^Font) { switch { // A CFF carries its own matrix and it wins wherever there is one: an // OpenType wrapper leaves both readers live, and it is the CFF that draws. case f.cff != nil: m := cff_font_matrix(f.cff) f.mtx = matrix_is_finite(m) && matrix_determinant(m) != 0 ? m : Matrix{0.001, 0, 0, 0.001, 0, 0} case f.ttf != nil: upem := tt_units_per_em(f.ttf) if upem <= 0 || !is_finite(upem) { upem = 1000 } f.mtx = Matrix{1 / upem, 0, 0, 1 / upem, 0, 0} case f.t1 != nil: m := t1_font_matrix(f.t1) f.mtx = matrix_is_finite(m) && matrix_determinant(m) != 0 ? m : Matrix{0.001, 0, 0, 0.001, 0, 0} case: f.mtx = Matrix{0.001, 0, 0, 0.001, 0, 0} } } // --------------------------------------------------------------------------- // Decoding a string // --------------------------------------------------------------------------- // font_decode splits a PDF string into character codes and fills in // everything the interpreter and a text device need about each one. // // `out` is CLEARED first, so it may be a scratch buffer reused across every // string on the page. One byte is one code for a simple font; a composite // font's codes are as wide as its CMap's codespace ranges say, which is not // necessarily two and not necessarily constant within one string. font_decode :: proc(f: ^Font, s: []byte, out: ^[dynamic]Font_Code) { if out == nil { return } clear(out) if f == nil || len(s) == 0 { return } i := 0 for i < len(s) { fc: Font_Code if f.kind == .Type0 { code, n := cmap_next_code(f.cmap, s[i:]) // cmap_next_code consumes at least one byte for a non-empty // string, but the loop's termination must not depend on a // promise made in another file. fc.nbytes = max(n, 1) fc.code = code if cid, ok := cmap_lookup(f.cmap, code); ok { fc.cid = cid } fc.gid = font_cid_gid(f, fc.cid) w0 := font_cid_width(f, fc.cid) fc.width = w0 if f.vertical { w1y, vx, vy := font_cid_vmetric(f, fc.cid, w0) fc.width = w1y fc.vx, fc.vy = vx, vy } } else { b := s[i] fc.code = u32(b) fc.cid = u32(b) fc.nbytes = 1 fc.gid = f.code_gid[b] fc.width = f.code_width[b] } // 9.3.3: word spacing applies to a single-byte code 32 and to nothing // else. A two-byte 0x0020 in a Type0 font is a perfectly ordinary // CID, and taking it for a space stretches CJK text at random. fc.is_space = fc.nbytes == 1 && fc.code == 32 fc.unicode = font_code_unicode(f, fc) append(out, fc) i += fc.nbytes } } // font_code_unicode is the character a code stands for, or -1. // // /ToUnicode is authoritative when it is there: it is the producer telling us // what it meant, and it is the only route that works for a subset font whose // glyph names are "g17". Failing that a simple font's glyph name is turned // into a code point through the AGL, which covers every font that uses real // names. The `post` table is the same trick for a symbolic TrueType that has // no PDF encoding at all. @(private) font_code_unicode :: proc(f: ^Font, fc: Font_Code) -> rune { if f.to_unicode != nil { if txt, ok := cmap_lookup_text(f.to_unicode, fc.code); ok && len(txt) > 0 { r, _ := utf8.decode_rune_in_string(txt) if r != utf8.RUNE_ERROR { return r } } } if f.kind == .Type0 { // A Type0 font may wrap a name-keyed CFF, in which case the glyph // still has a name even though the code never went through one. if f.cff != nil && fc.gid <= 0xFFFF { if name := cff_glyph_name(f.cff, u16(fc.gid)); name != "" { if r := glyph_name_to_rune(name); r >= 0 { return r } } } } else if fc.code < FONT_CODES { if name := f.enc_names[fc.code]; name != "" { if r := glyph_name_to_rune(name); r >= 0 { return r } } } if f.ttf != nil && fc.gid > 0 && int(fc.gid) < len(f.ttf.names) { if r := glyph_name_to_rune(f.ttf.names[fc.gid]); r >= 0 { return r } } // Last resort, and only for a non-symbolic single-byte code: over ASCII // every Latin text encoding agrees with Unicode, so a font that offered // no other answer still extracts as readable text. A symbolic font is // excluded because its code 0x41 is a dingbat, not an "A". if !f.symbolic && fc.nbytes == 1 && fc.code >= 0x20 && fc.code < 0x7F { return rune(fc.code) } return -1 } // font_clamp_width keeps a width from the file finite and within an absurd // but harmless range. A NaN here reaches the text matrix and from there the // geometry, which is not allowed to see one. @(private) font_clamp_width :: proc(w: f64) -> f64 { if !is_finite(w) { return 0 } return clamp(w, -MAX_GLYPH_WIDTH, MAX_GLYPH_WIDTH) }