1464 lines
52 KiB
Odin
1464 lines
52 KiB
Odin
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)
|
|
}
|