font.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. /*
  2. * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. package gofpdf
  17. // Utility to generate font definition files
  18. // Version: 1.2
  19. // Date: 2011-06-18
  20. // Author: Olivier PLATHEY
  21. // Port to Go: Kurt Jung, 2013-07-15
  22. import (
  23. "bufio"
  24. "compress/zlib"
  25. "encoding/binary"
  26. "encoding/json"
  27. "fmt"
  28. "io"
  29. "io/ioutil"
  30. "os"
  31. "path/filepath"
  32. "strconv"
  33. "strings"
  34. )
  35. func baseNoExt(fileStr string) string {
  36. str := filepath.Base(fileStr)
  37. extLen := len(filepath.Ext(str))
  38. if extLen > 0 {
  39. str = str[:len(str)-extLen]
  40. }
  41. return str
  42. }
  43. func loadMap(encodingFileStr string) (encList encListType, err error) {
  44. // printf("Encoding file string [%s]\n", encodingFileStr)
  45. var f *os.File
  46. // f, err = os.Open(encodingFilepath(encodingFileStr))
  47. f, err = os.Open(encodingFileStr)
  48. if err == nil {
  49. defer f.Close()
  50. for j := range encList {
  51. encList[j].uv = -1
  52. encList[j].name = ".notdef"
  53. }
  54. scanner := bufio.NewScanner(f)
  55. var enc encType
  56. var pos int
  57. for scanner.Scan() {
  58. // "!3F U+003F question"
  59. _, err = fmt.Sscanf(scanner.Text(), "!%x U+%x %s", &pos, &enc.uv, &enc.name)
  60. if err == nil {
  61. if pos < 256 {
  62. encList[pos] = enc
  63. } else {
  64. err = fmt.Errorf("map position 0x%2X exceeds 0xFF", pos)
  65. return
  66. }
  67. } else {
  68. return
  69. }
  70. }
  71. if err = scanner.Err(); err != nil {
  72. return
  73. }
  74. }
  75. return
  76. }
  77. // getInfoFromTrueType returns information from a TrueType font
  78. func getInfoFromTrueType(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
  79. info.Widths = make([]int, 256)
  80. var ttf TtfType
  81. ttf, err = TtfParse(fileStr)
  82. if err != nil {
  83. return
  84. }
  85. if embed {
  86. if !ttf.Embeddable {
  87. err = fmt.Errorf("font license does not allow embedding")
  88. return
  89. }
  90. info.Data, err = ioutil.ReadFile(fileStr)
  91. if err != nil {
  92. return
  93. }
  94. info.OriginalSize = len(info.Data)
  95. }
  96. k := 1000.0 / float64(ttf.UnitsPerEm)
  97. info.FontName = ttf.PostScriptName
  98. info.Bold = ttf.Bold
  99. info.Desc.ItalicAngle = int(ttf.ItalicAngle)
  100. info.IsFixedPitch = ttf.IsFixedPitch
  101. info.Desc.Ascent = round(k * float64(ttf.TypoAscender))
  102. info.Desc.Descent = round(k * float64(ttf.TypoDescender))
  103. info.UnderlineThickness = round(k * float64(ttf.UnderlineThickness))
  104. info.UnderlinePosition = round(k * float64(ttf.UnderlinePosition))
  105. info.Desc.FontBBox = fontBoxType{
  106. round(k * float64(ttf.Xmin)),
  107. round(k * float64(ttf.Ymin)),
  108. round(k * float64(ttf.Xmax)),
  109. round(k * float64(ttf.Ymax)),
  110. }
  111. // printf("FontBBox\n")
  112. // dump(info.Desc.FontBBox)
  113. info.Desc.CapHeight = round(k * float64(ttf.CapHeight))
  114. info.Desc.MissingWidth = round(k * float64(ttf.Widths[0]))
  115. var wd int
  116. for j := 0; j < len(info.Widths); j++ {
  117. wd = info.Desc.MissingWidth
  118. if encList[j].name != ".notdef" {
  119. uv := encList[j].uv
  120. pos, ok := ttf.Chars[uint16(uv)]
  121. if ok {
  122. wd = round(k * float64(ttf.Widths[pos]))
  123. } else {
  124. fmt.Fprintf(msgWriter, "Character %s is missing\n", encList[j].name)
  125. }
  126. }
  127. info.Widths[j] = wd
  128. }
  129. // printf("getInfoFromTrueType/FontBBox\n")
  130. // dump(info.Desc.FontBBox)
  131. return
  132. }
  133. type segmentType struct {
  134. marker uint8
  135. tp uint8
  136. size uint32
  137. data []byte
  138. }
  139. func segmentRead(r io.Reader) (s segmentType, err error) {
  140. if err = binary.Read(r, binary.LittleEndian, &s.marker); err != nil {
  141. return
  142. }
  143. if s.marker != 128 {
  144. err = fmt.Errorf("font file is not a valid binary Type1")
  145. return
  146. }
  147. if err = binary.Read(r, binary.LittleEndian, &s.tp); err != nil {
  148. return
  149. }
  150. if err = binary.Read(r, binary.LittleEndian, &s.size); err != nil {
  151. return
  152. }
  153. s.data = make([]byte, s.size)
  154. _, err = r.Read(s.data)
  155. return
  156. }
  157. // -rw-r--r-- 1 root root 9532 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.afm
  158. // -rw-r--r-- 1 root root 37744 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.pfb
  159. // getInfoFromType1 return information from a Type1 font
  160. func getInfoFromType1(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
  161. info.Widths = make([]int, 256)
  162. if embed {
  163. var f *os.File
  164. f, err = os.Open(fileStr)
  165. if err != nil {
  166. return
  167. }
  168. defer f.Close()
  169. // Read first segment
  170. var s1, s2 segmentType
  171. s1, err = segmentRead(f)
  172. if err != nil {
  173. return
  174. }
  175. s2, err = segmentRead(f)
  176. if err != nil {
  177. return
  178. }
  179. info.Data = s1.data
  180. info.Data = append(info.Data, s2.data...)
  181. info.Size1 = s1.size
  182. info.Size2 = s2.size
  183. }
  184. afmFileStr := fileStr[0:len(fileStr)-3] + "afm"
  185. size, ok := fileSize(afmFileStr)
  186. if !ok {
  187. err = fmt.Errorf("font file (ATM) %s not found", afmFileStr)
  188. return
  189. } else if size == 0 {
  190. err = fmt.Errorf("font file (AFM) %s empty or not readable", afmFileStr)
  191. return
  192. }
  193. var f *os.File
  194. f, err = os.Open(afmFileStr)
  195. if err != nil {
  196. return
  197. }
  198. defer f.Close()
  199. scanner := bufio.NewScanner(f)
  200. var fields []string
  201. var wd int
  202. var wt, name string
  203. wdMap := make(map[string]int)
  204. for scanner.Scan() {
  205. fields = strings.Fields(strings.TrimSpace(scanner.Text()))
  206. // Comment Generated by FontForge 20080203
  207. // FontName Symbol
  208. // C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
  209. if len(fields) >= 2 {
  210. switch fields[0] {
  211. case "C":
  212. if wd, err = strconv.Atoi(fields[4]); err == nil {
  213. name = fields[7]
  214. wdMap[name] = wd
  215. }
  216. case "FontName":
  217. info.FontName = fields[1]
  218. case "Weight":
  219. wt = strings.ToLower(fields[1])
  220. case "ItalicAngle":
  221. info.Desc.ItalicAngle, err = strconv.Atoi(fields[1])
  222. case "Ascender":
  223. info.Desc.Ascent, err = strconv.Atoi(fields[1])
  224. case "Descender":
  225. info.Desc.Descent, err = strconv.Atoi(fields[1])
  226. case "UnderlineThickness":
  227. info.UnderlineThickness, err = strconv.Atoi(fields[1])
  228. case "UnderlinePosition":
  229. info.UnderlinePosition, err = strconv.Atoi(fields[1])
  230. case "IsFixedPitch":
  231. info.IsFixedPitch = fields[1] == "true"
  232. case "FontBBox":
  233. if info.Desc.FontBBox.Xmin, err = strconv.Atoi(fields[1]); err == nil {
  234. if info.Desc.FontBBox.Ymin, err = strconv.Atoi(fields[2]); err == nil {
  235. if info.Desc.FontBBox.Xmax, err = strconv.Atoi(fields[3]); err == nil {
  236. info.Desc.FontBBox.Ymax, err = strconv.Atoi(fields[4])
  237. }
  238. }
  239. }
  240. case "CapHeight":
  241. info.Desc.CapHeight, err = strconv.Atoi(fields[1])
  242. case "StdVW":
  243. info.Desc.StemV, err = strconv.Atoi(fields[1])
  244. }
  245. }
  246. if err != nil {
  247. return
  248. }
  249. }
  250. if err = scanner.Err(); err != nil {
  251. return
  252. }
  253. if info.FontName == "" {
  254. err = fmt.Errorf("the field FontName missing in AFM file %s", afmFileStr)
  255. return
  256. }
  257. info.Bold = wt == "bold" || wt == "black"
  258. var missingWd int
  259. missingWd, ok = wdMap[".notdef"]
  260. if ok {
  261. info.Desc.MissingWidth = missingWd
  262. }
  263. for j := 0; j < len(info.Widths); j++ {
  264. info.Widths[j] = info.Desc.MissingWidth
  265. }
  266. for j := 0; j < len(info.Widths); j++ {
  267. name = encList[j].name
  268. if name != ".notdef" {
  269. wd, ok = wdMap[name]
  270. if ok {
  271. info.Widths[j] = wd
  272. } else {
  273. fmt.Fprintf(msgWriter, "Character %s is missing\n", name)
  274. }
  275. }
  276. }
  277. // printf("getInfoFromType1/FontBBox\n")
  278. // dump(info.Desc.FontBBox)
  279. return
  280. }
  281. func makeFontDescriptor(info *fontInfoType) {
  282. if info.Desc.CapHeight == 0 {
  283. info.Desc.CapHeight = info.Desc.Ascent
  284. }
  285. info.Desc.Flags = 1 << 5
  286. if info.IsFixedPitch {
  287. info.Desc.Flags |= 1
  288. }
  289. if info.Desc.ItalicAngle != 0 {
  290. info.Desc.Flags |= 1 << 6
  291. }
  292. if info.Desc.StemV == 0 {
  293. if info.Bold {
  294. info.Desc.StemV = 120
  295. } else {
  296. info.Desc.StemV = 70
  297. }
  298. }
  299. // printf("makeFontDescriptor/FontBBox\n")
  300. // dump(info.Desc.FontBBox)
  301. }
  302. // makeFontEncoding builds differences from reference encoding
  303. func makeFontEncoding(encList encListType, refEncFileStr string) (diffStr string, err error) {
  304. var refList encListType
  305. if refList, err = loadMap(refEncFileStr); err != nil {
  306. return
  307. }
  308. var buf fmtBuffer
  309. last := 0
  310. for j := 32; j < 256; j++ {
  311. if encList[j].name != refList[j].name {
  312. if j != last+1 {
  313. buf.printf("%d ", j)
  314. }
  315. last = j
  316. buf.printf("/%s ", encList[j].name)
  317. }
  318. }
  319. diffStr = strings.TrimSpace(buf.String())
  320. return
  321. }
  322. func makeDefinitionFile(fileStr, tpStr, encodingFileStr string, embed bool, encList encListType, info fontInfoType) error {
  323. var err error
  324. var def fontDefType
  325. def.Tp = tpStr
  326. def.Name = info.FontName
  327. makeFontDescriptor(&info)
  328. def.Desc = info.Desc
  329. // printf("makeDefinitionFile/FontBBox\n")
  330. // dump(def.Desc.FontBBox)
  331. def.Up = info.UnderlinePosition
  332. def.Ut = info.UnderlineThickness
  333. def.Cw = info.Widths
  334. def.Enc = baseNoExt(encodingFileStr)
  335. // fmt.Printf("encodingFileStr [%s], def.Enc [%s]\n", encodingFileStr, def.Enc)
  336. // fmt.Printf("reference [%s]\n", filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
  337. def.Diff, err = makeFontEncoding(encList, filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
  338. if err != nil {
  339. return err
  340. }
  341. def.File = info.File
  342. def.Size1 = int(info.Size1)
  343. def.Size2 = int(info.Size2)
  344. def.OriginalSize = info.OriginalSize
  345. // printf("Font definition file [%s]\n", fileStr)
  346. var buf []byte
  347. buf, err = json.Marshal(def)
  348. if err != nil {
  349. return err
  350. }
  351. var f *os.File
  352. f, err = os.Create(fileStr)
  353. if err != nil {
  354. return err
  355. }
  356. defer f.Close()
  357. _, err = f.Write(buf)
  358. if err != nil {
  359. return err
  360. }
  361. err = f.Close()
  362. if err != nil {
  363. return err
  364. }
  365. return err
  366. }
  367. // MakeFont generates a font definition file in JSON format. A definition file
  368. // of this type is required to use non-core fonts in the PDF documents that
  369. // gofpdf generates. See the makefont utility in the gofpdf package for a
  370. // command line interface to this function.
  371. //
  372. // fontFileStr is the name of the TrueType file (extension .ttf), OpenType file
  373. // (extension .otf) or binary Type1 file (extension .pfb) from which to
  374. // generate a definition file. If an OpenType file is specified, it must be one
  375. // that is based on TrueType outlines, not PostScript outlines; this cannot be
  376. // determined from the file extension alone. If a Type1 file is specified, a
  377. // metric file with the same pathname except with the extension .afm must be
  378. // present.
  379. //
  380. // encodingFileStr is the name of the encoding file that corresponds to the
  381. // font.
  382. //
  383. // dstDirStr is the name of the directory in which to save the definition file
  384. // and, if embed is true, the compressed font file.
  385. //
  386. // msgWriter is the writer that is called to display messages throughout the
  387. // process. Use nil to turn off messages.
  388. //
  389. // embed is true if the font is to be embedded in the PDF files.
  390. func MakeFont(fontFileStr, encodingFileStr, dstDirStr string, msgWriter io.Writer, embed bool) error {
  391. if msgWriter == nil {
  392. msgWriter = ioutil.Discard
  393. }
  394. if !fileExist(fontFileStr) {
  395. return fmt.Errorf("font file not found: %s", fontFileStr)
  396. }
  397. extStr := strings.ToLower(fontFileStr[len(fontFileStr)-3:])
  398. // printf("Font file extension [%s]\n", extStr)
  399. var tpStr string
  400. switch extStr {
  401. case "ttf":
  402. fallthrough
  403. case "otf":
  404. tpStr = "TrueType"
  405. case "pfb":
  406. tpStr = "Type1"
  407. default:
  408. return fmt.Errorf("unrecognized font file extension: %s", extStr)
  409. }
  410. var info fontInfoType
  411. encList, err := loadMap(encodingFileStr)
  412. if err != nil {
  413. return err
  414. }
  415. // printf("Encoding table\n")
  416. // dump(encList)
  417. if tpStr == "TrueType" {
  418. info, err = getInfoFromTrueType(fontFileStr, msgWriter, embed, encList)
  419. if err != nil {
  420. return err
  421. }
  422. } else {
  423. info, err = getInfoFromType1(fontFileStr, msgWriter, embed, encList)
  424. if err != nil {
  425. return err
  426. }
  427. }
  428. baseStr := baseNoExt(fontFileStr)
  429. // fmt.Printf("Base [%s]\n", baseStr)
  430. if embed {
  431. var f *os.File
  432. info.File = baseStr + ".z"
  433. zFileStr := filepath.Join(dstDirStr, info.File)
  434. f, err = os.Create(zFileStr)
  435. if err != nil {
  436. return err
  437. }
  438. defer f.Close()
  439. cmp := zlib.NewWriter(f)
  440. _, err = cmp.Write(info.Data)
  441. if err != nil {
  442. return err
  443. }
  444. err = cmp.Close()
  445. if err != nil {
  446. return err
  447. }
  448. fmt.Fprintf(msgWriter, "Font file compressed: %s\n", zFileStr)
  449. }
  450. defFileStr := filepath.Join(dstDirStr, baseStr+".json")
  451. err = makeDefinitionFile(defFileStr, tpStr, encodingFileStr, embed, encList, info)
  452. if err != nil {
  453. return err
  454. }
  455. fmt.Fprintf(msgWriter, "Font definition file successfully generated: %s\n", defFileStr)
  456. return nil
  457. }