ttfparser.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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 parse TTF font files
  18. // Version: 1.0
  19. // Date: 2011-06-18
  20. // Author: Olivier PLATHEY
  21. // Port to Go: Kurt Jung, 2013-07-15
  22. import (
  23. "encoding/binary"
  24. "fmt"
  25. "os"
  26. "regexp"
  27. "strings"
  28. )
  29. // TtfType contains metrics of a TrueType font.
  30. type TtfType struct {
  31. Embeddable bool
  32. UnitsPerEm uint16
  33. PostScriptName string
  34. Bold bool
  35. ItalicAngle int16
  36. IsFixedPitch bool
  37. TypoAscender int16
  38. TypoDescender int16
  39. UnderlinePosition int16
  40. UnderlineThickness int16
  41. Xmin, Ymin, Xmax, Ymax int16
  42. CapHeight int16
  43. Widths []uint16
  44. Chars map[uint16]uint16
  45. }
  46. type ttfParser struct {
  47. rec TtfType
  48. f *os.File
  49. tables map[string]uint32
  50. numberOfHMetrics uint16
  51. numGlyphs uint16
  52. }
  53. // TtfParse extracts various metrics from a TrueType font file.
  54. func TtfParse(fileStr string) (TtfRec TtfType, err error) {
  55. var t ttfParser
  56. t.f, err = os.Open(fileStr)
  57. if err != nil {
  58. return
  59. }
  60. version, err := t.ReadStr(4)
  61. if err != nil {
  62. return
  63. }
  64. if version == "OTTO" {
  65. err = fmt.Errorf("fonts based on PostScript outlines are not supported")
  66. return
  67. }
  68. if version != "\x00\x01\x00\x00" {
  69. err = fmt.Errorf("unrecognized file format")
  70. return
  71. }
  72. numTables := int(t.ReadUShort())
  73. t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
  74. t.tables = make(map[string]uint32)
  75. var tag string
  76. for j := 0; j < numTables; j++ {
  77. tag, err = t.ReadStr(4)
  78. if err != nil {
  79. return
  80. }
  81. t.Skip(4) // checkSum
  82. offset := t.ReadULong()
  83. t.Skip(4) // length
  84. t.tables[tag] = offset
  85. }
  86. err = t.ParseComponents()
  87. if err != nil {
  88. return
  89. }
  90. t.f.Close()
  91. TtfRec = t.rec
  92. return
  93. }
  94. func (t *ttfParser) ParseComponents() (err error) {
  95. err = t.ParseHead()
  96. if err == nil {
  97. err = t.ParseHhea()
  98. if err == nil {
  99. err = t.ParseMaxp()
  100. if err == nil {
  101. err = t.ParseHmtx()
  102. if err == nil {
  103. err = t.ParseCmap()
  104. if err == nil {
  105. err = t.ParseName()
  106. if err == nil {
  107. err = t.ParseOS2()
  108. if err == nil {
  109. err = t.ParsePost()
  110. }
  111. }
  112. }
  113. }
  114. }
  115. }
  116. }
  117. return
  118. }
  119. func (t *ttfParser) ParseHead() (err error) {
  120. err = t.Seek("head")
  121. t.Skip(3 * 4) // version, fontRevision, checkSumAdjustment
  122. magicNumber := t.ReadULong()
  123. if magicNumber != 0x5F0F3CF5 {
  124. err = fmt.Errorf("incorrect magic number")
  125. return
  126. }
  127. t.Skip(2) // flags
  128. t.rec.UnitsPerEm = t.ReadUShort()
  129. t.Skip(2 * 8) // created, modified
  130. t.rec.Xmin = t.ReadShort()
  131. t.rec.Ymin = t.ReadShort()
  132. t.rec.Xmax = t.ReadShort()
  133. t.rec.Ymax = t.ReadShort()
  134. return
  135. }
  136. func (t *ttfParser) ParseHhea() (err error) {
  137. err = t.Seek("hhea")
  138. if err == nil {
  139. t.Skip(4 + 15*2)
  140. t.numberOfHMetrics = t.ReadUShort()
  141. }
  142. return
  143. }
  144. func (t *ttfParser) ParseMaxp() (err error) {
  145. err = t.Seek("maxp")
  146. if err == nil {
  147. t.Skip(4)
  148. t.numGlyphs = t.ReadUShort()
  149. }
  150. return
  151. }
  152. func (t *ttfParser) ParseHmtx() (err error) {
  153. err = t.Seek("hmtx")
  154. if err == nil {
  155. t.rec.Widths = make([]uint16, 0, 8)
  156. for j := uint16(0); j < t.numberOfHMetrics; j++ {
  157. t.rec.Widths = append(t.rec.Widths, t.ReadUShort())
  158. t.Skip(2) // lsb
  159. }
  160. if t.numberOfHMetrics < t.numGlyphs {
  161. lastWidth := t.rec.Widths[t.numberOfHMetrics-1]
  162. for j := t.numberOfHMetrics; j < t.numGlyphs; j++ {
  163. t.rec.Widths = append(t.rec.Widths, lastWidth)
  164. }
  165. }
  166. }
  167. return
  168. }
  169. func (t *ttfParser) ParseCmap() (err error) {
  170. var offset int64
  171. if err = t.Seek("cmap"); err != nil {
  172. return
  173. }
  174. t.Skip(2) // version
  175. numTables := int(t.ReadUShort())
  176. offset31 := int64(0)
  177. for j := 0; j < numTables; j++ {
  178. platformID := t.ReadUShort()
  179. encodingID := t.ReadUShort()
  180. offset = int64(t.ReadULong())
  181. if platformID == 3 && encodingID == 1 {
  182. offset31 = offset
  183. }
  184. }
  185. if offset31 == 0 {
  186. err = fmt.Errorf("no Unicode encoding found")
  187. return
  188. }
  189. startCount := make([]uint16, 0, 8)
  190. endCount := make([]uint16, 0, 8)
  191. idDelta := make([]int16, 0, 8)
  192. idRangeOffset := make([]uint16, 0, 8)
  193. t.rec.Chars = make(map[uint16]uint16)
  194. t.f.Seek(int64(t.tables["cmap"])+offset31, os.SEEK_SET)
  195. format := t.ReadUShort()
  196. if format != 4 {
  197. err = fmt.Errorf("unexpected subtable format: %d", format)
  198. return
  199. }
  200. t.Skip(2 * 2) // length, language
  201. segCount := int(t.ReadUShort() / 2)
  202. t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
  203. for j := 0; j < segCount; j++ {
  204. endCount = append(endCount, t.ReadUShort())
  205. }
  206. t.Skip(2) // reservedPad
  207. for j := 0; j < segCount; j++ {
  208. startCount = append(startCount, t.ReadUShort())
  209. }
  210. for j := 0; j < segCount; j++ {
  211. idDelta = append(idDelta, t.ReadShort())
  212. }
  213. offset, _ = t.f.Seek(int64(0), os.SEEK_CUR)
  214. for j := 0; j < segCount; j++ {
  215. idRangeOffset = append(idRangeOffset, t.ReadUShort())
  216. }
  217. for j := 0; j < segCount; j++ {
  218. c1 := startCount[j]
  219. c2 := endCount[j]
  220. d := idDelta[j]
  221. ro := idRangeOffset[j]
  222. if ro > 0 {
  223. t.f.Seek(offset+2*int64(j)+int64(ro), os.SEEK_SET)
  224. }
  225. for c := c1; c <= c2; c++ {
  226. if c == 0xFFFF {
  227. break
  228. }
  229. var gid int32
  230. if ro > 0 {
  231. gid = int32(t.ReadUShort())
  232. if gid > 0 {
  233. gid += int32(d)
  234. }
  235. } else {
  236. gid = int32(c) + int32(d)
  237. }
  238. if gid >= 65536 {
  239. gid -= 65536
  240. }
  241. if gid > 0 {
  242. t.rec.Chars[c] = uint16(gid)
  243. }
  244. }
  245. }
  246. return
  247. }
  248. func (t *ttfParser) ParseName() (err error) {
  249. err = t.Seek("name")
  250. if err == nil {
  251. tableOffset, _ := t.f.Seek(0, os.SEEK_CUR)
  252. t.rec.PostScriptName = ""
  253. t.Skip(2) // format
  254. count := t.ReadUShort()
  255. stringOffset := t.ReadUShort()
  256. for j := uint16(0); j < count && t.rec.PostScriptName == ""; j++ {
  257. t.Skip(3 * 2) // platformID, encodingID, languageID
  258. nameID := t.ReadUShort()
  259. length := t.ReadUShort()
  260. offset := t.ReadUShort()
  261. if nameID == 6 {
  262. // PostScript name
  263. t.f.Seek(int64(tableOffset)+int64(stringOffset)+int64(offset), os.SEEK_SET)
  264. var s string
  265. s, err = t.ReadStr(int(length))
  266. if err != nil {
  267. return
  268. }
  269. s = strings.Replace(s, "\x00", "", -1)
  270. var re *regexp.Regexp
  271. if re, err = regexp.Compile("[(){}<> /%[\\]]"); err != nil {
  272. return
  273. }
  274. t.rec.PostScriptName = re.ReplaceAllString(s, "")
  275. }
  276. }
  277. if t.rec.PostScriptName == "" {
  278. err = fmt.Errorf("the name PostScript was not found")
  279. }
  280. }
  281. return
  282. }
  283. func (t *ttfParser) ParseOS2() (err error) {
  284. err = t.Seek("OS/2")
  285. if err == nil {
  286. version := t.ReadUShort()
  287. t.Skip(3 * 2) // xAvgCharWidth, usWeightClass, usWidthClass
  288. fsType := t.ReadUShort()
  289. t.rec.Embeddable = (fsType != 2) && (fsType&0x200) == 0
  290. t.Skip(11*2 + 10 + 4*4 + 4)
  291. fsSelection := t.ReadUShort()
  292. t.rec.Bold = (fsSelection & 32) != 0
  293. t.Skip(2 * 2) // usFirstCharIndex, usLastCharIndex
  294. t.rec.TypoAscender = t.ReadShort()
  295. t.rec.TypoDescender = t.ReadShort()
  296. if version >= 2 {
  297. t.Skip(3*2 + 2*4 + 2)
  298. t.rec.CapHeight = t.ReadShort()
  299. } else {
  300. t.rec.CapHeight = 0
  301. }
  302. }
  303. return
  304. }
  305. func (t *ttfParser) ParsePost() (err error) {
  306. err = t.Seek("post")
  307. if err == nil {
  308. t.Skip(4) // version
  309. t.rec.ItalicAngle = t.ReadShort()
  310. t.Skip(2) // Skip decimal part
  311. t.rec.UnderlinePosition = t.ReadShort()
  312. t.rec.UnderlineThickness = t.ReadShort()
  313. t.rec.IsFixedPitch = t.ReadULong() != 0
  314. }
  315. return
  316. }
  317. func (t *ttfParser) Seek(tag string) (err error) {
  318. ofs, ok := t.tables[tag]
  319. if ok {
  320. t.f.Seek(int64(ofs), os.SEEK_SET)
  321. } else {
  322. err = fmt.Errorf("table not found: %s", tag)
  323. }
  324. return
  325. }
  326. func (t *ttfParser) Skip(n int) {
  327. t.f.Seek(int64(n), os.SEEK_CUR)
  328. }
  329. func (t *ttfParser) ReadStr(length int) (str string, err error) {
  330. var n int
  331. buf := make([]byte, length)
  332. n, err = t.f.Read(buf)
  333. if err == nil {
  334. if n == length {
  335. str = string(buf)
  336. } else {
  337. err = fmt.Errorf("unable to read %d bytes", length)
  338. }
  339. }
  340. return
  341. }
  342. func (t *ttfParser) ReadUShort() (val uint16) {
  343. binary.Read(t.f, binary.BigEndian, &val)
  344. return
  345. }
  346. func (t *ttfParser) ReadShort() (val int16) {
  347. binary.Read(t.f, binary.BigEndian, &val)
  348. return
  349. }
  350. func (t *ttfParser) ReadULong() (val uint32) {
  351. binary.Read(t.f, binary.BigEndian, &val)
  352. return
  353. }