1
16
17 package gofpdf
18
19
20
21
22
23
24
25
26 import (
27 "bufio"
28 "compress/zlib"
29 "encoding/binary"
30 "encoding/json"
31 "fmt"
32 "io"
33 "io/ioutil"
34 "os"
35 "path/filepath"
36 "strconv"
37 "strings"
38 )
39
40 func baseNoExt(fileStr string) string {
41 str := filepath.Base(fileStr)
42 extLen := len(filepath.Ext(str))
43 if extLen > 0 {
44 str = str[:len(str)-extLen]
45 }
46 return str
47 }
48
49 func loadMap(encodingFileStr string) (encList encListType, err error) {
50
51 var f *os.File
52
53 f, err = os.Open(encodingFileStr)
54 if err == nil {
55 defer f.Close()
56 for j := range encList {
57 encList[j].uv = -1
58 encList[j].name = ".notdef"
59 }
60 scanner := bufio.NewScanner(f)
61 var enc encType
62 var pos int
63 for scanner.Scan() {
64
65 _, err = fmt.Sscanf(scanner.Text(), "!%x U+%x %s", &pos, &enc.uv, &enc.name)
66 if err == nil {
67 if pos < 256 {
68 encList[pos] = enc
69 } else {
70 err = fmt.Errorf("map position 0x%2X exceeds 0xFF", pos)
71 return
72 }
73 } else {
74 return
75 }
76 }
77 if err = scanner.Err(); err != nil {
78 return
79 }
80 }
81 return
82 }
83
84
85 func getInfoFromTrueType(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
86 info.Widths = make([]int, 256)
87 var ttf TtfType
88 ttf, err = TtfParse(fileStr)
89 if err != nil {
90 return
91 }
92 if embed {
93 if !ttf.Embeddable {
94 err = fmt.Errorf("font license does not allow embedding")
95 return
96 }
97 info.Data, err = ioutil.ReadFile(fileStr)
98 if err != nil {
99 return
100 }
101 info.OriginalSize = len(info.Data)
102 }
103 k := 1000.0 / float64(ttf.UnitsPerEm)
104 info.FontName = ttf.PostScriptName
105 info.Bold = ttf.Bold
106 info.Desc.ItalicAngle = int(ttf.ItalicAngle)
107 info.IsFixedPitch = ttf.IsFixedPitch
108 info.Desc.Ascent = round(k * float64(ttf.TypoAscender))
109 info.Desc.Descent = round(k * float64(ttf.TypoDescender))
110 info.UnderlineThickness = round(k * float64(ttf.UnderlineThickness))
111 info.UnderlinePosition = round(k * float64(ttf.UnderlinePosition))
112 info.Desc.FontBBox = fontBoxType{
113 round(k * float64(ttf.Xmin)),
114 round(k * float64(ttf.Ymin)),
115 round(k * float64(ttf.Xmax)),
116 round(k * float64(ttf.Ymax)),
117 }
118
119
120 info.Desc.CapHeight = round(k * float64(ttf.CapHeight))
121 info.Desc.MissingWidth = round(k * float64(ttf.Widths[0]))
122 var wd int
123 for j := 0; j < len(info.Widths); j++ {
124 wd = info.Desc.MissingWidth
125 if encList[j].name != ".notdef" {
126 uv := encList[j].uv
127 pos, ok := ttf.Chars[uint16(uv)]
128 if ok {
129 wd = round(k * float64(ttf.Widths[pos]))
130 } else {
131 fmt.Fprintf(msgWriter, "Character %s is missing\n", encList[j].name)
132 }
133 }
134 info.Widths[j] = wd
135 }
136
137
138 return
139 }
140
141 type segmentType struct {
142 marker uint8
143 tp uint8
144 size uint32
145 data []byte
146 }
147
148 func segmentRead(r io.Reader) (s segmentType, err error) {
149 if err = binary.Read(r, binary.LittleEndian, &s.marker); err != nil {
150 return
151 }
152 if s.marker != 128 {
153 err = fmt.Errorf("font file is not a valid binary Type1")
154 return
155 }
156 if err = binary.Read(r, binary.LittleEndian, &s.tp); err != nil {
157 return
158 }
159 if err = binary.Read(r, binary.LittleEndian, &s.size); err != nil {
160 return
161 }
162 s.data = make([]byte, s.size)
163 _, err = r.Read(s.data)
164 return
165 }
166
167
168
169
170
171 func getInfoFromType1(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
172 info.Widths = make([]int, 256)
173 if embed {
174 var f *os.File
175 f, err = os.Open(fileStr)
176 if err != nil {
177 return
178 }
179 defer f.Close()
180
181 var s1, s2 segmentType
182 s1, err = segmentRead(f)
183 if err != nil {
184 return
185 }
186 s2, err = segmentRead(f)
187 if err != nil {
188 return
189 }
190 info.Data = s1.data
191 info.Data = append(info.Data, s2.data...)
192 info.Size1 = s1.size
193 info.Size2 = s2.size
194 }
195 afmFileStr := fileStr[0:len(fileStr)-3] + "afm"
196 size, ok := fileSize(afmFileStr)
197 if !ok {
198 err = fmt.Errorf("font file (ATM) %s not found", afmFileStr)
199 return
200 } else if size == 0 {
201 err = fmt.Errorf("font file (AFM) %s empty or not readable", afmFileStr)
202 return
203 }
204 var f *os.File
205 f, err = os.Open(afmFileStr)
206 if err != nil {
207 return
208 }
209 defer f.Close()
210 scanner := bufio.NewScanner(f)
211 var fields []string
212 var wd int
213 var wt, name string
214 wdMap := make(map[string]int)
215 for scanner.Scan() {
216 fields = strings.Fields(strings.TrimSpace(scanner.Text()))
217
218
219
220 if len(fields) >= 2 {
221 switch fields[0] {
222 case "C":
223 if wd, err = strconv.Atoi(fields[4]); err == nil {
224 name = fields[7]
225 wdMap[name] = wd
226 }
227 case "FontName":
228 info.FontName = fields[1]
229 case "Weight":
230 wt = strings.ToLower(fields[1])
231 case "ItalicAngle":
232 info.Desc.ItalicAngle, err = strconv.Atoi(fields[1])
233 case "Ascender":
234 info.Desc.Ascent, err = strconv.Atoi(fields[1])
235 case "Descender":
236 info.Desc.Descent, err = strconv.Atoi(fields[1])
237 case "UnderlineThickness":
238 info.UnderlineThickness, err = strconv.Atoi(fields[1])
239 case "UnderlinePosition":
240 info.UnderlinePosition, err = strconv.Atoi(fields[1])
241 case "IsFixedPitch":
242 info.IsFixedPitch = fields[1] == "true"
243 case "FontBBox":
244 if info.Desc.FontBBox.Xmin, err = strconv.Atoi(fields[1]); err == nil {
245 if info.Desc.FontBBox.Ymin, err = strconv.Atoi(fields[2]); err == nil {
246 if info.Desc.FontBBox.Xmax, err = strconv.Atoi(fields[3]); err == nil {
247 info.Desc.FontBBox.Ymax, err = strconv.Atoi(fields[4])
248 }
249 }
250 }
251 case "CapHeight":
252 info.Desc.CapHeight, err = strconv.Atoi(fields[1])
253 case "StdVW":
254 info.Desc.StemV, err = strconv.Atoi(fields[1])
255 }
256 }
257 if err != nil {
258 return
259 }
260 }
261 if err = scanner.Err(); err != nil {
262 return
263 }
264 if info.FontName == "" {
265 err = fmt.Errorf("the field FontName missing in AFM file %s", afmFileStr)
266 return
267 }
268 info.Bold = wt == "bold" || wt == "black"
269 var missingWd int
270 missingWd, ok = wdMap[".notdef"]
271 if ok {
272 info.Desc.MissingWidth = missingWd
273 }
274 for j := 0; j < len(info.Widths); j++ {
275 info.Widths[j] = info.Desc.MissingWidth
276 }
277 for j := 0; j < len(info.Widths); j++ {
278 name = encList[j].name
279 if name != ".notdef" {
280 wd, ok = wdMap[name]
281 if ok {
282 info.Widths[j] = wd
283 } else {
284 fmt.Fprintf(msgWriter, "Character %s is missing\n", name)
285 }
286 }
287 }
288
289
290 return
291 }
292
293 func makeFontDescriptor(info *fontInfoType) {
294 if info.Desc.CapHeight == 0 {
295 info.Desc.CapHeight = info.Desc.Ascent
296 }
297 info.Desc.Flags = 1 << 5
298 if info.IsFixedPitch {
299 info.Desc.Flags |= 1
300 }
301 if info.Desc.ItalicAngle != 0 {
302 info.Desc.Flags |= 1 << 6
303 }
304 if info.Desc.StemV == 0 {
305 if info.Bold {
306 info.Desc.StemV = 120
307 } else {
308 info.Desc.StemV = 70
309 }
310 }
311
312
313 }
314
315
316 func makeFontEncoding(encList encListType, refEncFileStr string) (diffStr string, err error) {
317 var refList encListType
318 if refList, err = loadMap(refEncFileStr); err != nil {
319 return
320 }
321 var buf fmtBuffer
322 last := 0
323 for j := 32; j < 256; j++ {
324 if encList[j].name != refList[j].name {
325 if j != last+1 {
326 buf.printf("%d ", j)
327 }
328 last = j
329 buf.printf("/%s ", encList[j].name)
330 }
331 }
332 diffStr = strings.TrimSpace(buf.String())
333 return
334 }
335
336 func makeDefinitionFile(fileStr, tpStr, encodingFileStr string, embed bool, encList encListType, info fontInfoType) error {
337 var err error
338 var def fontDefType
339 def.Tp = tpStr
340 def.Name = info.FontName
341 makeFontDescriptor(&info)
342 def.Desc = info.Desc
343
344
345 def.Up = info.UnderlinePosition
346 def.Ut = info.UnderlineThickness
347 def.Cw = info.Widths
348 def.Enc = baseNoExt(encodingFileStr)
349
350
351 def.Diff, err = makeFontEncoding(encList, filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
352 if err != nil {
353 return err
354 }
355 def.File = info.File
356 def.Size1 = int(info.Size1)
357 def.Size2 = int(info.Size2)
358 def.OriginalSize = info.OriginalSize
359
360 var buf []byte
361 buf, err = json.Marshal(def)
362 if err != nil {
363 return err
364 }
365 var f *os.File
366 f, err = os.Create(fileStr)
367 if err != nil {
368 return err
369 }
370 defer f.Close()
371 _, err = f.Write(buf)
372 if err != nil {
373 return err
374 }
375 err = f.Close()
376 if err != nil {
377 return err
378 }
379
380 return err
381 }
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406 func MakeFont(fontFileStr, encodingFileStr, dstDirStr string, msgWriter io.Writer, embed bool) error {
407 if msgWriter == nil {
408 msgWriter = ioutil.Discard
409 }
410 if !fileExist(fontFileStr) {
411 return fmt.Errorf("font file not found: %s", fontFileStr)
412 }
413 extStr := strings.ToLower(fontFileStr[len(fontFileStr)-3:])
414
415 var tpStr string
416 switch extStr {
417 case "ttf":
418 fallthrough
419 case "otf":
420 tpStr = "TrueType"
421 case "pfb":
422 tpStr = "Type1"
423 default:
424 return fmt.Errorf("unrecognized font file extension: %s", extStr)
425 }
426
427 var info fontInfoType
428 encList, err := loadMap(encodingFileStr)
429 if err != nil {
430 return err
431 }
432
433
434 if tpStr == "TrueType" {
435 info, err = getInfoFromTrueType(fontFileStr, msgWriter, embed, encList)
436 if err != nil {
437 return err
438 }
439 } else {
440 info, err = getInfoFromType1(fontFileStr, msgWriter, embed, encList)
441 if err != nil {
442 return err
443 }
444 }
445 baseStr := baseNoExt(fontFileStr)
446
447 if embed {
448 var f *os.File
449 info.File = baseStr + ".z"
450 zFileStr := filepath.Join(dstDirStr, info.File)
451 f, err = os.Create(zFileStr)
452 if err != nil {
453 return err
454 }
455 defer f.Close()
456 cmp := zlib.NewWriter(f)
457 _, err = cmp.Write(info.Data)
458 if err != nil {
459 return err
460 }
461 err = cmp.Close()
462 if err != nil {
463 return err
464 }
465 fmt.Fprintf(msgWriter, "Font file compressed: %s\n", zFileStr)
466 }
467 defFileStr := filepath.Join(dstDirStr, baseStr+".json")
468 err = makeDefinitionFile(defFileStr, tpStr, encodingFileStr, embed, encList, info)
469 if err != nil {
470 return err
471 }
472 fmt.Fprintf(msgWriter, "Font definition file successfully generated: %s\n", defFileStr)
473 return nil
474 }
475
View as plain text