github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/font.go (about) 1 package giopdf 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 9 "gioui.org/f32" 10 "github.com/andybalholm/giopdf/cff" 11 "github.com/andybalholm/giopdf/pdf" 12 "github.com/benoitkugler/textlayout/fonts" 13 "github.com/benoitkugler/textlayout/fonts/simpleencodings" 14 "github.com/benoitkugler/textlayout/fonts/truetype" 15 "github.com/benoitkugler/textlayout/fonts/type1" 16 "golang.org/x/image/math/fixed" 17 ) 18 19 // A Glyph represents a character from a font. It uses a coordinate system 20 // where the origin is at the left end of the baseline of the glyph, y 21 // increases vertically, and the font size is one unit. 22 type Glyph struct { 23 Outlines []PathElement 24 Width float32 25 } 26 27 // A Font converts text strings to slices of Glyphs, so that they can be 28 // displayed. 29 type Font interface { 30 ToGlyphs(s string) []Glyph 31 } 32 33 // A SimpleFont is a font with a simple 8-bit encoding. 34 type SimpleFont struct { 35 Glyphs [256]Glyph 36 } 37 38 func (f *SimpleFont) ToGlyphs(s string) []Glyph { 39 result := make([]Glyph, len(s)) 40 for i := 0; i < len(s); i++ { 41 result[i] = f.Glyphs[s[i]] 42 } 43 return result 44 } 45 46 func scalePoint(p fixed.Point26_6, ppem fixed.Int26_6) f32.Point { 47 return f32.Pt(float32(p.X)/float32(ppem), -float32(p.Y)/float32(ppem)) 48 } 49 50 // SimpleFontFromSFNT converts an SFNT (TrueType or OpenType) font to a 51 // SimpleFont with the specified encoding. 52 func SimpleFontFromSFNT(data []byte, enc simpleencodings.Encoding) (*SimpleFont, error) { 53 f, err := truetype.Parse(bytes.NewReader(data)) 54 if err != nil { 55 return nil, err 56 } 57 58 ppem := f.Upem() 59 scale := 1 / float32(ppem) 60 fm := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(scale, scale)) 61 62 var GIDEncoding [256]fonts.GID 63 64 if enc != (simpleencodings.Encoding{}) { 65 for b, r := range enc.ByteToRune() { 66 gi, ok := f.NominalGlyph(r) 67 if ok { 68 GIDEncoding[b] = gi 69 } 70 } 71 } else { 72 // The encoding in the font dictionary was missing, so use the font's 73 // builtin encoding. 74 fp, err := truetype.NewFontParser(bytes.NewReader(data)) 75 if err != nil { 76 return nil, err 77 } 78 ct, err := fp.CmapTable() 79 if err != nil { 80 return nil, err 81 } 82 cmap3_0 := ct.FindSubtable(truetype.CmapID{3, 0}) 83 if cmap3_0 != nil { 84 // If the font contains a (3, 0) subtable, the range of character codes must be one 85 // of the following: 0x0000 - 0x00FF, 0xF000 - 0xF0FF, 0xF100 - 0xF1FF, or 86 // 0xF200 - 0xF2FF. Depending on the range of codes, each byte from the string is 87 // prepended with the high byte of the range, to form a two-byte character, which 88 // is used to select the associated glyph description from the subtable. 89 iter := cmap3_0.Iter() 90 for iter.Next() { 91 code, gid := iter.Char() 92 switch code & 0xFF00 { 93 case 0x0000, 0xF000, 0xF100, 0xF200: 94 b := code & 0xFF 95 if GIDEncoding[b] == 0 { 96 GIDEncoding[b] = gid 97 } 98 } 99 } 100 } 101 cmap1_0 := ct.FindSubtable(truetype.CmapID{1, 0}) 102 if cmap1_0 != nil { 103 // Otherwise, if the font contains a (1, 0) subtable, single bytes from the string are 104 // used to look up the associated glyph descriptions from the subtable. 105 iter := cmap1_0.Iter() 106 for iter.Next() { 107 code, gid := iter.Char() 108 if code > 255 { 109 continue 110 } 111 if GIDEncoding[code] == 0 { 112 GIDEncoding[code] = gid 113 } 114 } 115 } 116 } 117 118 simple := new(SimpleFont) 119 120 for i, gi := range GIDEncoding { 121 var g Glyph 122 g.Width = f.HorizontalAdvance(gi) * scale 123 124 gd := f.GlyphData(gi, ppem, ppem) 125 switch gd := gd.(type) { 126 case fonts.GlyphOutline: 127 g.Outlines = glyphOutline(gd, fm) 128 case fonts.GlyphSVG: 129 g.Outlines = glyphOutline(gd.Outline, fm) 130 case fonts.GlyphBitmap: 131 return nil, errors.New("bitmap fonts not supported") 132 } 133 simple.Glyphs[i] = g 134 } 135 136 return simple, nil 137 } 138 139 func getEncoding(e pdf.Value) (simpleencodings.Encoding, error) { 140 switch e.Kind() { 141 case pdf.Null: 142 return simpleencodings.Encoding{}, nil 143 144 case pdf.Name: 145 switch e.Name() { 146 case "MacRomanEncoding": 147 return simpleencodings.MacRoman, nil 148 case "MaxExpertEncoding": 149 return simpleencodings.MacExpert, nil 150 case "WinAnsiEncoding": 151 return simpleencodings.WinAnsi, nil 152 default: 153 return simpleencodings.Encoding{}, fmt.Errorf("unknown encoding: %v", e) 154 } 155 156 case pdf.Dict: 157 enc, err := getEncoding(e.Key("BaseEncoding")) 158 if err != nil { 159 return enc, err 160 } 161 diff := e.Key("Differences") 162 code := 0 163 for i := 0; i < diff.Len(); i++ { 164 item := diff.Index(i) 165 switch item.Kind() { 166 case pdf.Integer: 167 code = item.Int() - 1 168 case pdf.Name: 169 code++ 170 enc[code] = item.Name() 171 } 172 } 173 return enc, nil 174 175 default: 176 return simpleencodings.Encoding{}, fmt.Errorf("invalid encoding: %v", e) 177 } 178 } 179 180 // SimpleFontFromType1 converts a Type 1 font to a SimpleFont, using the 181 // encoding provided. 182 func SimpleFontFromType1(data []byte, enc simpleencodings.Encoding) (*SimpleFont, error) { 183 f, err := type1.Parse(bytes.NewReader(data)) 184 if err != nil { 185 return nil, err 186 } 187 188 nameToGID := map[string]fonts.GID{} 189 for i := fonts.GID(0); ; i++ { 190 name := f.GlyphName(i) 191 if name == "" { 192 break 193 } 194 nameToGID[name] = i 195 } 196 197 for i, name := range enc { 198 // Fill in the blanks with the font's builtin encoding. 199 if name == "" { 200 enc[i] = f.Encoding[i] 201 } 202 } 203 204 fm := f32.NewAffine2D(f.FontMatrix[0], f.FontMatrix[2], f.FontMatrix[4], f.FontMatrix[1], f.FontMatrix[3], f.FontMatrix[5]) 205 206 simple := new(SimpleFont) 207 for i, name := range enc { 208 var g Glyph 209 gi, ok := nameToGID[name] 210 if !ok { 211 continue 212 } 213 214 width := f.HorizontalAdvance(gi) 215 g.Width = fm.Transform(f32.Pt(width, 0)).X 216 217 gd := f.GlyphData(gi, 0, 0) 218 if gd == nil { 219 continue 220 } 221 g.Outlines = glyphOutline(gd.(fonts.GlyphOutline), fm) 222 simple.Glyphs[i] = g 223 } 224 225 return simple, nil 226 } 227 228 // SimpleFontFromCFF converts a CFF font to a SimpleFont, using the 229 // encoding provided. 230 func SimpleFontFromCFF(data []byte, enc simpleencodings.Encoding) (*SimpleFont, error) { 231 f, err := cff.Parse(bytes.NewReader(data)) 232 if err != nil { 233 return nil, err 234 } 235 236 nameToGID := map[string]fonts.GID{} 237 for i := fonts.GID(0); ; i++ { 238 name := f.GlyphName(i) 239 if name == "" { 240 break 241 } 242 nameToGID[name] = i 243 } 244 245 for i, name := range enc { 246 // Fill in the blanks with the font's builtin encoding. 247 if name == "" { 248 enc[i] = f.Encoding[i] 249 } 250 } 251 252 fm := f32.NewAffine2D(f.FontMatrix[0], f.FontMatrix[2], f.FontMatrix[4], f.FontMatrix[1], f.FontMatrix[3], f.FontMatrix[5]) 253 254 simple := new(SimpleFont) 255 for i, name := range enc { 256 var g Glyph 257 gi, ok := nameToGID[name] 258 if !ok { 259 continue 260 } 261 262 gd, err := f.LoadGlyph(gi) 263 if err != nil { 264 return nil, err 265 } 266 267 g.Width = fm.Transform(f32.Pt(float32(gd.Width), 0)).X 268 269 g.Outlines = glyphOutline(fonts.GlyphOutline{Segments: gd.Outlines}, fm) 270 simple.Glyphs[i] = g 271 } 272 273 return simple, nil 274 } 275 276 // glyphOutline converts outline to our path format, transforming the points 277 // with fm. 278 func glyphOutline(outline fonts.GlyphOutline, fm f32.Affine2D) []PathElement { 279 var p PathBuilder 280 for _, seg := range outline.Segments { 281 p0 := fm.Transform(f32.Point(seg.Args[0])) 282 p1 := fm.Transform(f32.Point(seg.Args[1])) 283 p2 := fm.Transform(f32.Point(seg.Args[2])) 284 switch seg.Op { 285 case fonts.SegmentOpMoveTo: 286 p.ClosePath() 287 p.MoveTo(p0.X, p0.Y) 288 case fonts.SegmentOpLineTo: 289 p.LineTo(p0.X, p0.Y) 290 case fonts.SegmentOpQuadTo: 291 p.QuadraticCurveTo(p0.X, p0.Y, p1.X, p1.Y) 292 case fonts.SegmentOpCubeTo: 293 p.CurveTo(p0.X, p0.Y, p1.X, p1.Y, p2.X, p2.Y) 294 } 295 } 296 p.ClosePath() 297 return p.Path 298 } 299 300 func importPDFFont(f pdf.Font) (font Font, err error) { 301 enc, err := getEncoding(f.V.Key("Encoding")) 302 if err != nil { 303 return nil, err 304 } 305 306 switch f.V.Key("Subtype").Name() { 307 case "TrueType": 308 file := f.V.Key("FontDescriptor").Key("FontFile2") 309 if file.IsNull() { 310 return nil, fmt.Errorf("%v does not have embedded font data", f.V.Key("BaseFont")) 311 } 312 data, err := io.ReadAll(file.Reader()) 313 if err != nil { 314 return nil, err 315 } 316 font, err = SimpleFontFromSFNT(data, enc) 317 318 case "Type1": 319 if f.V.Key("FontDescriptor").Key("FontFile3").Key("Subtype").Name() == "Type1C" { 320 file := f.V.Key("FontDescriptor").Key("FontFile3") 321 if file.IsNull() { 322 return nil, fmt.Errorf("%v does not have embedded font data", f.V.Key("BaseFont")) 323 } 324 data, err := io.ReadAll(file.Reader()) 325 if err != nil { 326 return nil, err 327 } 328 font, err = SimpleFontFromCFF(data, enc) 329 } else { 330 file := f.V.Key("FontDescriptor").Key("FontFile") 331 if file.IsNull() { 332 return nil, fmt.Errorf("%v does not have embedded font data", f.V.Key("BaseFont")) 333 } 334 data, err := io.ReadAll(file.Reader()) 335 if err != nil { 336 return nil, err 337 } 338 font, err = SimpleFontFromType1(data, enc) 339 } 340 341 default: 342 return nil, fmt.Errorf("%v is an unsupported font type (%v)", f.V.Key("BaseFont"), f.V.Key("Subtype")) 343 } 344 345 if err != nil { 346 return nil, err 347 } 348 349 // Glyph widths from the font dictionary override widths from the font itself. 350 if simple, ok := font.(*SimpleFont); ok { 351 firstChar := f.V.Key("FirstChar").Int() 352 lastChar := f.V.Key("LastChar").Int() 353 widths := f.V.Key("Widths") 354 355 for i := firstChar; i <= lastChar; i++ { 356 simple.Glyphs[i].Width = widths.Index(i-firstChar).Float32() / 1000 357 } 358 } 359 360 return font, nil 361 }