Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 66 additions & 36 deletions page.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,20 @@ func (f Font) Encoder() TextEncoding {
}

func (f Font) getEncoder() TextEncoding {
// Check ToUnicode first - it's the authoritative character mapping
// per PDF spec and takes precedence over Encoding
toUnicode := f.V.Key("ToUnicode")
if toUnicode.Kind() == Stream {
if m := readCmap(toUnicode); m != nil {
return m
}
// ToUnicode stream exists but failed to parse - fall through to Encoding
if DebugOn {
println("ToUnicode stream failed to parse, falling back to Encoding")
}
}

// Fall back to Encoding-based decoding
enc := f.V.Key("Encoding")
switch enc.Kind() {
case Name:
Expand All @@ -209,65 +223,81 @@ func (f Font) getEncoder() TextEncoding {
case "MacRomanEncoding":
return &byteEncoder{&macRomanEncoding}
case "Identity-H":
return f.charmapEncoding()
return &byteEncoder{&pdfDocEncoding}
default:
if DebugOn {
println("unknown encoding", enc.Name())
}
return &nopEncoder{}
return &byteEncoder{&pdfDocEncoding}
}
case Dict:
return &dictEncoder{enc.Key("Differences")}
return newDictEncoder(enc)
case Null:
return f.charmapEncoding()
return &byteEncoder{&pdfDocEncoding}
default:
if DebugOn {
println("unexpected encoding", enc.String())
}
return &nopEncoder{}
return &byteEncoder{&pdfDocEncoding}
}
}

func (f *Font) charmapEncoding() TextEncoding {
toUnicode := f.V.Key("ToUnicode")
if toUnicode.Kind() == Stream {
m := readCmap(toUnicode)
if m == nil {
return &nopEncoder{}
}
return m
}

return &byteEncoder{&pdfDocEncoding}
}

// dictEncoder handles fonts with Encoding dictionaries containing
// BaseEncoding and/or Differences arrays per PDF spec section 9.6.6.
type dictEncoder struct {
v Value
table [256]rune // combined encoding table
}

func (e *dictEncoder) Decode(raw string) (text string) {
r := make([]rune, 0, len(raw))
for i := 0; i < len(raw); i++ {
ch := rune(raw[i])
n := -1
for j := 0; j < e.v.Len(); j++ {
x := e.v.Index(j)
// newDictEncoder creates an encoder from an Encoding dictionary.
// It first applies BaseEncoding (defaulting to StandardEncoding/PDFDocEncoding),
// then overlays any Differences.
func newDictEncoder(enc Value) *dictEncoder {
e := &dictEncoder{}

// Start with base encoding
baseEnc := enc.Key("BaseEncoding")
var baseTable *[256]rune
switch baseEnc.Name() {
case "WinAnsiEncoding":
baseTable = &winAnsiEncoding
case "MacRomanEncoding":
baseTable = &macRomanEncoding
case "MacExpertEncoding":
baseTable = &pdfDocEncoding // fallback
default:
// Per PDF spec, if BaseEncoding is absent, use the font's built-in
// encoding. For simplicity, we use PDFDocEncoding as fallback.
baseTable = &pdfDocEncoding
}
copy(e.table[:], baseTable[:])

// Apply Differences array on top
// Format: [firstCode /name1 /name2 ... nextCode /nameN ...]
diff := enc.Key("Differences")
if diff.Kind() == Array {
code := -1
for j := 0; j < diff.Len(); j++ {
x := diff.Index(j)
if x.Kind() == Integer {
n = int(x.Int64())
code = int(x.Int64())
continue
}
if x.Kind() == Name {
if int(raw[i]) == n {
r := nameToRune[x.Name()]
if r != 0 {
ch = r
break
}
if x.Kind() == Name && code >= 0 && code < 256 {
if r := nameToRune[x.Name()]; r != 0 {
e.table[code] = r
}
n++
code++
}
}
r = append(r, ch)
}

return e
}

func (e *dictEncoder) Decode(raw string) (text string) {
r := make([]rune, 0, len(raw))
for i := 0; i < len(raw); i++ {
r = append(r, e.table[raw[i]])
}
return string(r)
}
Expand Down