mirror of
https://github.com/unidoc/unipdf.git
synced 2025-04-27 13:48:51 +08:00
Merge pull request #259 from dennwc/font_codes_strict
Strict types for runes, char codes and GIDs
This commit is contained in:
commit
01a87efdf3
@ -12,6 +12,7 @@ import (
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/contentstream"
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
"github.com/unidoc/unidoc/pdf/internal/textencoding"
|
||||
"github.com/unidoc/unidoc/pdf/model"
|
||||
)
|
||||
|
||||
@ -33,7 +34,7 @@ type AppearanceStyle struct {
|
||||
// How much of Rect height to fill when autosizing text.
|
||||
AutoFontSizeFraction float64
|
||||
// Glyph used for check mark in checkboxes (for ZapfDingbats font).
|
||||
CheckmarkGlyph string
|
||||
CheckmarkGlyph textencoding.GlyphName
|
||||
|
||||
BorderSize float64
|
||||
BorderColor model.PdfColor
|
||||
@ -1111,7 +1112,9 @@ func (style *AppearanceStyle) applyAppearanceCharacteristics(mkDict *core.PdfObj
|
||||
if CA, has := core.GetString(mkDict.Get("CA")); has && font != nil {
|
||||
encoded := CA.Bytes()
|
||||
if len(encoded) == 1 {
|
||||
if checkglyph, has := font.Encoder().CharcodeToGlyph(uint16(encoded[0])); has {
|
||||
// TODO: this may be a multi-byte encoding
|
||||
charcode := textencoding.CharCode(encoded[0])
|
||||
if checkglyph, has := font.Encoder().CharcodeToGlyph(charcode); has {
|
||||
style.CheckmarkGlyph = checkglyph
|
||||
}
|
||||
}
|
||||
|
@ -286,13 +286,15 @@ func (p *Paragraph) wrapText() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
line := []rune{}
|
||||
var line []rune
|
||||
lineWidth := 0.0
|
||||
p.textLines = []string{}
|
||||
p.textLines = nil
|
||||
|
||||
runes := []rune(p.text)
|
||||
glyphs := []string{}
|
||||
widths := []float64{}
|
||||
var (
|
||||
glyphs []textencoding.GlyphName
|
||||
widths []float64
|
||||
)
|
||||
|
||||
for _, val := range runes {
|
||||
glyph, found := p.textFont.Encoder().RuneToGlyph(val)
|
||||
@ -305,10 +307,10 @@ func (p *Paragraph) wrapText() error {
|
||||
if glyph == "controlLF" {
|
||||
// Moves to next line.
|
||||
p.textLines = append(p.textLines, string(line))
|
||||
line = []rune{}
|
||||
line = nil
|
||||
lineWidth = 0
|
||||
widths = []float64{}
|
||||
glyphs = []string{}
|
||||
widths = nil
|
||||
glyphs = nil
|
||||
continue
|
||||
}
|
||||
|
||||
@ -345,7 +347,7 @@ func (p *Paragraph) wrapText() error {
|
||||
} else {
|
||||
p.textLines = append(p.textLines, string(line))
|
||||
line = []rune{val}
|
||||
glyphs = []string{glyph}
|
||||
glyphs = []textencoding.GlyphName{glyph}
|
||||
widths = []float64{w}
|
||||
lineWidth = w
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/unidoc/unidoc/pdf/internal/textencoding"
|
||||
|
||||
"github.com/unidoc/unidoc/common"
|
||||
"github.com/unidoc/unidoc/pdf/contentstream"
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
@ -352,7 +354,7 @@ func (p *StyledParagraph) wrapText() error {
|
||||
annotation := chunk.annotation
|
||||
|
||||
var part []rune
|
||||
var glyphs []string
|
||||
var glyphs []textencoding.GlyphName
|
||||
var widths []float64
|
||||
|
||||
for _, r := range chunk.Text {
|
||||
@ -373,12 +375,12 @@ func (p *StyledParagraph) wrapText() error {
|
||||
annotation: copyAnnotation(annotation),
|
||||
})
|
||||
p.lines = append(p.lines, line)
|
||||
line = []*TextChunk{}
|
||||
line = nil
|
||||
|
||||
lineWidth = 0
|
||||
part = []rune{}
|
||||
widths = []float64{}
|
||||
glyphs = []string{}
|
||||
part = nil
|
||||
widths = nil
|
||||
glyphs = nil
|
||||
continue
|
||||
}
|
||||
|
||||
@ -422,7 +424,7 @@ func (p *StyledParagraph) wrapText() error {
|
||||
} else {
|
||||
lineWidth = w
|
||||
part = []rune{r}
|
||||
glyphs = []string{glyph}
|
||||
glyphs = []textencoding.GlyphName{glyph}
|
||||
widths = []float64{w}
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,11 @@ type CMap struct {
|
||||
codespaces []Codespace
|
||||
|
||||
// For ToUnicode (ctype 2) cmaps.
|
||||
codeToUnicode map[CharCode]string
|
||||
codeToUnicode map[CharCode]rune
|
||||
}
|
||||
|
||||
// NewToUnicodeCMap returns an identity CMap with codeToUnicode matching the `codeToUnicode` arg.
|
||||
func NewToUnicodeCMap(codeToUnicode map[CharCode]string) *CMap {
|
||||
func NewToUnicodeCMap(codeToUnicode map[CharCode]rune) *CMap {
|
||||
return &CMap{
|
||||
name: "Adobe-Identity-UCS",
|
||||
ctype: 2,
|
||||
@ -102,11 +102,10 @@ func newCMap(isSimple bool) *CMap {
|
||||
if isSimple {
|
||||
nbits = 8
|
||||
}
|
||||
cmap := &CMap{
|
||||
return &CMap{
|
||||
nbits: nbits,
|
||||
codeToUnicode: map[CharCode]string{},
|
||||
codeToUnicode: make(map[CharCode]rune),
|
||||
}
|
||||
return cmap
|
||||
}
|
||||
|
||||
// String returns a human readable description of `info`.
|
||||
@ -153,9 +152,6 @@ func (cmap *CMap) Type() int {
|
||||
// MissingCodeRune replaces runes that can't be decoded. '\ufffd' = <20>. Was '?'.
|
||||
const MissingCodeRune = textencoding.MissingCodeRune
|
||||
|
||||
// MissingCodeString replaces strings that can't be decoded.
|
||||
var MissingCodeString = string(MissingCodeRune)
|
||||
|
||||
// CharcodeBytesToUnicode converts a byte array of charcodes to a unicode string representation.
|
||||
// It also returns a bool flag to tell if the conversion was successful.
|
||||
// NOTE: This only works for ToUnicode cmaps.
|
||||
@ -167,17 +163,19 @@ func (cmap *CMap) CharcodeBytesToUnicode(data []byte) (string, int) {
|
||||
return "", 0
|
||||
}
|
||||
|
||||
parts := []string{}
|
||||
missing := []CharCode{}
|
||||
var (
|
||||
parts []rune
|
||||
missing []CharCode
|
||||
)
|
||||
for _, code := range charcodes {
|
||||
s, ok := cmap.codeToUnicode[code]
|
||||
if !ok {
|
||||
missing = append(missing, code)
|
||||
s = MissingCodeString
|
||||
s = MissingCodeRune
|
||||
}
|
||||
parts = append(parts, s)
|
||||
}
|
||||
unicode := strings.Join(parts, "")
|
||||
unicode := string(parts)
|
||||
if len(missing) > 0 {
|
||||
common.Log.Debug("ERROR: CharcodeBytesToUnicode. Not in map.\n"+
|
||||
"\tdata=[% 02x]=%#q\n"+
|
||||
@ -191,13 +189,13 @@ func (cmap *CMap) CharcodeBytesToUnicode(data []byte) (string, int) {
|
||||
}
|
||||
|
||||
// CharcodeToUnicode converts a single character code `code` to a unicode string.
|
||||
// If `code` is not in the unicode map, "<22>" is returned.
|
||||
// If `code` is not in the unicode map, '<27>' is returned.
|
||||
// NOTE: CharcodeBytesToUnicode is typically more efficient.
|
||||
func (cmap *CMap) CharcodeToUnicode(code CharCode) (string, bool) {
|
||||
func (cmap *CMap) CharcodeToUnicode(code CharCode) (rune, bool) {
|
||||
if s, ok := cmap.codeToUnicode[code]; ok {
|
||||
return s, true
|
||||
}
|
||||
return MissingCodeString, false
|
||||
return MissingCodeRune, false
|
||||
}
|
||||
|
||||
// bytesToCharcodes attempts to convert the entire byte array `data` to a list of character codes
|
||||
@ -207,7 +205,7 @@ func (cmap *CMap) CharcodeToUnicode(code CharCode) (string, bool) {
|
||||
// matched?
|
||||
// NOTE: A partial list of character codes will be returned if a complete match is not possible.
|
||||
func (cmap *CMap) bytesToCharcodes(data []byte) ([]CharCode, bool) {
|
||||
charcodes := []CharCode{}
|
||||
var charcodes []CharCode
|
||||
if cmap.nbits == 8 {
|
||||
for _, b := range data {
|
||||
charcodes = append(charcodes, CharCode(b))
|
||||
@ -350,7 +348,7 @@ func (cmap *CMap) toBfData() string {
|
||||
fbRanges = append(fbRanges, fbRange{
|
||||
code0: cr.code0,
|
||||
code1: cr.code1,
|
||||
r0: []rune(cmap.codeToUnicode[cr.code0])[0],
|
||||
r0: cmap.codeToUnicode[cr.code0],
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -365,8 +363,7 @@ func (cmap *CMap) toBfData() string {
|
||||
lines = append(lines, fmt.Sprintf("%d beginbfchar", n))
|
||||
for j := 0; j < n; j++ {
|
||||
code := fbChars[i*maxBfEntries+j]
|
||||
s := cmap.codeToUnicode[code]
|
||||
r := []rune(s)[0]
|
||||
r := cmap.codeToUnicode[code]
|
||||
lines = append(lines, fmt.Sprintf("<%04x> <%04x>", code, r))
|
||||
}
|
||||
lines = append(lines, "endbfchar")
|
||||
|
@ -391,8 +391,7 @@ func (cmap *CMap) parseBfchar() error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
target := ""
|
||||
|
||||
var target rune
|
||||
switch v := o.(type) {
|
||||
case cmapOperand:
|
||||
if v.Operand == endbfchar {
|
||||
@ -401,10 +400,10 @@ func (cmap *CMap) parseBfchar() error {
|
||||
common.Log.Debug("ERROR: Unexpected operand. %#v", v)
|
||||
return ErrBadCMap
|
||||
case cmapHexString:
|
||||
target = hexToString(v)
|
||||
target = hexToRune(v)
|
||||
case cmapName:
|
||||
common.Log.Debug("ERROR: Unexpected name. %#v", v)
|
||||
target = MissingCodeString
|
||||
target = MissingCodeRune
|
||||
default:
|
||||
common.Log.Debug("ERROR: Unexpected type. %#v", o)
|
||||
return ErrBadCMap
|
||||
@ -484,17 +483,16 @@ func (cmap *CMap) parseBfrange() error {
|
||||
if !ok {
|
||||
return errors.New("Non-hex string in array")
|
||||
}
|
||||
s := hexToString(hexs)
|
||||
cmap.codeToUnicode[code] = s
|
||||
r := hexToRune(hexs)
|
||||
cmap.codeToUnicode[code] = r
|
||||
}
|
||||
|
||||
case cmapHexString:
|
||||
// <codeFrom> <codeTo> <dst>, maps [from,to] to [dst,dst+to-from].
|
||||
target := hexToString(v)
|
||||
runes := []rune(target)
|
||||
r := hexToRune(v)
|
||||
for code := srcCodeFrom; code <= srcCodeTo; code++ {
|
||||
cmap.codeToUnicode[code] = string(runes)
|
||||
runes[len(runes)-1]++
|
||||
cmap.codeToUnicode[code] = r
|
||||
r++
|
||||
}
|
||||
default:
|
||||
common.Log.Debug("ERROR: Unexpected type %T", o)
|
||||
|
@ -104,14 +104,14 @@ func TestCMapParser1(t *testing.T) {
|
||||
}
|
||||
|
||||
for k, expected := range expectedMappings {
|
||||
if v, ok := cmap.CharcodeToUnicode(k); !ok || v != string(expected) {
|
||||
if v, ok := cmap.CharcodeToUnicode(k); !ok || v != expected {
|
||||
t.Errorf("incorrect mapping, expecting 0x%X ➞ 0x%X (%#v)", k, expected, v)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
v, _ := cmap.CharcodeToUnicode(0x99)
|
||||
if v != MissingCodeString { //!= "notdef" {
|
||||
if v != MissingCodeRune { //!= "notdef" {
|
||||
t.Errorf("Unmapped code, expected to map to undefined")
|
||||
return
|
||||
}
|
||||
@ -188,7 +188,7 @@ func TestCMapParser2(t *testing.T) {
|
||||
}
|
||||
|
||||
for k, expected := range expectedMappings {
|
||||
if v, ok := cmap.CharcodeToUnicode(k); !ok || v != string(expected) {
|
||||
if v, ok := cmap.CharcodeToUnicode(k); !ok || v != expected {
|
||||
t.Errorf("incorrect mapping, expecting 0x%X ➞ 0x%X (got 0x%X)", k, expected, v)
|
||||
return
|
||||
}
|
||||
@ -297,7 +297,7 @@ func TestCMapParser3(t *testing.T) {
|
||||
0xd140: 0xa000,
|
||||
}
|
||||
for k, expected := range expectedMappings {
|
||||
if v, ok := cmap.CharcodeToUnicode(k); !ok || v != string(expected) {
|
||||
if v, ok := cmap.CharcodeToUnicode(k); !ok || v != expected {
|
||||
t.Errorf("incorrect mapping: expecting 0x%02X ➞ 0x%02X (got 0x%02X)", k, expected, v)
|
||||
return
|
||||
}
|
||||
@ -399,11 +399,11 @@ func TestCMapParser4(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
expectedMappings := map[CharCode]string{
|
||||
0x0889: "\U0001d484", // `𝒄`
|
||||
0x0893: "\U0001d48e", // `𝒎`
|
||||
0x08DD: "\U0001d49e", // `𝒞`
|
||||
0x08E5: "\U0001d4a6", // `𝒦
|
||||
expectedMappings := map[CharCode]rune{
|
||||
0x0889: '\U0001d484', // `𝒄`
|
||||
0x0893: '\U0001d48e', // `𝒎`
|
||||
0x08DD: '\U0001d49e', // `𝒞`
|
||||
0x08E5: '\U0001d4a6', // `𝒦
|
||||
}
|
||||
|
||||
for k, expected := range expectedMappings {
|
||||
@ -435,185 +435,185 @@ func TestCMapParser4(t *testing.T) {
|
||||
}
|
||||
|
||||
var (
|
||||
codeToUnicode1 = map[CharCode]string{ // 40 entries
|
||||
0x02ca: "ˊ",
|
||||
0x02cb: "ˋ",
|
||||
0x02cd: "ˍ",
|
||||
0x039c: "Μ",
|
||||
0x039d: "Ν",
|
||||
0x039e: "Ξ",
|
||||
0x039f: "Ο",
|
||||
0x03a0: "Π",
|
||||
0x03a1: "Ρ",
|
||||
0x03a6: "Φ",
|
||||
0x03b1: "α",
|
||||
0x03b2: "β",
|
||||
0x03b3: "γ",
|
||||
0x03b4: "δ",
|
||||
0x03b5: "ε",
|
||||
0x03b6: "ζ",
|
||||
0x03b7: "η",
|
||||
0x03c6: "φ",
|
||||
0x03c7: "χ",
|
||||
0x03c9: "ω",
|
||||
0x2013: "–",
|
||||
0x2014: "—",
|
||||
0x2018: "‘",
|
||||
0x2019: "’",
|
||||
0x203e: "‾",
|
||||
0x20ac: "€",
|
||||
0x2163: "Ⅳ",
|
||||
0x2164: "Ⅴ",
|
||||
0x2165: "Ⅵ",
|
||||
0x2166: "Ⅶ",
|
||||
0x2167: "Ⅷ",
|
||||
0x2168: "Ⅸ",
|
||||
0x2169: "Ⅹ",
|
||||
0x2190: "←",
|
||||
0x2191: "↑",
|
||||
0x2192: "→",
|
||||
0x2193: "↓",
|
||||
0x2220: "∠",
|
||||
0x2223: "∣",
|
||||
0x222a: "∪",
|
||||
codeToUnicode1 = map[CharCode]rune{ // 40 entries
|
||||
0x02ca: 'ˊ',
|
||||
0x02cb: 'ˋ',
|
||||
0x02cd: 'ˍ',
|
||||
0x039c: 'Μ',
|
||||
0x039d: 'Ν',
|
||||
0x039e: 'Ξ',
|
||||
0x039f: 'Ο',
|
||||
0x03a0: 'Π',
|
||||
0x03a1: 'Ρ',
|
||||
0x03a6: 'Φ',
|
||||
0x03b1: 'α',
|
||||
0x03b2: 'β',
|
||||
0x03b3: 'γ',
|
||||
0x03b4: 'δ',
|
||||
0x03b5: 'ε',
|
||||
0x03b6: 'ζ',
|
||||
0x03b7: 'η',
|
||||
0x03c6: 'φ',
|
||||
0x03c7: 'χ',
|
||||
0x03c9: 'ω',
|
||||
0x2013: '–',
|
||||
0x2014: '—',
|
||||
0x2018: '‘',
|
||||
0x2019: '’',
|
||||
0x203e: '‾',
|
||||
0x20ac: '€',
|
||||
0x2163: 'Ⅳ',
|
||||
0x2164: 'Ⅴ',
|
||||
0x2165: 'Ⅵ',
|
||||
0x2166: 'Ⅶ',
|
||||
0x2167: 'Ⅷ',
|
||||
0x2168: 'Ⅸ',
|
||||
0x2169: 'Ⅹ',
|
||||
0x2190: '←',
|
||||
0x2191: '↑',
|
||||
0x2192: '→',
|
||||
0x2193: '↓',
|
||||
0x2220: '∠',
|
||||
0x2223: '∣',
|
||||
0x222a: '∪',
|
||||
}
|
||||
|
||||
codeToUnicode2 = map[CharCode]string{ // 40 entries
|
||||
0x0100: "Ā",
|
||||
0x0101: "ā",
|
||||
0x0102: "Ă",
|
||||
0x0111: "đ",
|
||||
0x0112: "Ē",
|
||||
0x0113: "ē",
|
||||
0x0114: "Ĕ",
|
||||
0x0115: "ĕ",
|
||||
0x0116: "Ė",
|
||||
0x011b: "ě",
|
||||
0x0126: "Ħ",
|
||||
0x0127: "ħ",
|
||||
0x0128: "Ĩ",
|
||||
0x0129: "ĩ",
|
||||
0x012a: "Ī",
|
||||
0x012b: "ī",
|
||||
0x012c: "Ĭ",
|
||||
0x013b: "Ļ",
|
||||
0x013c: "ļ",
|
||||
0x013e: "ľ",
|
||||
0x013f: "Ŀ",
|
||||
0x0140: "ŀ",
|
||||
0x0141: "Ł",
|
||||
0x0150: "Ő",
|
||||
0x0151: "ő",
|
||||
0x0152: "Œ",
|
||||
0x0153: "œ",
|
||||
0x0154: "Ŕ",
|
||||
0x0155: "ŕ",
|
||||
0x015a: "Ś",
|
||||
0x0165: "ť",
|
||||
0x0166: "Ŧ",
|
||||
0x0167: "ŧ",
|
||||
0x0168: "Ũ",
|
||||
0x0169: "ũ",
|
||||
0x016a: "Ū",
|
||||
0x016b: "ū",
|
||||
0x017a: "ź",
|
||||
0x017b: "Ż",
|
||||
0x017d: "Ž",
|
||||
codeToUnicode2 = map[CharCode]rune{ // 40 entries
|
||||
0x0100: 'Ā',
|
||||
0x0101: 'ā',
|
||||
0x0102: 'Ă',
|
||||
0x0111: 'đ',
|
||||
0x0112: 'Ē',
|
||||
0x0113: 'ē',
|
||||
0x0114: 'Ĕ',
|
||||
0x0115: 'ĕ',
|
||||
0x0116: 'Ė',
|
||||
0x011b: 'ě',
|
||||
0x0126: 'Ħ',
|
||||
0x0127: 'ħ',
|
||||
0x0128: 'Ĩ',
|
||||
0x0129: 'ĩ',
|
||||
0x012a: 'Ī',
|
||||
0x012b: 'ī',
|
||||
0x012c: 'Ĭ',
|
||||
0x013b: 'Ļ',
|
||||
0x013c: 'ļ',
|
||||
0x013e: 'ľ',
|
||||
0x013f: 'Ŀ',
|
||||
0x0140: 'ŀ',
|
||||
0x0141: 'Ł',
|
||||
0x0150: 'Ő',
|
||||
0x0151: 'ő',
|
||||
0x0152: 'Œ',
|
||||
0x0153: 'œ',
|
||||
0x0154: 'Ŕ',
|
||||
0x0155: 'ŕ',
|
||||
0x015a: 'Ś',
|
||||
0x0165: 'ť',
|
||||
0x0166: 'Ŧ',
|
||||
0x0167: 'ŧ',
|
||||
0x0168: 'Ũ',
|
||||
0x0169: 'ũ',
|
||||
0x016a: 'Ū',
|
||||
0x016b: 'ū',
|
||||
0x017a: 'ź',
|
||||
0x017b: 'Ż',
|
||||
0x017d: 'Ž',
|
||||
}
|
||||
codeToUnicode3 = map[CharCode]string{ // 93 entries
|
||||
0x0124: "Ĥ",
|
||||
0x0125: "ĥ",
|
||||
0x0126: "Ħ",
|
||||
0x0127: "ħ",
|
||||
0x0134: "Ĵ",
|
||||
0x0135: "ĵ",
|
||||
0x0136: "Ķ",
|
||||
0x0137: "ķ",
|
||||
0x0138: "ĸ",
|
||||
0x0144: "ń",
|
||||
0x0145: "Ņ",
|
||||
0x0146: "ņ",
|
||||
0x0147: "Ň",
|
||||
0x0154: "Ŕ",
|
||||
0x0155: "ŕ",
|
||||
0x0156: "Ŗ",
|
||||
0x0157: "ŗ",
|
||||
0x0164: "Ť",
|
||||
0x0169: "ũ",
|
||||
0x0174: "Ŵ",
|
||||
0x0175: "ŵ",
|
||||
0x0176: "Ŷ",
|
||||
0x0177: "ŷ",
|
||||
0x0184: "Ƅ",
|
||||
0x0185: "ƅ",
|
||||
0x0186: "Ɔ",
|
||||
0x0187: "Ƈ",
|
||||
0x0194: "Ɣ",
|
||||
0x019a: "ƚ",
|
||||
0x01a4: "Ƥ",
|
||||
0x01a5: "ƥ",
|
||||
0x01a6: "Ʀ",
|
||||
0x01a7: "Ƨ",
|
||||
0x01b4: "ƴ",
|
||||
0x01b5: "Ƶ",
|
||||
0x01b6: "ƶ",
|
||||
0x01b7: "Ʒ",
|
||||
0x01c4: "DŽ",
|
||||
0x01cb: "Nj",
|
||||
0x01d4: "ǔ",
|
||||
0x01d5: "Ǖ",
|
||||
0x01d6: "ǖ",
|
||||
0x01d7: "Ǘ",
|
||||
0x01e4: "Ǥ",
|
||||
0x01e5: "ǥ",
|
||||
0x01e6: "Ǧ",
|
||||
0x01e7: "ǧ",
|
||||
0x01f4: "Ǵ",
|
||||
0x01f5: "ǵ",
|
||||
0x0204: "Ȅ",
|
||||
0x0205: "ȅ",
|
||||
0x0206: "Ȇ",
|
||||
0x0207: "ȇ",
|
||||
0x0214: "Ȕ",
|
||||
0x0215: "ȕ",
|
||||
0x0216: "Ȗ",
|
||||
0x0217: "ȗ",
|
||||
0x0224: "Ȥ",
|
||||
0x0226: "Ȧ",
|
||||
0x0227: "ȧ",
|
||||
0x0254: "ɔ",
|
||||
0x0255: "ɕ",
|
||||
0x0256: "ɖ",
|
||||
0x0257: "ɗ",
|
||||
0x0264: "ɤ",
|
||||
0x0265: "ɥ",
|
||||
0x0266: "ɦ",
|
||||
0x0267: "ɧ",
|
||||
0x0273: "ɳ",
|
||||
0x0274: "ɴ",
|
||||
0x0275: "ɵ",
|
||||
0x0276: "ɶ",
|
||||
0x0277: "ɷ",
|
||||
0x0284: "ʄ",
|
||||
0x0285: "ʅ",
|
||||
0x0286: "ʆ",
|
||||
0x0287: "ʇ",
|
||||
0x0294: "ʔ",
|
||||
0x0296: "ʖ",
|
||||
0x0297: "ʗ",
|
||||
0x02a4: "ʤ",
|
||||
0x02a5: "ʥ",
|
||||
0x02c6: "ˆ",
|
||||
0x02c7: "ˇ",
|
||||
0x0304: "̄",
|
||||
0x0305: "̅",
|
||||
0x0306: "̆",
|
||||
0x0307: "̇",
|
||||
0x030d: "̍",
|
||||
0x0314: "̔",
|
||||
0x0315: "̕",
|
||||
0x0316: "̖",
|
||||
0x0317: "̗",
|
||||
codeToUnicode3 = map[CharCode]rune{ // 93 entries
|
||||
0x0124: 'Ĥ',
|
||||
0x0125: 'ĥ',
|
||||
0x0126: 'Ħ',
|
||||
0x0127: 'ħ',
|
||||
0x0134: 'Ĵ',
|
||||
0x0135: 'ĵ',
|
||||
0x0136: 'Ķ',
|
||||
0x0137: 'ķ',
|
||||
0x0138: 'ĸ',
|
||||
0x0144: 'ń',
|
||||
0x0145: 'Ņ',
|
||||
0x0146: 'ņ',
|
||||
0x0147: 'Ň',
|
||||
0x0154: 'Ŕ',
|
||||
0x0155: 'ŕ',
|
||||
0x0156: 'Ŗ',
|
||||
0x0157: 'ŗ',
|
||||
0x0164: 'Ť',
|
||||
0x0169: 'ũ',
|
||||
0x0174: 'Ŵ',
|
||||
0x0175: 'ŵ',
|
||||
0x0176: 'Ŷ',
|
||||
0x0177: 'ŷ',
|
||||
0x0184: 'Ƅ',
|
||||
0x0185: 'ƅ',
|
||||
0x0186: 'Ɔ',
|
||||
0x0187: 'Ƈ',
|
||||
0x0194: 'Ɣ',
|
||||
0x019a: 'ƚ',
|
||||
0x01a4: 'Ƥ',
|
||||
0x01a5: 'ƥ',
|
||||
0x01a6: 'Ʀ',
|
||||
0x01a7: 'Ƨ',
|
||||
0x01b4: 'ƴ',
|
||||
0x01b5: 'Ƶ',
|
||||
0x01b6: 'ƶ',
|
||||
0x01b7: 'Ʒ',
|
||||
0x01c4: 'DŽ',
|
||||
0x01cb: 'Nj',
|
||||
0x01d4: 'ǔ',
|
||||
0x01d5: 'Ǖ',
|
||||
0x01d6: 'ǖ',
|
||||
0x01d7: 'Ǘ',
|
||||
0x01e4: 'Ǥ',
|
||||
0x01e5: 'ǥ',
|
||||
0x01e6: 'Ǧ',
|
||||
0x01e7: 'ǧ',
|
||||
0x01f4: 'Ǵ',
|
||||
0x01f5: 'ǵ',
|
||||
0x0204: 'Ȅ',
|
||||
0x0205: 'ȅ',
|
||||
0x0206: 'Ȇ',
|
||||
0x0207: 'ȇ',
|
||||
0x0214: 'Ȕ',
|
||||
0x0215: 'ȕ',
|
||||
0x0216: 'Ȗ',
|
||||
0x0217: 'ȗ',
|
||||
0x0224: 'Ȥ',
|
||||
0x0226: 'Ȧ',
|
||||
0x0227: 'ȧ',
|
||||
0x0254: 'ɔ',
|
||||
0x0255: 'ɕ',
|
||||
0x0256: 'ɖ',
|
||||
0x0257: 'ɗ',
|
||||
0x0264: 'ɤ',
|
||||
0x0265: 'ɥ',
|
||||
0x0266: 'ɦ',
|
||||
0x0267: 'ɧ',
|
||||
0x0273: 'ɳ',
|
||||
0x0274: 'ɴ',
|
||||
0x0275: 'ɵ',
|
||||
0x0276: 'ɶ',
|
||||
0x0277: 'ɷ',
|
||||
0x0284: 'ʄ',
|
||||
0x0285: 'ʅ',
|
||||
0x0286: 'ʆ',
|
||||
0x0287: 'ʇ',
|
||||
0x0294: 'ʔ',
|
||||
0x0296: 'ʖ',
|
||||
0x0297: 'ʗ',
|
||||
0x02a4: 'ʤ',
|
||||
0x02a5: 'ʥ',
|
||||
0x02c6: 'ˆ',
|
||||
0x02c7: 'ˇ',
|
||||
0x0304: '̄',
|
||||
0x0305: '̅',
|
||||
0x0306: '̆',
|
||||
0x0307: '̇',
|
||||
0x030d: '̍',
|
||||
0x0314: '̔',
|
||||
0x0315: '̕',
|
||||
0x0316: '̖',
|
||||
0x0317: '̗',
|
||||
}
|
||||
)
|
||||
|
||||
@ -662,7 +662,7 @@ func TestCMapCreation(t *testing.T) {
|
||||
|
||||
// checkCmapWriteRead creates CMap data from `codeToUnicode` then parses it and checks that the
|
||||
// same codeToUnicode is returned.
|
||||
func checkCmapWriteRead(t *testing.T, codeToUnicode map[CharCode]string) {
|
||||
func checkCmapWriteRead(t *testing.T, codeToUnicode map[CharCode]rune) {
|
||||
cmap0 := NewToUnicodeCMap(codeToUnicode)
|
||||
|
||||
data := cmap0.Bytes()
|
||||
|
@ -21,16 +21,11 @@ func hexToCharCode(shex cmapHexString) CharCode {
|
||||
return code
|
||||
}
|
||||
|
||||
// hexToString returns the unicode string that is UTF-16BE encoded in `shex`.
|
||||
// hexToString decodes the UTF-16BE encoded string `shex` to unicode runes.
|
||||
// 9.10.3 ToUnicode CMaps (page 293)
|
||||
// • It shall use the beginbfchar, endbfchar, beginbfrange, and endbfrange operators to define the
|
||||
// mapping from character codes to Unicode character sequences expressed in UTF-16BE encoding.
|
||||
func hexToString(shex cmapHexString) string {
|
||||
return string(utf16ToRunes(shex))
|
||||
}
|
||||
|
||||
// hexToString decodes the UTF-16BE encoded string `shex` to unicode runes.
|
||||
func utf16ToRunes(shex cmapHexString) []rune {
|
||||
func hexToRunes(shex cmapHexString) []rune {
|
||||
if len(shex.b) == 1 {
|
||||
return []rune{rune(shex.b[0])}
|
||||
}
|
||||
@ -47,3 +42,16 @@ func utf16ToRunes(shex cmapHexString) []rune {
|
||||
runes := utf16.Decode(chars)
|
||||
return runes
|
||||
}
|
||||
|
||||
// hexToRune is the same as hexToRunes but expects only a single rune to be decoded.
|
||||
func hexToRune(shex cmapHexString) rune {
|
||||
runes := hexToRunes(shex)
|
||||
if n := len(runes); n == 0 {
|
||||
common.Log.Debug("ERROR: hexToRune. Expected at least one rune shex=%#v", shex)
|
||||
return MissingCodeRune
|
||||
}
|
||||
if len(runes) > 1 {
|
||||
common.Log.Debug("ERROR: hexToRune. Expected exactly one rune shex=%#v -> %#v", shex, runes)
|
||||
}
|
||||
return runes[0]
|
||||
}
|
||||
|
@ -12,6 +12,12 @@ import (
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
)
|
||||
|
||||
// CharCode is a character code used in the specific encoding.
|
||||
type CharCode uint16
|
||||
|
||||
// GlyphName is a name of a glyph.
|
||||
type GlyphName string
|
||||
|
||||
// TextEncoder defines the common methods that a text encoder implementation must have in UniDoc.
|
||||
type TextEncoder interface {
|
||||
// String returns a string that describes the TextEncoder instance.
|
||||
@ -22,29 +28,29 @@ type TextEncoder interface {
|
||||
|
||||
// CharcodeToGlyph returns the glyph name for character code `code`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
CharcodeToGlyph(code uint16) (string, bool)
|
||||
CharcodeToGlyph(code CharCode) (GlyphName, bool)
|
||||
|
||||
// GlyphToCharcode returns the PDF character code corresponding to glyph name `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
GlyphToCharcode(glyph string) (uint16, bool)
|
||||
GlyphToCharcode(glyph GlyphName) (CharCode, bool)
|
||||
|
||||
// RuneToCharcode returns the PDF character code corresponding to rune `r`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
// This is usually implemented as RuneToGlyph->GlyphToCharcode
|
||||
RuneToCharcode(r rune) (uint16, bool)
|
||||
RuneToCharcode(r rune) (CharCode, bool)
|
||||
|
||||
// CharcodeToRune returns the rune corresponding to character code `code`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
// This is usually implemented as CharcodeToGlyph->GlyphToRune
|
||||
CharcodeToRune(code uint16) (rune, bool)
|
||||
CharcodeToRune(code CharCode) (rune, bool)
|
||||
|
||||
// RuneToGlyph returns the glyph name for rune `r`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
RuneToGlyph(r rune) (string, bool)
|
||||
RuneToGlyph(r rune) (GlyphName, bool)
|
||||
|
||||
// GlyphToRune returns the rune corresponding to glyph name `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
GlyphToRune(glyph string) (rune, bool)
|
||||
GlyphToRune(glyph GlyphName) (rune, bool)
|
||||
|
||||
// ToPdfObject returns a PDF Object that represents the encoding.
|
||||
ToPdfObject() core.PdfObject
|
||||
@ -82,7 +88,7 @@ func encodeString16bit(enc TextEncoder, raw string) []byte {
|
||||
|
||||
// Each entry represented by 2 bytes.
|
||||
var v [2]byte
|
||||
binary.BigEndian.PutUint16(v[:], code)
|
||||
binary.BigEndian.PutUint16(v[:], uint16(code))
|
||||
encoded = append(encoded, v[:]...)
|
||||
}
|
||||
return encoded
|
||||
@ -90,7 +96,7 @@ func encodeString16bit(enc TextEncoder, raw string) []byte {
|
||||
|
||||
// doRuneToCharcode converts rune `r` to a PDF character code.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func doRuneToCharcode(enc TextEncoder, r rune) (uint16, bool) {
|
||||
func doRuneToCharcode(enc TextEncoder, r rune) (CharCode, bool) {
|
||||
g, ok := enc.RuneToGlyph(r)
|
||||
if !ok {
|
||||
return 0, false
|
||||
@ -100,7 +106,7 @@ func doRuneToCharcode(enc TextEncoder, r rune) (uint16, bool) {
|
||||
|
||||
// doCharcodeToRune converts PDF character code `code` to a rune.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func doCharcodeToRune(enc TextEncoder, code uint16) (rune, bool) {
|
||||
func doCharcodeToRune(enc TextEncoder, code CharCode) (rune, bool) {
|
||||
g, ok := enc.CharcodeToGlyph(code)
|
||||
if !ok {
|
||||
return 0, false
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,20 +36,20 @@ func (enc IdentityEncoder) Encode(raw string) []byte {
|
||||
|
||||
// CharcodeToGlyph returns the glyph name matching character code `code`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc IdentityEncoder) CharcodeToGlyph(code uint16) (string, bool) {
|
||||
func (enc IdentityEncoder) CharcodeToGlyph(code CharCode) (GlyphName, bool) {
|
||||
r, found := enc.CharcodeToRune(code)
|
||||
if found && r == 0x20 {
|
||||
return "space", true
|
||||
}
|
||||
|
||||
// Returns "uniXXXX" format where XXXX is the code in hex format.
|
||||
glyph := fmt.Sprintf("uni%.4X", code)
|
||||
glyph := GlyphName(fmt.Sprintf("uni%.4X", code))
|
||||
return glyph, true
|
||||
}
|
||||
|
||||
// GlyphToCharcode returns the character code matching glyph `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc IdentityEncoder) GlyphToCharcode(glyph string) (uint16, bool) {
|
||||
func (enc IdentityEncoder) GlyphToCharcode(glyph GlyphName) (CharCode, bool) {
|
||||
r, ok := enc.GlyphToRune(glyph)
|
||||
if !ok {
|
||||
return 0, false
|
||||
@ -59,36 +59,36 @@ func (enc IdentityEncoder) GlyphToCharcode(glyph string) (uint16, bool) {
|
||||
|
||||
// RuneToCharcode converts rune `r` to a PDF character code.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc IdentityEncoder) RuneToCharcode(r rune) (uint16, bool) {
|
||||
return uint16(r), true
|
||||
func (enc IdentityEncoder) RuneToCharcode(r rune) (CharCode, bool) {
|
||||
return CharCode(r), true
|
||||
}
|
||||
|
||||
// CharcodeToRune converts PDF character code `code` to a rune.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc IdentityEncoder) CharcodeToRune(code uint16) (rune, bool) {
|
||||
func (enc IdentityEncoder) CharcodeToRune(code CharCode) (rune, bool) {
|
||||
return rune(code), true
|
||||
}
|
||||
|
||||
// RuneToGlyph returns the glyph name for rune `r`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc IdentityEncoder) RuneToGlyph(r rune) (string, bool) {
|
||||
func (enc IdentityEncoder) RuneToGlyph(r rune) (GlyphName, bool) {
|
||||
if r == ' ' {
|
||||
return "space", true
|
||||
}
|
||||
glyph := fmt.Sprintf("uni%.4X", r)
|
||||
glyph := GlyphName(fmt.Sprintf("uni%.4X", r))
|
||||
return glyph, true
|
||||
}
|
||||
|
||||
// GlyphToRune returns the rune corresponding to glyph name `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc IdentityEncoder) GlyphToRune(glyph string) (rune, bool) {
|
||||
func (enc IdentityEncoder) GlyphToRune(glyph GlyphName) (rune, bool) {
|
||||
// String with "uniXXXX" format where XXXX is the hexcode.
|
||||
if glyph == "space" {
|
||||
return ' ', true
|
||||
} else if !strings.HasPrefix(glyph, "uni") || len(glyph) != 7 {
|
||||
} else if !strings.HasPrefix(string(glyph), "uni") || len(glyph) != 7 {
|
||||
return 0, false
|
||||
}
|
||||
r, err := strconv.ParseUint(glyph[3:], 16, 16)
|
||||
r, err := strconv.ParseUint(string(glyph[3:]), 16, 16)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
@ -25,20 +25,22 @@ import (
|
||||
|
||||
// SimpleEncoder represents a 1 byte encoding
|
||||
type SimpleEncoder struct {
|
||||
baseName string
|
||||
baseEncoding map[uint16]rune
|
||||
differences map[byte]string
|
||||
CodeToGlyph map[uint16]string
|
||||
glyphToCode map[string]uint16
|
||||
codeToRune map[uint16]rune
|
||||
baseName string
|
||||
|
||||
baseEncoding map[CharCode]rune
|
||||
differences map[CharCode]GlyphName
|
||||
|
||||
codeToGlyph map[CharCode]GlyphName
|
||||
glyphToCode map[GlyphName]CharCode
|
||||
codeToRune map[CharCode]rune
|
||||
}
|
||||
|
||||
// NewCustomSimpleTextEncoder returns a SimpleEncoder based on map `encoding` and difference map
|
||||
// `differences`.
|
||||
func NewCustomSimpleTextEncoder(encoding map[uint16]string, differences map[byte]string) (
|
||||
func NewCustomSimpleTextEncoder(encoding, differences map[CharCode]GlyphName) (
|
||||
*SimpleEncoder, error) {
|
||||
baseName := "custom"
|
||||
baseEncoding := map[uint16]rune{}
|
||||
baseEncoding := make(map[CharCode]rune)
|
||||
if len(encoding) == 0 {
|
||||
return &SimpleEncoder{}, errors.New("Empty custom encoding")
|
||||
}
|
||||
@ -54,14 +56,14 @@ func NewCustomSimpleTextEncoder(encoding map[uint16]string, differences map[byte
|
||||
}
|
||||
|
||||
// ApplyDifferences applies the encoding delta `differences` to `se`.
|
||||
func (se *SimpleEncoder) ApplyDifferences(differences map[byte]string) {
|
||||
func (se *SimpleEncoder) ApplyDifferences(differences map[CharCode]GlyphName) {
|
||||
se.differences = differences
|
||||
se.computeTables()
|
||||
}
|
||||
|
||||
// NewSimpleTextEncoder returns a SimpleEncoder based on predefined encoding `baseName` and
|
||||
// difference map `differences`.
|
||||
func NewSimpleTextEncoder(baseName string, differences map[byte]string) (*SimpleEncoder, error) {
|
||||
func NewSimpleTextEncoder(baseName string, differences map[CharCode]GlyphName) (*SimpleEncoder, error) {
|
||||
baseEncoding, ok := simpleEncodings[baseName]
|
||||
if !ok {
|
||||
common.Log.Debug("ERROR: NewSimpleTextEncoder. Unknown encoding %q", baseName)
|
||||
@ -72,8 +74,8 @@ func NewSimpleTextEncoder(baseName string, differences map[byte]string) (*Simple
|
||||
|
||||
// newSimpleTextEncoder returns a SimpleEncoder based on map `encoding` and difference map
|
||||
// `differences`.
|
||||
func newSimpleTextEncoder(baseEncoding map[uint16]rune, baseName string,
|
||||
differences map[byte]string) (*SimpleEncoder, error) {
|
||||
func newSimpleTextEncoder(baseEncoding map[CharCode]rune, baseName string,
|
||||
differences map[CharCode]GlyphName) (*SimpleEncoder, error) {
|
||||
|
||||
se := SimpleEncoder{
|
||||
baseName: baseName,
|
||||
@ -94,15 +96,17 @@ func (se SimpleEncoder) String() string {
|
||||
name = fmt.Sprintf("%s(diff)", se.baseName)
|
||||
}
|
||||
parts := []string{
|
||||
fmt.Sprintf("%#q %d entries %d differences", name, len(se.CodeToGlyph), len(se.differences)),
|
||||
fmt.Sprintf("%#q %d entries %d differences", name, len(se.codeToGlyph), len(se.differences)),
|
||||
fmt.Sprintf("differences=%+v", se.differences),
|
||||
}
|
||||
|
||||
codes := []int{}
|
||||
for c := range se.CodeToGlyph {
|
||||
codes = append(codes, int(c))
|
||||
codes := make([]CharCode, 0, len(se.codeToGlyph))
|
||||
for c := range se.codeToGlyph {
|
||||
codes = append(codes, c)
|
||||
}
|
||||
sort.Ints(codes)
|
||||
sort.Slice(codes, func(i, j int) bool {
|
||||
return codes[i] < codes[j]
|
||||
})
|
||||
numCodes := len(codes)
|
||||
if numCodes > simpleEncoderNumEntries {
|
||||
numCodes = simpleEncoderNumEntries
|
||||
@ -110,7 +114,7 @@ func (se SimpleEncoder) String() string {
|
||||
|
||||
for i := 0; i < numCodes; i++ {
|
||||
c := codes[i]
|
||||
parts = append(parts, fmt.Sprintf("%d=0x%02x: %q", c, c, se.CodeToGlyph[uint16(c)]))
|
||||
parts = append(parts, fmt.Sprintf("%d=0x%02x: %q", c, c, se.codeToGlyph[c]))
|
||||
}
|
||||
return fmt.Sprintf("SIMPLE_ENCODER{%s}", strings.Join(parts, ", "))
|
||||
}
|
||||
@ -122,8 +126,8 @@ func (se SimpleEncoder) Encode(raw string) []byte {
|
||||
|
||||
// CharcodeToGlyph returns the glyph name for character code `code`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (se SimpleEncoder) CharcodeToGlyph(code uint16) (string, bool) {
|
||||
glyph, ok := se.CodeToGlyph[code]
|
||||
func (se SimpleEncoder) CharcodeToGlyph(code CharCode) (GlyphName, bool) {
|
||||
glyph, ok := se.codeToGlyph[code]
|
||||
if !ok {
|
||||
common.Log.Debug("Charcode -> Glyph error: charcode not found: 0x%04x", code)
|
||||
}
|
||||
@ -132,7 +136,7 @@ func (se SimpleEncoder) CharcodeToGlyph(code uint16) (string, bool) {
|
||||
|
||||
// GlyphToCharcode returns character code for glyph `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (se SimpleEncoder) GlyphToCharcode(glyph string) (uint16, bool) {
|
||||
func (se SimpleEncoder) GlyphToCharcode(glyph GlyphName) (CharCode, bool) {
|
||||
code, ok := se.glyphToCode[glyph]
|
||||
if !ok {
|
||||
common.Log.Debug("Glyph -> Charcode error: glyph not found: %q %s", glyph, se)
|
||||
@ -142,13 +146,13 @@ func (se SimpleEncoder) GlyphToCharcode(glyph string) (uint16, bool) {
|
||||
|
||||
// RuneToCharcode returns the PDF character code corresponding to rune `r`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (se SimpleEncoder) RuneToCharcode(val rune) (uint16, bool) {
|
||||
func (se SimpleEncoder) RuneToCharcode(val rune) (CharCode, bool) {
|
||||
return doRuneToCharcode(se, val)
|
||||
}
|
||||
|
||||
// CharcodeToRune returns the rune corresponding to character code `code`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (se SimpleEncoder) CharcodeToRune(code uint16) (rune, bool) {
|
||||
func (se SimpleEncoder) CharcodeToRune(code CharCode) (rune, bool) {
|
||||
r, ok := se.codeToRune[code]
|
||||
if !ok {
|
||||
common.Log.Debug("Charcode -> Rune error: charcode not found: 0x%04x", code)
|
||||
@ -158,13 +162,13 @@ func (se SimpleEncoder) CharcodeToRune(code uint16) (rune, bool) {
|
||||
|
||||
// RuneToGlyph returns the glyph corresponding to rune `r`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (se SimpleEncoder) RuneToGlyph(r rune) (string, bool) {
|
||||
func (se SimpleEncoder) RuneToGlyph(r rune) (GlyphName, bool) {
|
||||
return runeToGlyph(r, glyphlistRuneToGlyphMap)
|
||||
}
|
||||
|
||||
// GlyphToRune returns the rune corresponding to glyph `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (se SimpleEncoder) GlyphToRune(glyph string) (rune, bool) {
|
||||
func (se SimpleEncoder) GlyphToRune(glyph GlyphName) (rune, bool) {
|
||||
return glyphToRune(glyph, glyphlistGlyphToRuneMap)
|
||||
}
|
||||
|
||||
@ -187,46 +191,44 @@ func (se SimpleEncoder) ToPdfObject() core.PdfObject {
|
||||
// computeTables computes the tables needed for a working SimpleEncoder from the member
|
||||
// fields `baseEncoding` and `differences`.
|
||||
func (se *SimpleEncoder) computeTables() {
|
||||
codeToRune := map[uint16]rune{}
|
||||
codeToRune := make(map[CharCode]rune)
|
||||
for code, r := range se.baseEncoding {
|
||||
codeToRune[code] = r
|
||||
}
|
||||
if se.differences != nil {
|
||||
for code, glyph := range se.differences {
|
||||
r, ok := GlyphToRune(glyph)
|
||||
if !ok {
|
||||
common.Log.Debug("ERROR: No match for glyph=%q differences=%+v", glyph,
|
||||
se.differences)
|
||||
}
|
||||
codeToRune[uint16(code)] = r
|
||||
for code, glyph := range se.differences {
|
||||
r, ok := GlyphToRune(glyph)
|
||||
if !ok {
|
||||
common.Log.Debug("ERROR: No match for glyph=%q differences=%+v", glyph,
|
||||
se.differences)
|
||||
}
|
||||
codeToRune[code] = r
|
||||
}
|
||||
|
||||
codeToGlyph := map[uint16]string{}
|
||||
glyphToCode := map[string]uint16{}
|
||||
codeToGlyph := make(map[CharCode]GlyphName)
|
||||
glyphToCode := make(map[GlyphName]CharCode)
|
||||
for code, r := range codeToRune {
|
||||
if glyph, ok := RuneToGlyph(r); ok {
|
||||
codeToGlyph[code] = glyph
|
||||
glyphToCode[glyph] = code
|
||||
}
|
||||
}
|
||||
se.CodeToGlyph = codeToGlyph
|
||||
se.codeToGlyph = codeToGlyph
|
||||
se.glyphToCode = glyphToCode
|
||||
se.codeToRune = codeToRune
|
||||
}
|
||||
|
||||
// FromFontDifferences converts `diffList` (a /Differences array from an /Encoding object) to a map
|
||||
// representing character code to glyph mappings.
|
||||
func FromFontDifferences(diffList *core.PdfObjectArray) (map[byte]string, error) {
|
||||
differences := map[byte]string{}
|
||||
var n byte
|
||||
func FromFontDifferences(diffList *core.PdfObjectArray) (map[CharCode]GlyphName, error) {
|
||||
differences := make(map[CharCode]GlyphName)
|
||||
var n CharCode
|
||||
for _, obj := range diffList.Elements() {
|
||||
switch v := obj.(type) {
|
||||
case *core.PdfObjectInteger:
|
||||
n = byte(*v)
|
||||
n = CharCode(*v)
|
||||
case *core.PdfObjectName:
|
||||
s := string(*v)
|
||||
differences[n] = s
|
||||
differences[n] = GlyphName(s)
|
||||
n++
|
||||
default:
|
||||
common.Log.Debug("ERROR: Bad type. obj=%s", obj)
|
||||
@ -238,12 +240,12 @@ func FromFontDifferences(diffList *core.PdfObjectArray) (map[byte]string, error)
|
||||
|
||||
// ToFontDifferences converts `differences` (a map representing character code to glyph mappings)
|
||||
// to a /Differences array for an /Encoding object.
|
||||
func ToFontDifferences(differences map[byte]string) *core.PdfObjectArray {
|
||||
func ToFontDifferences(differences map[CharCode]GlyphName) *core.PdfObjectArray {
|
||||
if len(differences) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
codes := []byte{}
|
||||
codes := make([]CharCode, 0, len(differences))
|
||||
for c := range differences {
|
||||
codes = append(codes, c)
|
||||
}
|
||||
@ -252,10 +254,10 @@ func ToFontDifferences(differences map[byte]string) *core.PdfObjectArray {
|
||||
})
|
||||
|
||||
n := codes[0]
|
||||
diffList := []core.PdfObject{core.MakeInteger(int64(n)), core.MakeName(differences[n])}
|
||||
diffList := []core.PdfObject{core.MakeInteger(int64(n)), core.MakeName(string(differences[n]))}
|
||||
for _, c := range codes[1:] {
|
||||
if c == n+1 {
|
||||
diffList = append(diffList, core.MakeName(differences[c]))
|
||||
diffList = append(diffList, core.MakeName(string(differences[c])))
|
||||
} else {
|
||||
diffList = append(diffList, core.MakeInteger(int64(c)))
|
||||
}
|
||||
@ -265,7 +267,7 @@ func ToFontDifferences(differences map[byte]string) *core.PdfObjectArray {
|
||||
}
|
||||
|
||||
// simpleEncodings is a map of the standard 8 bit character encodings.
|
||||
var simpleEncodings = map[string]map[uint16]rune{
|
||||
var simpleEncodings = map[string]map[CharCode]rune{
|
||||
"MacExpertEncoding": { // 165 entries
|
||||
0x20: 0x0020, // "space"
|
||||
0x21: 0xf721, // "exclamsmall"
|
||||
|
@ -26,19 +26,19 @@ func TestBasicEncodings(t *testing.T) {
|
||||
}
|
||||
|
||||
var testCases = []encodingTest{
|
||||
encodingTest{"MacExpertEncoding", "₂₃₄₅", []string{"twoinferior", "threeinferior", "fourinferior", "fiveinferior"}},
|
||||
encodingTest{"MacRomanEncoding", "◊fl˝ˇ", []string{"lozenge", "fl", "hungarumlaut", "caron"}},
|
||||
encodingTest{"PdfDocEncoding", "¾Ðí©", []string{"threequarters", "Eth", "iacute", "copyright"}},
|
||||
encodingTest{"StandardEncoding", "ºªı„", []string{"ordmasculine", "ordfeminine", "dotlessi", "quotedblbase"}},
|
||||
encodingTest{"SymbolEncoding", "δ∂ℵ⌡", []string{"delta", "partialdiff", "aleph", "integralbt"}},
|
||||
encodingTest{"WinAnsiEncoding", "×÷®Ï", []string{"multiply", "divide", "registered", "Idieresis"}},
|
||||
encodingTest{"ZapfDingbatsEncoding", "☎①➔➨", []string{"a4", "a120", "a160", "a178"}},
|
||||
{"MacExpertEncoding", "₂₃₄₅", []GlyphName{"twoinferior", "threeinferior", "fourinferior", "fiveinferior"}},
|
||||
{"MacRomanEncoding", "◊fl˝ˇ", []GlyphName{"lozenge", "fl", "hungarumlaut", "caron"}},
|
||||
{"PdfDocEncoding", "¾Ðí©", []GlyphName{"threequarters", "Eth", "iacute", "copyright"}},
|
||||
{"StandardEncoding", "ºªı„", []GlyphName{"ordmasculine", "ordfeminine", "dotlessi", "quotedblbase"}},
|
||||
{"SymbolEncoding", "δ∂ℵ⌡", []GlyphName{"delta", "partialdiff", "aleph", "integralbt"}},
|
||||
{"WinAnsiEncoding", "×÷®Ï", []GlyphName{"multiply", "divide", "registered", "Idieresis"}},
|
||||
{"ZapfDingbatsEncoding", "☎①➔➨", []GlyphName{"a4", "a120", "a160", "a178"}},
|
||||
}
|
||||
|
||||
type encodingTest struct {
|
||||
encoding string
|
||||
runes string
|
||||
glyphs []string
|
||||
glyphs []GlyphName
|
||||
}
|
||||
|
||||
func (f *encodingTest) String() string {
|
||||
|
@ -14,23 +14,26 @@ import (
|
||||
"github.com/unidoc/unidoc/pdf/core"
|
||||
)
|
||||
|
||||
// GID is a glyph index.
|
||||
type GID uint16
|
||||
|
||||
// TrueTypeFontEncoder handles text encoding for composite TrueType fonts.
|
||||
// It performs mapping between character ids and glyph ids.
|
||||
// It has a preloaded rune (unicode code point) to glyph index map that has been loaded from a font.
|
||||
// Corresponds to Identity-H.
|
||||
type TrueTypeFontEncoder struct {
|
||||
runeToGlyphIndexMap map[uint16]uint16
|
||||
cmap CMap
|
||||
runeToGIDMap map[rune]GID
|
||||
cmap CMap
|
||||
}
|
||||
|
||||
// NewTrueTypeFontEncoder creates a new text encoder for TTF fonts with a pre-loaded
|
||||
// runeToGlyphIndexMap, that has been pre-loaded from the font file.
|
||||
// runeToGIDMap, that has been pre-loaded from the font file.
|
||||
// The new instance is preloaded with a CMapIdentityH (Identity-H) CMap which maps 2-byte charcodes
|
||||
// to CIDs (glyph index).
|
||||
func NewTrueTypeFontEncoder(runeToGlyphIndexMap map[uint16]uint16) TrueTypeFontEncoder {
|
||||
func NewTrueTypeFontEncoder(runeToGIDMap map[rune]GID) TrueTypeFontEncoder {
|
||||
return TrueTypeFontEncoder{
|
||||
runeToGlyphIndexMap: runeToGlyphIndexMap,
|
||||
cmap: CMapIdentityH{},
|
||||
runeToGIDMap: runeToGIDMap,
|
||||
cmap: CMapIdentityH{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,23 +43,25 @@ const ttEncoderMaxNumEntries = 10
|
||||
// String returns a string that describes `enc`.
|
||||
func (enc TrueTypeFontEncoder) String() string {
|
||||
parts := []string{
|
||||
fmt.Sprintf("%d entries", len(enc.runeToGlyphIndexMap)),
|
||||
fmt.Sprintf("%d entries", len(enc.runeToGIDMap)),
|
||||
}
|
||||
|
||||
codes := []int{}
|
||||
for c := range enc.runeToGlyphIndexMap {
|
||||
codes = append(codes, int(c))
|
||||
runes := make([]rune, 0, len(enc.runeToGIDMap))
|
||||
for r := range enc.runeToGIDMap {
|
||||
runes = append(runes, r)
|
||||
}
|
||||
sort.Ints(codes)
|
||||
numCodes := len(codes)
|
||||
if numCodes > ttEncoderMaxNumEntries {
|
||||
numCodes = ttEncoderMaxNumEntries
|
||||
sort.Slice(runes, func(i, j int) bool {
|
||||
return runes[i] < runes[j]
|
||||
})
|
||||
n := len(runes)
|
||||
if n > ttEncoderMaxNumEntries {
|
||||
n = ttEncoderMaxNumEntries
|
||||
}
|
||||
|
||||
for i := 0; i < numCodes; i++ {
|
||||
c := codes[i]
|
||||
for i := 0; i < n; i++ {
|
||||
r := runes[i]
|
||||
parts = append(parts, fmt.Sprintf("%d=0x%02x: %q",
|
||||
c, c, enc.runeToGlyphIndexMap[uint16(c)]))
|
||||
r, r, enc.runeToGIDMap[r]))
|
||||
}
|
||||
return fmt.Sprintf("TRUETYPE_ENCODER{%s}", strings.Join(parts, ", "))
|
||||
}
|
||||
@ -68,24 +73,24 @@ func (enc TrueTypeFontEncoder) Encode(raw string) []byte {
|
||||
|
||||
// CharcodeToGlyph returns the glyph name matching character code `code`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc TrueTypeFontEncoder) CharcodeToGlyph(code uint16) (string, bool) {
|
||||
func (enc TrueTypeFontEncoder) CharcodeToGlyph(code CharCode) (GlyphName, bool) {
|
||||
r, found := enc.CharcodeToRune(code)
|
||||
if found && r == 0x20 {
|
||||
return "space", true
|
||||
}
|
||||
|
||||
// Returns "uniXXXX" format where XXXX is the code in hex format.
|
||||
glyph := fmt.Sprintf("uni%.4X", code)
|
||||
glyph := GlyphName(fmt.Sprintf("uni%.4X", code))
|
||||
return glyph, true
|
||||
}
|
||||
|
||||
// GlyphToCharcode returns character code matching the glyph name `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc TrueTypeFontEncoder) GlyphToCharcode(glyph string) (uint16, bool) {
|
||||
func (enc TrueTypeFontEncoder) GlyphToCharcode(glyph GlyphName) (CharCode, bool) {
|
||||
// String with "uniXXXX" format where XXXX is the hexcode.
|
||||
if len(glyph) == 7 && glyph[0:3] == "uni" {
|
||||
var unicode uint16
|
||||
n, err := fmt.Sscanf(glyph, "uni%X", &unicode)
|
||||
n, err := fmt.Sscanf(string(glyph), "uni%X", &unicode)
|
||||
if n == 1 && err == nil {
|
||||
return enc.RuneToCharcode(rune(unicode))
|
||||
}
|
||||
@ -102,25 +107,29 @@ func (enc TrueTypeFontEncoder) GlyphToCharcode(glyph string) (uint16, bool) {
|
||||
|
||||
// RuneToCharcode converts rune `r` to a PDF character code.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc TrueTypeFontEncoder) RuneToCharcode(r rune) (uint16, bool) {
|
||||
glyphIndex, ok := enc.runeToGlyphIndexMap[uint16(r)]
|
||||
func (enc TrueTypeFontEncoder) RuneToCharcode(r rune) (CharCode, bool) {
|
||||
glyphIndex, ok := enc.runeToGIDMap[r]
|
||||
if !ok {
|
||||
common.Log.Debug("Missing rune %d (%+q) from encoding", r, r)
|
||||
return 0, false
|
||||
}
|
||||
// Identity : charcode <-> glyphIndex
|
||||
charcode := glyphIndex
|
||||
// TODO(dennwc): Here charcode is probably the same as CID.
|
||||
// TODO(dennwc): Find out what are the alternative mappings (enc.cmap?).
|
||||
charcode := CharCode(glyphIndex)
|
||||
|
||||
return uint16(charcode), true
|
||||
return charcode, true
|
||||
}
|
||||
|
||||
// CharcodeToRune converts PDF character code `code` to a rune.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc TrueTypeFontEncoder) CharcodeToRune(code uint16) (rune, bool) {
|
||||
func (enc TrueTypeFontEncoder) CharcodeToRune(code CharCode) (rune, bool) {
|
||||
// TODO: Make a reverse map stored.
|
||||
for code, glyphIndex := range enc.runeToGlyphIndexMap {
|
||||
if glyphIndex == code {
|
||||
return rune(code), true
|
||||
for r, gid := range enc.runeToGIDMap {
|
||||
// Identity : glyphIndex <-> charcode
|
||||
charcode := CharCode(gid)
|
||||
if charcode == code {
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
common.Log.Debug("CharcodeToRune: No match. code=0x%04x enc=%s", code, enc)
|
||||
@ -129,21 +138,21 @@ func (enc TrueTypeFontEncoder) CharcodeToRune(code uint16) (rune, bool) {
|
||||
|
||||
// RuneToGlyph returns the glyph name for rune `r`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc TrueTypeFontEncoder) RuneToGlyph(r rune) (string, bool) {
|
||||
func (enc TrueTypeFontEncoder) RuneToGlyph(r rune) (GlyphName, bool) {
|
||||
if r == 0x20 {
|
||||
return "space", true
|
||||
}
|
||||
glyph := fmt.Sprintf("uni%.4X", r)
|
||||
glyph := GlyphName(fmt.Sprintf("uni%.4X", r))
|
||||
return glyph, true
|
||||
}
|
||||
|
||||
// GlyphToRune returns the rune corresponding to glyph name `glyph`.
|
||||
// The bool return flag is true if there was a match, and false otherwise.
|
||||
func (enc TrueTypeFontEncoder) GlyphToRune(glyph string) (rune, bool) {
|
||||
func (enc TrueTypeFontEncoder) GlyphToRune(glyph GlyphName) (rune, bool) {
|
||||
// String with "uniXXXX" format where XXXX is the hexcode.
|
||||
if len(glyph) == 7 && glyph[0:3] == "uni" {
|
||||
unicode := uint16(0)
|
||||
n, err := fmt.Sscanf(glyph, "uni%X", &unicode)
|
||||
n, err := fmt.Sscanf(string(glyph), "uni%X", &unicode)
|
||||
if n == 1 && err == nil {
|
||||
return rune(unicode), true
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"github.com/unidoc/unidoc/common"
|
||||
)
|
||||
|
||||
func glyphToRune(glyph string, glyphToRuneMap map[string]rune) (rune, bool) {
|
||||
func glyphToRune(glyph GlyphName, glyphToRuneMap map[GlyphName]rune) (rune, bool) {
|
||||
r, ok := glyphToRuneMap[glyph]
|
||||
if ok {
|
||||
return r, true
|
||||
@ -22,7 +22,7 @@ func glyphToRune(glyph string, glyphToRuneMap map[string]rune) (rune, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func runeToGlyph(r rune, runeToGlyphMap map[rune]string) (string, bool) {
|
||||
func runeToGlyph(r rune, runeToGlyphMap map[rune]GlyphName) (GlyphName, bool) {
|
||||
glyph, ok := runeToGlyphMap[r]
|
||||
if ok {
|
||||
return glyph, true
|
||||
|
@ -126,28 +126,27 @@ func NewStandard14FontWithEncoding(basefont Standard14Font, alphabet map[rune]in
|
||||
}
|
||||
|
||||
// glyphCode are the encoding glyphs. We need to match them to the font glyphs.
|
||||
glyphCode := map[string]byte{}
|
||||
glyphCode := make(map[textencoding.GlyphName]textencoding.CharCode)
|
||||
|
||||
// slots are the indexes in the encoding where the new character codes are added.
|
||||
// slots are unused indexes, which are filled first. slots1 are the used indexes.
|
||||
slots := []byte{}
|
||||
slots1 := []byte{}
|
||||
for code := uint16(1); code <= 0xff; code++ {
|
||||
if glyph, ok := encoder.CodeToGlyph[code]; ok {
|
||||
glyphCode[glyph] = byte(code)
|
||||
var slots, slots1 []textencoding.CharCode
|
||||
for code := textencoding.CharCode(1); code <= 0xff; code++ {
|
||||
if glyph, ok := encoder.CharcodeToGlyph(code); ok {
|
||||
glyphCode[glyph] = code
|
||||
// Don't overwrite space
|
||||
if glyph != "space" {
|
||||
|
||||
slots1 = append(slots1, byte(code))
|
||||
slots1 = append(slots1, code)
|
||||
}
|
||||
} else {
|
||||
slots = append(slots, byte(code))
|
||||
slots = append(slots, code)
|
||||
}
|
||||
}
|
||||
slots = append(slots, slots1...)
|
||||
|
||||
// `glyphs` are the font glyphs that we need to encode.
|
||||
glyphs := []string{}
|
||||
var glyphs []textencoding.GlyphName
|
||||
for _, r := range sortedAlphabet(alphabet) {
|
||||
glyph, ok := textencoding.RuneToGlyph(r)
|
||||
if !ok {
|
||||
@ -168,7 +167,7 @@ func NewStandard14FontWithEncoding(basefont Standard14Font, alphabet map[rune]in
|
||||
|
||||
// Fill the slots, starting with the empty ones.
|
||||
slotIdx := 0
|
||||
differences := map[byte]string{}
|
||||
differences := make(map[textencoding.CharCode]textencoding.GlyphName)
|
||||
for _, glyph := range glyphs {
|
||||
if _, ok := glyphCode[glyph]; !ok {
|
||||
differences[slots[slotIdx]] = glyph
|
||||
@ -304,7 +303,7 @@ func newPdfFontFromPdfObject(fontObj core.PdfObject, allowType0 bool) (*PdfFont,
|
||||
func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
|
||||
common.Log.Trace("showText: data=[% 02x]=%#q", data, data)
|
||||
|
||||
charcodes := make([]uint16, 0, len(data)+len(data)%2)
|
||||
charcodes := make([]textencoding.CharCode, 0, len(data)+len(data)%2)
|
||||
if font.baseFields().isCIDFont() {
|
||||
if len(data) == 1 {
|
||||
data = []byte{0, data[0]}
|
||||
@ -315,11 +314,11 @@ func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
|
||||
}
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
b := uint16(data[i])<<8 | uint16(data[i+1])
|
||||
charcodes = append(charcodes, b)
|
||||
charcodes = append(charcodes, textencoding.CharCode(b))
|
||||
}
|
||||
} else {
|
||||
for _, b := range data {
|
||||
charcodes = append(charcodes, uint16(b))
|
||||
charcodes = append(charcodes, textencoding.CharCode(b))
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,7 +328,7 @@ func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
|
||||
if font.baseFields().toUnicodeCmap != nil {
|
||||
r, ok := font.baseFields().toUnicodeCmap.CharcodeToUnicode(cmap.CharCode(code))
|
||||
if ok {
|
||||
charstrings = append(charstrings, r)
|
||||
charstrings = append(charstrings, string(r))
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -345,7 +344,7 @@ func (font PdfFont) CharcodeBytesToUnicode(data []byte) (string, int, int) {
|
||||
"\tfont=%s\n\tencoding=%s",
|
||||
code, data, data, charcodes, font.baseFields().isCIDFont(), font, encoder)
|
||||
numMisses++
|
||||
charstrings = append(charstrings, cmap.MissingCodeString)
|
||||
charstrings = append(charstrings, string(cmap.MissingCodeRune))
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,7 +391,7 @@ func (font PdfFont) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns the specified char metrics for a specified glyph name.
|
||||
func (font PdfFont) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
||||
func (font PdfFont) GetGlyphCharMetrics(glyph textencoding.GlyphName) (fonts.CharMetrics, bool) {
|
||||
t := font.actualFont()
|
||||
if t == nil {
|
||||
common.Log.Debug("ERROR: GetGlyphCharMetrics Not implemented for font type=%#T", font.context)
|
||||
|
@ -114,7 +114,7 @@ func (font *pdfFontType0) baseFields() *fontCommon {
|
||||
|
||||
// GetGlyphCharMetrics returns the character metrics for the specified glyph. A bool flag is
|
||||
// returned to indicate whether or not the entry was found in the glyph to charcode mapping.
|
||||
func (font pdfFontType0) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
||||
func (font pdfFontType0) GetGlyphCharMetrics(glyph textencoding.GlyphName) (fonts.CharMetrics, bool) {
|
||||
if font.DescendantFont == nil {
|
||||
common.Log.Debug("ERROR: No descendant. font=%s", font)
|
||||
return fonts.CharMetrics{}, false
|
||||
@ -226,7 +226,7 @@ func (font pdfCIDFontType0) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
|
||||
// GetGlyphCharMetrics returns the character metrics for the specified glyph. A bool flag is
|
||||
// returned to indicate whether or not the entry was found in the glyph to charcode mapping.
|
||||
func (font pdfCIDFontType0) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
||||
func (font pdfCIDFontType0) GetGlyphCharMetrics(glyph textencoding.GlyphName) (fonts.CharMetrics, bool) {
|
||||
return fonts.CharMetrics{}, true
|
||||
}
|
||||
|
||||
@ -273,10 +273,10 @@ type pdfCIDFontType2 struct {
|
||||
CIDToGIDMap core.PdfObject
|
||||
|
||||
// Mapping between unicode runes to widths.
|
||||
runeToWidthMap map[uint16]int
|
||||
runeToWidthMap map[rune]int
|
||||
|
||||
// Also mapping between GIDs (glyph index) and width.
|
||||
gidToWidthMap map[uint16]int
|
||||
gidToWidthMap map[fonts.GID]int
|
||||
}
|
||||
|
||||
// pdfCIDFontType2FromSkeleton returns a pdfCIDFontType2 with its common fields initalized.
|
||||
@ -303,7 +303,7 @@ func (font pdfCIDFontType2) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
|
||||
// GetGlyphCharMetrics returns the character metrics for the specified glyph. A bool flag is
|
||||
// returned to indicate whether or not the entry was found in the glyph to charcode mapping.
|
||||
func (font pdfCIDFontType2) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
||||
func (font pdfCIDFontType2) GetGlyphCharMetrics(glyph textencoding.GlyphName) (fonts.CharMetrics, bool) {
|
||||
metrics := fonts.CharMetrics{}
|
||||
|
||||
enc := textencoding.NewTrueTypeFontEncoder(font.ttfParser.Chars)
|
||||
@ -315,7 +315,7 @@ func (font pdfCIDFontType2) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics
|
||||
return metrics, false
|
||||
}
|
||||
|
||||
w, found := font.runeToWidthMap[uint16(r)]
|
||||
w, found := font.runeToWidthMap[r]
|
||||
if !found {
|
||||
dw, ok := core.GetInt(font.DW)
|
||||
if !ok {
|
||||
@ -409,9 +409,9 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
cidfont.ttfParser = &ttf
|
||||
|
||||
// 2-byte character codes ➞ runes
|
||||
runes := make([]uint16, 0, len(ttf.Chars))
|
||||
runes := make([]rune, 0, len(ttf.Chars))
|
||||
for r := range ttf.Chars {
|
||||
runes = append(runes, r)
|
||||
runes = append(runes, rune(r))
|
||||
}
|
||||
// make sure runes are sorted so PDF output is stable
|
||||
sort.Slice(runes, func(i, j int) bool {
|
||||
@ -427,14 +427,14 @@ func NewCompositePdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
missingWidth := k * float64(ttf.Widths[0])
|
||||
|
||||
// Construct a rune ➞ width map.
|
||||
runeToWidthMap := map[uint16]int{}
|
||||
gidToWidthMap := map[uint16]int{}
|
||||
runeToWidthMap := make(map[rune]int)
|
||||
gidToWidthMap := map[fonts.GID]int{}
|
||||
for _, r := range runes {
|
||||
glyphIndex := ttf.Chars[r]
|
||||
gid := ttf.Chars[r]
|
||||
|
||||
w := k * float64(ttf.Widths[glyphIndex])
|
||||
w := k * float64(ttf.Widths[gid])
|
||||
runeToWidthMap[r] = int(w)
|
||||
gidToWidthMap[glyphIndex] = int(w)
|
||||
gidToWidthMap[gid] = int(w)
|
||||
}
|
||||
cidfont.runeToWidthMap = runeToWidthMap
|
||||
cidfont.gidToWidthMap = gidToWidthMap
|
||||
|
@ -36,8 +36,8 @@ type pdfFontSimple struct {
|
||||
container *core.PdfIndirectObject
|
||||
|
||||
// These fields are specific to simple PDF fonts.
|
||||
firstChar int
|
||||
lastChar int
|
||||
firstChar textencoding.CharCode
|
||||
lastChar textencoding.CharCode
|
||||
charWidths []float64
|
||||
encoder textencoding.TextEncoder
|
||||
|
||||
@ -49,7 +49,7 @@ type pdfFontSimple struct {
|
||||
Encoding core.PdfObject
|
||||
|
||||
// Standard 14 fonts metrics
|
||||
fontMetrics map[string]fonts.CharMetrics
|
||||
fontMetrics map[textencoding.GlyphName]fonts.CharMetrics
|
||||
}
|
||||
|
||||
// pdfCIDFontType0FromSkeleton returns a pdfFontSimple with its common fields initalized.
|
||||
@ -76,7 +76,7 @@ func (font *pdfFontSimple) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
|
||||
// GetGlyphCharMetrics returns the character metrics for the specified glyph. A bool flag is
|
||||
// returned to indicate whether or not the entry was found in the glyph to charcode mapping.
|
||||
func (font pdfFontSimple) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics, bool) {
|
||||
func (font pdfFontSimple) GetGlyphCharMetrics(glyph textencoding.GlyphName) (fonts.CharMetrics, bool) {
|
||||
if font.fontMetrics != nil {
|
||||
metrics, ok := font.fontMetrics[glyph]
|
||||
return metrics, ok
|
||||
@ -90,17 +90,17 @@ func (font pdfFontSimple) GetGlyphCharMetrics(glyph string) (fonts.CharMetrics,
|
||||
}
|
||||
metrics.GlyphName = glyph
|
||||
|
||||
if int(code) < font.firstChar {
|
||||
if code < font.firstChar {
|
||||
common.Log.Debug("Code lower than firstchar (%d < %d)", code, font.firstChar)
|
||||
return metrics, false
|
||||
}
|
||||
|
||||
if int(code) > font.lastChar {
|
||||
if code > font.lastChar {
|
||||
common.Log.Debug("Code higher than lastchar (%d < %d)", code, font.lastChar)
|
||||
return metrics, false
|
||||
}
|
||||
|
||||
index := int(code) - font.firstChar
|
||||
index := int(code - font.firstChar)
|
||||
if index >= len(font.charWidths) {
|
||||
common.Log.Debug("Code outside of widths range")
|
||||
return metrics, false
|
||||
@ -135,7 +135,7 @@ func newSimpleFontFromPdfObject(d *core.PdfObjectDictionary, base *fontCommon, s
|
||||
common.Log.Debug("ERROR: Invalid FirstChar type (%T)", obj)
|
||||
return nil, core.ErrTypeError
|
||||
}
|
||||
font.firstChar = int(intVal)
|
||||
font.firstChar = textencoding.CharCode(intVal)
|
||||
|
||||
obj = d.Get("LastChar")
|
||||
if obj == nil {
|
||||
@ -147,7 +147,7 @@ func newSimpleFontFromPdfObject(d *core.PdfObjectDictionary, base *fontCommon, s
|
||||
common.Log.Debug("ERROR: Invalid LastChar type (%T)", obj)
|
||||
return nil, core.ErrTypeError
|
||||
}
|
||||
font.lastChar = int(intVal)
|
||||
font.lastChar = textencoding.CharCode(intVal)
|
||||
|
||||
font.charWidths = []float64{}
|
||||
obj = d.Get("Widths")
|
||||
@ -166,7 +166,7 @@ func newSimpleFontFromPdfObject(d *core.PdfObjectDictionary, base *fontCommon, s
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(widths) != (font.lastChar - font.firstChar + 1) {
|
||||
if len(widths) != int(font.lastChar-font.firstChar+1) {
|
||||
common.Log.Debug("ERROR: Invalid widths length != %d (%d)",
|
||||
font.lastChar-font.firstChar+1, len(widths))
|
||||
return nil, core.ErrRangeError
|
||||
@ -182,10 +182,12 @@ func newSimpleFontFromPdfObject(d *core.PdfObjectDictionary, base *fontCommon, s
|
||||
// addEncoding adds the encoding to the font.
|
||||
// The order of precedence is important.
|
||||
func (font *pdfFontSimple) addEncoding() error {
|
||||
var baseEncoder string
|
||||
var differences map[byte]string
|
||||
var err error
|
||||
var encoder *textencoding.SimpleEncoder
|
||||
var (
|
||||
baseEncoder string
|
||||
differences map[textencoding.CharCode]textencoding.GlyphName
|
||||
err error
|
||||
encoder *textencoding.SimpleEncoder
|
||||
)
|
||||
|
||||
if font.Encoding != nil {
|
||||
baseEncoder, differences, err = getFontEncoding(font.Encoding)
|
||||
@ -247,7 +249,7 @@ func (font *pdfFontSimple) addEncoding() error {
|
||||
// Except for Type 3 fonts, every font program shall have a built-in encoding. Under certain
|
||||
// circumstances, a PDF font dictionary may change the encoding used with the font program to match
|
||||
// the requirements of the conforming writer generating the text being shown.
|
||||
func getFontEncoding(obj core.PdfObject) (baseName string, differences map[byte]string, err error) {
|
||||
func getFontEncoding(obj core.PdfObject) (baseName string, differences map[textencoding.CharCode]textencoding.GlyphName, err error) {
|
||||
baseName = "StandardEncoding"
|
||||
|
||||
if obj == nil {
|
||||
@ -313,8 +315,8 @@ func (font *pdfFontSimple) ToPdfObject() core.PdfObject {
|
||||
// styling functions.
|
||||
// Uses a WinAnsiTextEncoder and loads only character codes 32-255.
|
||||
func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
const minCode = 32
|
||||
const maxCode = 255
|
||||
const minCode = textencoding.CharCode(32)
|
||||
const maxCode = textencoding.CharCode(255)
|
||||
|
||||
ttf, err := fonts.TtfParse(filePath)
|
||||
if err != nil {
|
||||
@ -336,8 +338,8 @@ func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
truefont.lastChar = maxCode
|
||||
|
||||
truefont.basefont = ttf.PostScriptName
|
||||
truefont.FirstChar = core.MakeInteger(minCode)
|
||||
truefont.LastChar = core.MakeInteger(maxCode)
|
||||
truefont.FirstChar = core.MakeInteger(int64(minCode))
|
||||
truefont.LastChar = core.MakeInteger(int64(maxCode))
|
||||
|
||||
k := 1000.0 / float64(ttf.UnitsPerEm)
|
||||
if len(ttf.Widths) <= 0 {
|
||||
@ -348,14 +350,14 @@ func NewPdfFontFromTTFFile(filePath string) (*PdfFont, error) {
|
||||
|
||||
vals := make([]float64, 0, maxCode-minCode+1)
|
||||
for code := minCode; code <= maxCode; code++ {
|
||||
r, found := truefont.Encoder().CharcodeToRune(uint16(code))
|
||||
r, found := truefont.Encoder().CharcodeToRune(code)
|
||||
if !found {
|
||||
common.Log.Debug("Rune not found (code: %d)", code)
|
||||
vals = append(vals, missingWidth)
|
||||
continue
|
||||
}
|
||||
|
||||
pos, ok := ttf.Chars[uint16(r)]
|
||||
pos, ok := ttf.Chars[r]
|
||||
if !ok {
|
||||
common.Log.Debug("Rune not in TTF Chars")
|
||||
vals = append(vals, missingWidth)
|
||||
@ -450,7 +452,7 @@ const (
|
||||
)
|
||||
|
||||
var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
Courier: pdfFontSimple{
|
||||
Courier: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Courier",
|
||||
@ -458,7 +460,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.CourierCharMetrics,
|
||||
},
|
||||
CourierBold: pdfFontSimple{
|
||||
CourierBold: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Courier-Bold",
|
||||
@ -466,7 +468,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.CourierBoldCharMetrics,
|
||||
},
|
||||
CourierBoldOblique: pdfFontSimple{
|
||||
CourierBoldOblique: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Courier-BoldOblique",
|
||||
@ -474,7 +476,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.CourierBoldObliqueCharMetrics,
|
||||
},
|
||||
CourierOblique: pdfFontSimple{
|
||||
CourierOblique: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Courier-Oblique",
|
||||
@ -482,7 +484,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.CourierObliqueCharMetrics,
|
||||
},
|
||||
Helvetica: pdfFontSimple{
|
||||
Helvetica: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Helvetica",
|
||||
@ -490,7 +492,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.HelveticaCharMetrics,
|
||||
},
|
||||
HelveticaBold: pdfFontSimple{
|
||||
HelveticaBold: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Helvetica-Bold",
|
||||
@ -498,7 +500,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.HelveticaBoldCharMetrics,
|
||||
},
|
||||
HelveticaBoldOblique: pdfFontSimple{
|
||||
HelveticaBoldOblique: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Helvetica-BoldOblique",
|
||||
@ -506,7 +508,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.HelveticaBoldObliqueCharMetrics,
|
||||
},
|
||||
HelveticaOblique: pdfFontSimple{
|
||||
HelveticaOblique: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Helvetica-Oblique",
|
||||
@ -514,7 +516,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.HelveticaObliqueCharMetrics,
|
||||
},
|
||||
TimesRoman: pdfFontSimple{
|
||||
TimesRoman: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Times-Roman",
|
||||
@ -522,7 +524,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.TimesRomanCharMetrics,
|
||||
},
|
||||
TimesBold: pdfFontSimple{
|
||||
TimesBold: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Times-Bold",
|
||||
@ -530,7 +532,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.TimesBoldCharMetrics,
|
||||
},
|
||||
TimesBoldItalic: pdfFontSimple{
|
||||
TimesBoldItalic: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Times-BoldItalic",
|
||||
@ -538,7 +540,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.TimesBoldItalicCharMetrics,
|
||||
},
|
||||
TimesItalic: pdfFontSimple{
|
||||
TimesItalic: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Times-Italic",
|
||||
@ -546,7 +548,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewWinAnsiTextEncoder(),
|
||||
fontMetrics: fonts.TimesItalicCharMetrics,
|
||||
},
|
||||
Symbol: pdfFontSimple{
|
||||
Symbol: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "Symbol",
|
||||
@ -554,7 +556,7 @@ var standard14Fonts = map[Standard14Font]pdfFontSimple{
|
||||
encoder: textencoding.NewSymbolEncoder(),
|
||||
fontMetrics: fonts.SymbolCharMetrics,
|
||||
},
|
||||
ZapfDingbats: pdfFontSimple{
|
||||
ZapfDingbats: {
|
||||
fontCommon: fontCommon{
|
||||
subtype: "Type1",
|
||||
basefont: "ZapfDingbats",
|
||||
|
@ -623,7 +623,7 @@ endobj
|
||||
|
||||
// Expected is WinAnsiEncoding with the applied differences.
|
||||
winansi := textencoding.NewWinAnsiTextEncoder()
|
||||
differencesMap := map[int]string{
|
||||
differencesMap := map[textencoding.CharCode]textencoding.GlyphName{
|
||||
24: `/breve`,
|
||||
25: `/caron`,
|
||||
26: `/circumflex`,
|
||||
@ -754,10 +754,10 @@ endobj
|
||||
255: `/ydieresis`,
|
||||
}
|
||||
|
||||
for ccode := 32; ccode < 255; ccode++ {
|
||||
fontglyph, has := font.Encoder().CharcodeToGlyph(uint16(ccode))
|
||||
for ccode := textencoding.CharCode(32); ccode < 255; ccode++ {
|
||||
fontglyph, has := font.Encoder().CharcodeToGlyph(ccode)
|
||||
if !has {
|
||||
baseglyph, bad := winansi.CharcodeToGlyph(uint16(ccode))
|
||||
baseglyph, bad := winansi.CharcodeToGlyph(ccode)
|
||||
if bad {
|
||||
t.Fatalf("font not having glyph for char code %d - whereas base encoding had '%s'", ccode, baseglyph)
|
||||
}
|
||||
@ -766,7 +766,7 @@ endobj
|
||||
// Check if in differencesmap first.
|
||||
glyph, has := differencesMap[ccode]
|
||||
if has {
|
||||
glyph = strings.Trim(glyph, `/`)
|
||||
glyph = textencoding.GlyphName(strings.Trim(string(glyph), `/`))
|
||||
if glyph != fontglyph {
|
||||
t.Fatalf("Mismatch for char code %d, font has: %s and expected is: %s (differences)", ccode, fontglyph, glyph)
|
||||
}
|
||||
@ -774,7 +774,7 @@ endobj
|
||||
}
|
||||
|
||||
// If not in differences, should be according to WinAnsiEncoding (base).
|
||||
glyph, has = winansi.CharcodeToGlyph(uint16(ccode))
|
||||
glyph, has = winansi.CharcodeToGlyph(ccode)
|
||||
if !has {
|
||||
t.Fatalf("WinAnsi not having glyph for char code %d", ccode)
|
||||
}
|
||||
|
@ -220,9 +220,9 @@ func getKeyValues(data string) map[string]string {
|
||||
}
|
||||
|
||||
// getEncodings returns the encodings encoded in `data`.
|
||||
func getEncodings(data string) (map[uint16]string, error) {
|
||||
func getEncodings(data string) (map[textencoding.CharCode]textencoding.GlyphName, error) {
|
||||
lines := strings.Split(data, "\n")
|
||||
keyValues := map[uint16]string{}
|
||||
keyValues := make(map[textencoding.CharCode]textencoding.GlyphName)
|
||||
for _, line := range lines {
|
||||
matches := reEncoding.FindStringSubmatch(line)
|
||||
if matches == nil {
|
||||
@ -234,7 +234,7 @@ func getEncodings(data string) (map[uint16]string, error) {
|
||||
common.Log.Debug("ERROR: Bad encoding line. %q", line)
|
||||
return nil, core.ErrTypeError
|
||||
}
|
||||
keyValues[uint16(code)] = glyph
|
||||
keyValues[textencoding.CharCode(code)] = textencoding.GlyphName(glyph)
|
||||
}
|
||||
common.Log.Trace("getEncodings: keyValues=%#v", keyValues)
|
||||
return keyValues, nil
|
||||
|
@ -38,7 +38,7 @@ func (font FontCourier) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontCourier) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontCourier) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := CourierCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontCourier) ToPdfObject() core.PdfObject {
|
||||
|
||||
// CourierCharMetrics are the font metrics loaded from afms/Courier.afm. See afms/MustRead.html for
|
||||
// license information.
|
||||
var CourierCharMetrics = map[string]CharMetrics{
|
||||
var CourierCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontCourierBold) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontCourierBold) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontCourierBold) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := CourierBoldCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -59,7 +59,7 @@ func (font FontCourierBold) ToPdfObject() core.PdfObject {
|
||||
}
|
||||
|
||||
// Courier-Bold font metrics loaded from afms/Courier-Bold.afm. See afms/MustRead.html for license information.
|
||||
var CourierBoldCharMetrics = map[string]CharMetrics{
|
||||
var CourierBoldCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
|
||||
|
@ -39,7 +39,7 @@ func (font FontCourierBoldOblique) SetEncoder(encoder textencoding.TextEncoder)
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontCourierBoldOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontCourierBoldOblique) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := CourierBoldObliqueCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -61,7 +61,7 @@ func (font FontCourierBoldOblique) ToPdfObject() core.PdfObject {
|
||||
|
||||
// CourierBoldObliqueCharMetrics are the font metrics loaded from afms/Courier-BoldOblique.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var CourierBoldObliqueCharMetrics = map[string]CharMetrics{
|
||||
var CourierBoldObliqueCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontCourierOblique) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontCourierOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontCourierOblique) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := CourierObliqueCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontCourierOblique) ToPdfObject() core.PdfObject {
|
||||
|
||||
// CourierObliqueCharMetrics are the font metrics loaded from afms/Courier-Oblique.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var CourierObliqueCharMetrics = map[string]CharMetrics{
|
||||
var CourierObliqueCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 600.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 600.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 600.000000, Wy: 0.000000},
|
||||
|
@ -15,13 +15,13 @@ import (
|
||||
type Font interface {
|
||||
Encoder() textencoding.TextEncoder
|
||||
SetEncoder(encoder textencoding.TextEncoder)
|
||||
GetGlyphCharMetrics(glyph string) (CharMetrics, bool)
|
||||
GetGlyphCharMetrics(glyph textencoding.GlyphName) (CharMetrics, bool)
|
||||
ToPdfObject() core.PdfObject
|
||||
}
|
||||
|
||||
// CharMetrics represents width and height metrics of a glyph.
|
||||
type CharMetrics struct {
|
||||
GlyphName string
|
||||
GlyphName textencoding.GlyphName
|
||||
Wx float64
|
||||
Wy float64
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func (font FontHelvetica) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontHelvetica) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontHelvetica) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := HelveticaCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontHelvetica) ToPdfObject() core.PdfObject {
|
||||
|
||||
// HelveticaCharMetrics are the font metrics loaded from afms/Helvetica.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var HelveticaCharMetrics = map[string]CharMetrics{
|
||||
var HelveticaCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 667.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 667.000000, Wy: 0.000000},
|
||||
|
@ -39,7 +39,7 @@ func (font FontHelveticaBold) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontHelveticaBold) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontHelveticaBold) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := HelveticaBoldCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -61,7 +61,7 @@ func (font FontHelveticaBold) ToPdfObject() core.PdfObject {
|
||||
|
||||
// HelveticaBoldCharMetrics are the font metrics loaded from afms/Helvetica-Bold.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var HelveticaBoldCharMetrics = map[string]CharMetrics{
|
||||
var HelveticaBoldCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontHelveticaBoldOblique) SetEncoder(encoder textencoding.TextEncoder
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontHelveticaBoldOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontHelveticaBoldOblique) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := HelveticaBoldObliqueCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontHelveticaBoldOblique) ToPdfObject() core.PdfObject {
|
||||
|
||||
// HelveticaBoldObliqueCharMetrics are the font metrics loaded from afms/Helvetica-BoldOblique.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var HelveticaBoldObliqueCharMetrics = map[string]CharMetrics{
|
||||
var HelveticaBoldObliqueCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontHelveticaOblique) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontHelveticaOblique) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontHelveticaOblique) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := HelveticaObliqueCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontHelveticaOblique) ToPdfObject() core.PdfObject {
|
||||
|
||||
// HelveticaObliqueCharMetrics are the font metrics loaded from afms/Helvetica-Oblique.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var HelveticaObliqueCharMetrics = map[string]CharMetrics{
|
||||
var HelveticaObliqueCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 667.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 667.000000, Wy: 0.000000},
|
||||
|
@ -39,7 +39,7 @@ func (font FontSymbol) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontSymbol) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontSymbol) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := SymbolCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -63,7 +63,7 @@ func (font FontSymbol) ToPdfObject() core.PdfObject {
|
||||
|
||||
// SymbolCharMetrics are the font metrics loaded from afms/Symbol.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var SymbolCharMetrics = map[string]CharMetrics{
|
||||
var SymbolCharMetrics = map[GlyphName]CharMetrics{
|
||||
"Alpha": {GlyphName: "Alpha", Wx: 722.000000, Wy: 0.000000},
|
||||
"Beta": {GlyphName: "Beta", Wx: 667.000000, Wy: 0.000000},
|
||||
"Chi": {GlyphName: "Chi", Wx: 722.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontTimesBold) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontTimesBold) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontTimesBold) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := TimesBoldCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontTimesBold) ToPdfObject() core.PdfObject {
|
||||
|
||||
// TimesBoldCharMetrics are the font metrics loaded from afms/Times-Bold.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var TimesBoldCharMetrics = map[string]CharMetrics{
|
||||
var TimesBoldCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 1000.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontTimesBoldItalic) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontTimesBoldItalic) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontTimesBoldItalic) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := TimesBoldItalicCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontTimesBoldItalic) ToPdfObject() core.PdfObject {
|
||||
|
||||
// TimesBoldItalicCharMetrics are the font metrics loaded from afms/Times-BoldItalic.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var TimesBoldItalicCharMetrics = map[string]CharMetrics{
|
||||
var TimesBoldItalicCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 667.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 944.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 667.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontTimesItalic) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontTimesItalic) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontTimesItalic) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := TimesItalicCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontTimesItalic) ToPdfObject() core.PdfObject {
|
||||
|
||||
// TimesItalicCharMetrics font metrics loaded from afms/Times-Italic.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var TimesItalicCharMetrics = map[string]CharMetrics{
|
||||
var TimesItalicCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 611.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 889.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 611.000000, Wy: 0.000000},
|
||||
|
@ -38,7 +38,7 @@ func (font FontTimesRoman) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontTimesRoman) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontTimesRoman) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := TimesRomanCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -60,7 +60,7 @@ func (font FontTimesRoman) ToPdfObject() core.PdfObject {
|
||||
|
||||
// TimesRomanCharMetrics are the font metrics loaded from afms/Times-Roman.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var TimesRomanCharMetrics = map[string]CharMetrics{
|
||||
var TimesRomanCharMetrics = map[GlyphName]CharMetrics{
|
||||
"A": {GlyphName: "A", Wx: 722.000000, Wy: 0.000000},
|
||||
"AE": {GlyphName: "AE", Wx: 889.000000, Wy: 0.000000},
|
||||
"Aacute": {GlyphName: "Aacute", Wx: 722.000000, Wy: 0.000000},
|
||||
|
@ -46,17 +46,19 @@ import (
|
||||
|
||||
// MakeEncoder returns an encoder built from the tables in `rec`.
|
||||
func (rec *TtfType) MakeEncoder() (*textencoding.SimpleEncoder, error) {
|
||||
encoding := map[uint16]string{}
|
||||
for code := uint16(0); code <= 256; code++ {
|
||||
gid, ok := rec.Chars[code]
|
||||
encoding := make(map[textencoding.CharCode]GlyphName)
|
||||
for code := textencoding.CharCode(0); code <= 256; code++ {
|
||||
r := rune(code) // TODO(dennwc): make sure this conversion is valid
|
||||
gid, ok := rec.Chars[r]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
glyph := ""
|
||||
var glyph GlyphName
|
||||
if int(gid) >= 0 && int(gid) < len(rec.GlyphNames) {
|
||||
glyph = rec.GlyphNames[gid]
|
||||
} else {
|
||||
glyph = string(rune(gid))
|
||||
// TODO(dennwc): shouldn't this be uniXXX?
|
||||
glyph = GlyphName(rune(gid))
|
||||
}
|
||||
encoding[code] = glyph
|
||||
}
|
||||
@ -67,6 +69,12 @@ func (rec *TtfType) MakeEncoder() (*textencoding.SimpleEncoder, error) {
|
||||
return textencoding.NewCustomSimpleTextEncoder(encoding, nil)
|
||||
}
|
||||
|
||||
// GID is a glyph index.
|
||||
type GID = textencoding.GID
|
||||
|
||||
// GlyphName is a name of a glyph.
|
||||
type GlyphName = textencoding.GlyphName
|
||||
|
||||
// TtfType describes a TrueType font file.
|
||||
// http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-chapter08
|
||||
type TtfType struct {
|
||||
@ -85,25 +93,27 @@ type TtfType struct {
|
||||
|
||||
// Chars maps rune values (unicode) to the indexes in GlyphNames. i.e. GlyphNames[Chars[r]] is
|
||||
// the glyph corresponding to rune r.
|
||||
Chars map[uint16]uint16
|
||||
Chars map[rune]GID
|
||||
// GlyphNames is a list of glyphs from the "post" section of the TrueType file.
|
||||
GlyphNames []string
|
||||
GlyphNames []GlyphName
|
||||
}
|
||||
|
||||
// MakeToUnicode returns a ToUnicode CMap based on the encoding of `ttf`.
|
||||
// XXX(peterwilliams97): This currently gives a bad text mapping for creator_test.go but leads to an
|
||||
// otherwise valid PDF file that Adobe Reader displays without error.
|
||||
func (ttf *TtfType) MakeToUnicode() *cmap.CMap {
|
||||
codeToUnicode := map[cmap.CharCode]string{}
|
||||
for code, idx := range ttf.Chars {
|
||||
glyph := ttf.GlyphNames[idx]
|
||||
codeToUnicode := make(map[cmap.CharCode]rune)
|
||||
for code, gid := range ttf.Chars {
|
||||
glyph := ttf.GlyphNames[gid]
|
||||
|
||||
// TODO(dennwc): 'code' is already a rune; do we need this extra lookup?
|
||||
r, ok := textencoding.GlyphToRune(glyph)
|
||||
if !ok {
|
||||
common.Log.Debug("No rune. code=0x%04x glyph=%q", code, glyph)
|
||||
r = textencoding.MissingCodeRune
|
||||
}
|
||||
codeToUnicode[cmap.CharCode(code)] = string(r)
|
||||
// TODO(dennwc): implies rune <-> charcode identity?
|
||||
codeToUnicode[cmap.CharCode(code)] = r
|
||||
}
|
||||
return cmap.NewToUnicodeCMap(codeToUnicode)
|
||||
}
|
||||
@ -322,11 +332,11 @@ func (t *ttfParser) ParseHmtx() error {
|
||||
|
||||
// parseCmapSubtable31 parses information from an (3,1) subtable (Windows Unicode).
|
||||
func (t *ttfParser) parseCmapSubtable31(offset31 int64) error {
|
||||
startCount := make([]uint16, 0, 8)
|
||||
endCount := make([]uint16, 0, 8)
|
||||
startCount := make([]rune, 0, 8)
|
||||
endCount := make([]rune, 0, 8)
|
||||
idDelta := make([]int16, 0, 8)
|
||||
idRangeOffset := make([]uint16, 0, 8)
|
||||
t.rec.Chars = make(map[uint16]uint16)
|
||||
t.rec.Chars = make(map[rune]GID)
|
||||
t.f.Seek(int64(t.tables["cmap"])+offset31, os.SEEK_SET)
|
||||
format := t.ReadUShort()
|
||||
if format != 4 {
|
||||
@ -336,11 +346,11 @@ func (t *ttfParser) parseCmapSubtable31(offset31 int64) error {
|
||||
segCount := int(t.ReadUShort() / 2)
|
||||
t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
|
||||
for j := 0; j < segCount; j++ {
|
||||
endCount = append(endCount, t.ReadUShort())
|
||||
endCount = append(endCount, rune(t.ReadUShort()))
|
||||
}
|
||||
t.Skip(2) // reservedPad
|
||||
for j := 0; j < segCount; j++ {
|
||||
startCount = append(startCount, t.ReadUShort())
|
||||
startCount = append(startCount, rune(t.ReadUShort()))
|
||||
}
|
||||
for j := 0; j < segCount; j++ {
|
||||
idDelta = append(idDelta, t.ReadShort())
|
||||
@ -374,7 +384,7 @@ func (t *ttfParser) parseCmapSubtable31(offset31 int64) error {
|
||||
gid -= 65536
|
||||
}
|
||||
if gid > 0 {
|
||||
t.rec.Chars[c] = uint16(gid)
|
||||
t.rec.Chars[c] = GID(gid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,7 +395,7 @@ func (t *ttfParser) parseCmapSubtable31(offset31 int64) error {
|
||||
func (t *ttfParser) parseCmapSubtable10(offset10 int64) error {
|
||||
|
||||
if t.rec.Chars == nil {
|
||||
t.rec.Chars = make(map[uint16]uint16)
|
||||
t.rec.Chars = make(map[rune]GID)
|
||||
}
|
||||
|
||||
t.f.Seek(int64(t.tables["cmap"])+offset10, os.SEEK_SET)
|
||||
@ -412,10 +422,10 @@ func (t *ttfParser) parseCmapSubtable10(offset10 int64) error {
|
||||
}
|
||||
data := []byte(dataStr)
|
||||
|
||||
for code, glyphId := range data {
|
||||
t.rec.Chars[uint16(code)] = uint16(glyphId)
|
||||
if glyphId != 0 {
|
||||
fmt.Printf("\t0x%02x ➞ 0x%02x=%c\n", code, glyphId, rune(glyphId))
|
||||
for code, gid := range data {
|
||||
t.rec.Chars[rune(code)] = GID(gid)
|
||||
if gid != 0 {
|
||||
fmt.Printf("\t0x%02x ➞ 0x%02x=%c\n", code, gid, rune(gid))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -468,7 +478,7 @@ func (t *ttfParser) parseCmapVersion(offset int64) error {
|
||||
common.Log.Trace("parseCmapVersion: offset=%d", offset)
|
||||
|
||||
if t.rec.Chars == nil {
|
||||
t.rec.Chars = make(map[uint16]uint16)
|
||||
t.rec.Chars = make(map[rune]GID)
|
||||
}
|
||||
|
||||
t.f.Seek(int64(t.tables["cmap"])+offset, os.SEEK_SET)
|
||||
@ -507,7 +517,7 @@ func (t *ttfParser) parseCmapFormat0() error {
|
||||
common.Log.Trace("parseCmapFormat0: %s\ndataStr=%+q\ndata=[% 02x]", t.rec.String(), dataStr, data)
|
||||
|
||||
for code, glyphId := range data {
|
||||
t.rec.Chars[uint16(code)] = uint16(glyphId)
|
||||
t.rec.Chars[rune(code)] = GID(glyphId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -521,8 +531,8 @@ func (t *ttfParser) parseCmapFormat6() error {
|
||||
t.rec.String(), firstCode, entryCount)
|
||||
|
||||
for i := 0; i < entryCount; i++ {
|
||||
glyphId := t.ReadUShort()
|
||||
t.rec.Chars[uint16(i+firstCode)] = glyphId
|
||||
glyphId := GID(t.ReadUShort())
|
||||
t.rec.Chars[rune(i+firstCode)] = glyphId
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -557,7 +567,7 @@ func (t *ttfParser) parseCmapFormat12() error {
|
||||
common.Log.Debug("Format 12 cmap contains character beyond UCS-4")
|
||||
}
|
||||
|
||||
t.rec.Chars[uint16(i+firstCode)] = uint16(glyphId)
|
||||
t.rec.Chars[rune(i+firstCode)] = GID(glyphId)
|
||||
}
|
||||
|
||||
}
|
||||
@ -646,7 +656,7 @@ func (t *ttfParser) ParsePost() error {
|
||||
case 2.0:
|
||||
numGlyphs := int(t.ReadUShort())
|
||||
glyphNameIndex := make([]int, numGlyphs)
|
||||
t.rec.GlyphNames = make([]string, numGlyphs)
|
||||
t.rec.GlyphNames = make([]GlyphName, numGlyphs)
|
||||
maxIndex := -1
|
||||
for i := 0; i < numGlyphs; i++ {
|
||||
index := int(t.ReadUShort())
|
||||
@ -656,16 +666,16 @@ func (t *ttfParser) ParsePost() error {
|
||||
maxIndex = index
|
||||
}
|
||||
}
|
||||
var nameArray []string
|
||||
var nameArray []GlyphName
|
||||
if maxIndex >= len(macGlyphNames) {
|
||||
nameArray = make([]string, maxIndex-len(macGlyphNames)+1)
|
||||
nameArray = make([]GlyphName, maxIndex-len(macGlyphNames)+1)
|
||||
for i := 0; i < maxIndex-len(macGlyphNames)+1; i++ {
|
||||
numberOfChars := int(t.ReadByte())
|
||||
names, err := t.ReadStr(numberOfChars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nameArray[i] = names
|
||||
nameArray[i] = GlyphName(names)
|
||||
}
|
||||
}
|
||||
for i := 0; i < numGlyphs; i++ {
|
||||
@ -684,7 +694,7 @@ func (t *ttfParser) ParsePost() error {
|
||||
offset := int(t.ReadSByte())
|
||||
glyphNameIndex[i] = i + 1 + offset
|
||||
}
|
||||
t.rec.GlyphNames = make([]string, len(glyphNameIndex))
|
||||
t.rec.GlyphNames = make([]GlyphName, len(glyphNameIndex))
|
||||
for i := 0; i < len(t.rec.GlyphNames); i++ {
|
||||
name := macGlyphNames[glyphNameIndex[i]]
|
||||
t.rec.GlyphNames[i] = name
|
||||
@ -700,7 +710,7 @@ func (t *ttfParser) ParsePost() error {
|
||||
}
|
||||
|
||||
// The 258 standard mac glyph names used in 'post' format 1 and 2.
|
||||
var macGlyphNames = []string{
|
||||
var macGlyphNames = []GlyphName{
|
||||
".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl",
|
||||
"numbersign", "dollar", "percent", "ampersand", "quotesingle",
|
||||
"parenleft", "parenright", "asterisk", "plus", "comma", "hyphen",
|
||||
|
@ -9,6 +9,8 @@ import (
|
||||
|
||||
const fontDir = `../../creator/testdata`
|
||||
|
||||
type charCode = textencoding.CharCode
|
||||
|
||||
var casesTTFParse = []struct {
|
||||
path string
|
||||
name string
|
||||
@ -18,7 +20,7 @@ var casesTTFParse = []struct {
|
||||
underlineTh int16
|
||||
isFixed bool
|
||||
bbox [4]int
|
||||
runes map[rune]uint16
|
||||
runes map[rune]charCode
|
||||
widths map[rune]int
|
||||
}{
|
||||
{
|
||||
@ -27,7 +29,7 @@ var casesTTFParse = []struct {
|
||||
underlinePos: -151,
|
||||
underlineTh: 50,
|
||||
bbox: [4]int{-631, 1632, -462, 1230},
|
||||
runes: map[rune]uint16{
|
||||
runes: map[rune]charCode{
|
||||
'x': 0x5d,
|
||||
'ё': 0x32a,
|
||||
},
|
||||
@ -43,7 +45,7 @@ var casesTTFParse = []struct {
|
||||
underlinePos: -150,
|
||||
underlineTh: 100,
|
||||
bbox: [4]int{-1488, 2439, -555, 2163},
|
||||
runes: map[rune]uint16{
|
||||
runes: map[rune]charCode{
|
||||
'x': 0x5c,
|
||||
'ё': 0x3cb,
|
||||
},
|
||||
@ -60,7 +62,7 @@ var casesTTFParse = []struct {
|
||||
underlinePos: -150,
|
||||
underlineTh: 100,
|
||||
bbox: [4]int{-1459, 2467, -555, 2163},
|
||||
runes: map[rune]uint16{
|
||||
runes: map[rune]charCode{
|
||||
'x': 0x5c,
|
||||
'ё': 0x3cb,
|
||||
},
|
||||
@ -103,6 +105,7 @@ func TestTTFParse(t *testing.T) {
|
||||
if ft.PostScriptName != c.name {
|
||||
t.Errorf("%q %q", ft.PostScriptName, c.name)
|
||||
}
|
||||
|
||||
enc := textencoding.NewTrueTypeFontEncoder(ft.Chars)
|
||||
|
||||
for _, r := range testRunes {
|
||||
@ -113,7 +116,7 @@ func TestTTFParse(t *testing.T) {
|
||||
} else if ind != c.runes[r] {
|
||||
t.Fatalf("%x != %x", ind, c.runes[r])
|
||||
}
|
||||
w := ft.Widths[ft.Chars[uint16(r)]]
|
||||
w := ft.Widths[ft.Chars[r]]
|
||||
if int(w) != c.widths[r] {
|
||||
t.Errorf("%d != %d", int(w), c.widths[r])
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func (font FontZapfDingbats) SetEncoder(encoder textencoding.TextEncoder) {
|
||||
}
|
||||
|
||||
// GetGlyphCharMetrics returns character metrics for a given glyph.
|
||||
func (font FontZapfDingbats) GetGlyphCharMetrics(glyph string) (CharMetrics, bool) {
|
||||
func (font FontZapfDingbats) GetGlyphCharMetrics(glyph GlyphName) (CharMetrics, bool) {
|
||||
metrics, has := ZapfDingbatsCharMetrics[glyph]
|
||||
if !has {
|
||||
return metrics, false
|
||||
@ -62,7 +62,7 @@ func (font FontZapfDingbats) ToPdfObject() core.PdfObject {
|
||||
|
||||
// ZapfDingbatsCharMetrics are the font metrics loaded from afms/ZapfDingbats.afm.
|
||||
// See afms/MustRead.html for license information.
|
||||
var ZapfDingbatsCharMetrics = map[string]CharMetrics{
|
||||
var ZapfDingbatsCharMetrics = map[GlyphName]CharMetrics{
|
||||
"a1": {GlyphName: "a1", Wx: 974.000000, Wy: 0.000000},
|
||||
"a10": {GlyphName: "a10", Wx: 692.000000, Wy: 0.000000},
|
||||
"a100": {GlyphName: "a100", Wx: 668.000000, Wy: 0.000000},
|
||||
|
Loading…
x
Reference in New Issue
Block a user