github.com/pdfcpu/pdfcpu@v0.11.1/pkg/font/install.go (about)

     1  /*
     2  Copyright 2019 The pdfcpu Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package font provides support for TrueType fonts.
    18  package font
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/binary"
    23  	"encoding/gob"
    24  	"fmt"
    25  	"io"
    26  	"os"
    27  	"path/filepath"
    28  	"reflect"
    29  	"sort"
    30  	"strings"
    31  	"unicode/utf16"
    32  
    33  	"github.com/pdfcpu/pdfcpu/pkg/log"
    34  	"github.com/pkg/errors"
    35  )
    36  
    37  const (
    38  	sfntVersionTrueType      = "\x00\x01\x00\x00"
    39  	sfntVersionTrueTypeApple = "true"
    40  	sfntVersionCFF           = "OTTO"
    41  	ttfHeadMagicNumber       = 0x5F0F3CF5
    42  	ttcTag                   = "ttcf"
    43  )
    44  
    45  type ttf struct {
    46  	PostscriptName     string            // name: NameID 6
    47  	Protected          bool              // OS/2: fsType
    48  	UnitsPerEm         int               // head: unitsPerEm
    49  	Ascent             int               // OS/2: sTypoAscender
    50  	Descent            int               // OS/2: sTypoDescender
    51  	CapHeight          int               // OS/2: sCapHeight
    52  	FirstChar          uint16            // OS/2: fsFirstCharIndex
    53  	LastChar           uint16            // OS/2: fsLastCharIndex
    54  	UnicodeRange       [4]uint32         // OS/2: Unicode Character Range
    55  	LLx, LLy, URx, URy float64           // head: xMin, yMin, xMax, yMax (fontbox)
    56  	ItalicAngle        float64           // post: italicAngle
    57  	FixedPitch         bool              // post: isFixedPitch
    58  	Bold               bool              // OS/2: usWeightClass == 7
    59  	HorMetricsCount    int               // hhea: numOfLongHorMetrics
    60  	GlyphCount         int               // maxp: numGlyphs
    61  	GlyphWidths        []int             // hmtx: fd.HorMetricsCount.advanceWidth
    62  	Chars              map[uint32]uint16 // cmap: Unicode character to glyph index
    63  	ToUnicode          map[uint16]uint32 // map glyph index to unicode character
    64  	Planes             map[int]bool      // used Unicode planes
    65  	FontFile           []byte
    66  }
    67  
    68  func (fd ttf) String() string {
    69  	return fmt.Sprintf(`
    70   PostscriptName = %s
    71        Protected = %t
    72       UnitsPerEm = %d
    73           Ascent = %d
    74          Descent = %d 
    75        CapHeight = %d
    76        FirstChar = %d
    77         LastChar = %d
    78  FontBoundingBox = (%.2f, %.2f, %.2f, %.2f)
    79      ItalicAngle = %.2f
    80       FixedPitch = %t
    81             Bold = %t
    82  HorMetricsCount = %d
    83       GlyphCount = %d`,
    84  		fd.PostscriptName,
    85  		fd.Protected,
    86  		fd.UnitsPerEm,
    87  		fd.Ascent,
    88  		fd.Descent,
    89  		fd.CapHeight,
    90  		fd.FirstChar,
    91  		fd.LastChar,
    92  		fd.LLx, fd.LLy, fd.URx, fd.URy,
    93  		fd.ItalicAngle,
    94  		fd.FixedPitch,
    95  		fd.Bold,
    96  		fd.HorMetricsCount,
    97  		fd.GlyphCount,
    98  	)
    99  }
   100  
   101  func (fd ttf) toPDFGlyphSpace(i int) int {
   102  	return i * 1000 / fd.UnitsPerEm
   103  }
   104  
   105  type myUint32 []uint32
   106  
   107  func (f myUint32) Len() int {
   108  	return len(f)
   109  }
   110  
   111  func (f myUint32) Less(i, j int) bool {
   112  	return f[i] < f[j]
   113  }
   114  
   115  func (f myUint32) Swap(i, j int) {
   116  	f[i], f[j] = f[j], f[i]
   117  }
   118  
   119  func (fd ttf) PrintChars() string {
   120  	var min = uint16(0xFFFF)
   121  	var max uint16
   122  	var sb strings.Builder
   123  	sb.WriteByte(0x0a)
   124  
   125  	keys := make(myUint32, 0, len(fd.Chars))
   126  	for k := range fd.Chars {
   127  		keys = append(keys, k)
   128  	}
   129  	sort.Sort(keys)
   130  
   131  	for _, c := range keys {
   132  		g := fd.Chars[c]
   133  		if g > max {
   134  			max = g
   135  		}
   136  		if g < min {
   137  			min = g
   138  		}
   139  		sb.WriteString(fmt.Sprintf("#%x -> #%x(%d)\n", c, g, g))
   140  	}
   141  	fmt.Printf("using glyphs[%08x,%08x] [%d,%d]\n", min, max, min, max)
   142  	fmt.Printf("using glyphs #%x - #%x (%d-%d)\n", min, max, min, max)
   143  	return sb.String()
   144  }
   145  
   146  type table struct {
   147  	chksum uint32
   148  	off    uint32
   149  	size   uint32
   150  	padded uint32
   151  	data   []byte
   152  }
   153  
   154  func (t table) uint16(off int) uint16 {
   155  	return binary.BigEndian.Uint16(t.data[off:])
   156  }
   157  
   158  func (t table) int16(off int) int16 {
   159  	return int16(t.uint16(off))
   160  }
   161  
   162  func (t table) uint32(off int) uint32 {
   163  	return binary.BigEndian.Uint32(t.data[off:])
   164  }
   165  
   166  func (t table) fixed32(off int) float64 {
   167  	return float64(t.uint32(off)) / 65536.0
   168  }
   169  
   170  func (t table) parseFontHeaderTable(fd *ttf) error {
   171  	// table "head"
   172  	magic := t.uint32(12)
   173  	if magic != ttfHeadMagicNumber {
   174  		return fmt.Errorf("parseHead: wrong magic number")
   175  	}
   176  
   177  	unitsPerEm := t.uint16(18)
   178  	//fmt.Printf("unitsPerEm: %d\n", unitsPerEm)
   179  	fd.UnitsPerEm = int(unitsPerEm)
   180  
   181  	llx := t.int16(36)
   182  	//fmt.Printf("llx: %d\n", llx)
   183  	fd.LLx = float64(fd.toPDFGlyphSpace(int(llx)))
   184  
   185  	lly := t.int16(38)
   186  	//fmt.Printf("lly: %d\n", lly)
   187  	fd.LLy = float64(fd.toPDFGlyphSpace(int(lly)))
   188  
   189  	urx := t.int16(40)
   190  	//fmt.Printf("urx: %d\n", urx)
   191  	fd.URx = float64(fd.toPDFGlyphSpace(int(urx)))
   192  
   193  	ury := t.int16(42)
   194  	//fmt.Printf("ury: %d\n", ury)
   195  	fd.URy = float64(fd.toPDFGlyphSpace(int(ury)))
   196  
   197  	return nil
   198  }
   199  
   200  func uint16ToBigEndianBytes(i uint16) []byte {
   201  	b := make([]byte, 2)
   202  	binary.BigEndian.PutUint16(b, i)
   203  	return b
   204  }
   205  
   206  func uint32ToBigEndianBytes(i uint32) []byte {
   207  	b := make([]byte, 4)
   208  	binary.BigEndian.PutUint32(b, i)
   209  	return b
   210  }
   211  
   212  func utf16BEToString(bb []byte) string {
   213  	buf := make([]uint16, len(bb)/2)
   214  	for i := 0; i < len(buf); i++ {
   215  		buf[i] = binary.BigEndian.Uint16(bb[2*i:])
   216  	}
   217  	return string(utf16.Decode(buf))
   218  }
   219  
   220  func (t table) parsePostScriptTable(fd *ttf) error {
   221  	// table "post"
   222  	italicAngle := t.fixed32(4)
   223  	//fmt.Printf("italicAngle: %2.2f\n", italicAngle)
   224  	fd.ItalicAngle = italicAngle
   225  
   226  	isFixedPitch := t.uint16(16)
   227  	//fmt.Printf("isFixedPitch: %t\n", isFixedPitch != 0)
   228  	fd.FixedPitch = isFixedPitch != 0
   229  
   230  	return nil
   231  }
   232  
   233  // func printUnicodeRange(off int, r uint32) {
   234  // 	for i := 0; i < 32; i++ {
   235  // 		if r&1 > 0 {
   236  // 			fmt.Printf("bit %d: on\n", off+i)
   237  // 		}
   238  // 		r >>= 1
   239  // 	}
   240  // }
   241  
   242  func (t table) parseWindowsMetricsTable(fd *ttf) error {
   243  	// table "OS/2"
   244  	version := t.uint16(0)
   245  	fsType := t.uint16(8)
   246  	fd.Protected = fsType&2 > 0
   247  	//fmt.Printf("protected: %t\n", fd.Protected)
   248  
   249  	uniCodeRange1 := t.uint32(42)
   250  	//fmt.Printf("uniCodeRange1: %032b\n", uniCodeRange1)
   251  	fd.UnicodeRange[0] = uniCodeRange1
   252  
   253  	uniCodeRange2 := t.uint32(46)
   254  	//fmt.Printf("uniCodeRange2: %032b\n", uniCodeRange2)
   255  	fd.UnicodeRange[1] = uniCodeRange2
   256  
   257  	uniCodeRange3 := t.uint32(50)
   258  	//fmt.Printf("uniCodeRange3: %032b\n", uniCodeRange3)
   259  	fd.UnicodeRange[2] = uniCodeRange3
   260  
   261  	uniCodeRange4 := t.uint32(54)
   262  	//fmt.Printf("uniCodeRange4: %032b\n", uniCodeRange4)
   263  	fd.UnicodeRange[3] = uniCodeRange4
   264  
   265  	// printUnicodeRange(0, uniCodeRange1)
   266  	// printUnicodeRange(32, uniCodeRange2)
   267  	// printUnicodeRange(64, uniCodeRange3)
   268  	// printUnicodeRange(96, uniCodeRange4)
   269  
   270  	sTypoAscender := t.int16(68)
   271  	fd.Ascent = fd.toPDFGlyphSpace(int(sTypoAscender))
   272  
   273  	sTypoDescender := t.int16(70)
   274  	fd.Descent = fd.toPDFGlyphSpace(int(sTypoDescender))
   275  
   276  	// sCapHeight: This field was defined in version 2 of the OS/2 table.
   277  	// sCapHeight = int16(0)
   278  	if version >= 2 {
   279  		sCapHeight := t.int16(88)
   280  		fd.CapHeight = fd.toPDFGlyphSpace(int(sCapHeight))
   281  	} else {
   282  		// TODO the value may be set equal to the top of the unscaled and unhinted glyph bounding box
   283  		// of the glyph encoded at U+0048 (LATIN CAPITAL LETTER H).
   284  		fd.CapHeight = fd.Ascent
   285  	}
   286  
   287  	fsSelection := t.uint16(62)
   288  	fd.Bold = fsSelection&0x40 > 0
   289  
   290  	fsFirstCharIndex := t.uint16(64)
   291  	fd.FirstChar = fsFirstCharIndex
   292  
   293  	fsLastCharIndex := t.uint16(66)
   294  	fd.LastChar = fsLastCharIndex
   295  
   296  	return nil
   297  }
   298  
   299  func (t table) parseNamingTable(fd *ttf) error {
   300  	// table "name"
   301  	count := int(t.uint16(2))
   302  	stringOffset := t.uint16(4)
   303  	var nameID uint16
   304  	baseOff := 6
   305  	for i := 0; i < count; i++ {
   306  		recOff := baseOff + i*12
   307  		pf := t.uint16(recOff)
   308  		enc := t.uint16(recOff + 2)
   309  		lang := t.uint16(recOff + 4)
   310  		nameID = t.uint16(recOff + 6)
   311  		l := t.uint16(recOff + 8)
   312  		o := t.uint16(recOff + 10)
   313  		soff := stringOffset + o
   314  		s := t.data[soff : soff+l]
   315  		if nameID == 6 {
   316  			if pf == 3 && enc == 1 && lang == 0x0409 {
   317  				fd.PostscriptName = utf16BEToString(s)
   318  				return nil
   319  			}
   320  			if pf == 1 && enc == 0 && lang == 0 {
   321  				fd.PostscriptName = string(s)
   322  				return nil
   323  			}
   324  		}
   325  	}
   326  
   327  	return errors.New("pdfcpu: unable to identify postscript name")
   328  }
   329  
   330  func (t table) parseHorizontalHeaderTable(fd *ttf) error {
   331  	// table "hhea"
   332  	ascent := t.int16(4)
   333  	//fmt.Printf("ascent: %d\n", ascent)
   334  	if fd.Ascent == 0 {
   335  		fd.Ascent = fd.toPDFGlyphSpace(int(ascent))
   336  	}
   337  
   338  	descent := t.int16(6)
   339  	//fmt.Printf("descent: %d\n", descent)
   340  	if fd.Descent == 0 {
   341  		fd.Descent = fd.toPDFGlyphSpace(int(descent))
   342  	}
   343  
   344  	//lineGap := t.int16(8)
   345  	//fmt.Printf("lineGap: %d\n", lineGap)
   346  
   347  	//advanceWidthMax := t.uint16(10)
   348  	//fmt.Printf("advanceWidthMax: %d\n", advanceWidthMax)
   349  
   350  	//minLeftSideBearing := t.int16(12)
   351  	//fmt.Printf("minLeftSideBearing: %d\n", minLeftSideBearing)
   352  
   353  	//minRightSideBearing := t.int16(14)
   354  	//fmt.Printf("minRightSideBearing: %d\n", minRightSideBearing)
   355  
   356  	//xMaxExtent := t.int16(16)
   357  	//fmt.Printf("xMaxExtent: %d\n", xMaxExtent)
   358  
   359  	numOfLongHorMetrics := t.uint16(34)
   360  	//fmt.Printf("numOfLongHorMetrics: %d\n", numOfLongHorMetrics)
   361  	fd.HorMetricsCount = int(numOfLongHorMetrics)
   362  
   363  	return nil
   364  }
   365  
   366  func (t table) parseMaximumProfile(fd *ttf) error {
   367  	// table "maxp"
   368  	numGlyphs := t.uint16(4)
   369  	fd.GlyphCount = int(numGlyphs)
   370  	return nil
   371  }
   372  
   373  func (t table) parseHorizontalMetricsTable(fd *ttf) error {
   374  	// table "hmtx"
   375  	fd.GlyphWidths = make([]int, fd.GlyphCount)
   376  
   377  	for i := 0; i < int(fd.HorMetricsCount); i++ {
   378  		fd.GlyphWidths[i] = fd.toPDFGlyphSpace(int(t.uint16(i * 4)))
   379  	}
   380  
   381  	for i := fd.HorMetricsCount; i < fd.GlyphCount; i++ {
   382  		fd.GlyphWidths[i] = fd.GlyphWidths[fd.HorMetricsCount-1]
   383  	}
   384  
   385  	return nil
   386  }
   387  
   388  func (t table) parseCMapFormat4(fd *ttf) error {
   389  	fd.Planes[0] = true
   390  	segCount := int(t.uint16(6) / 2)
   391  	endOff := 14
   392  	startOff := endOff + 2*segCount + 2
   393  	deltaOff := startOff + 2*segCount
   394  	rangeOff := deltaOff + 2*segCount
   395  
   396  	count := 0
   397  	for i := 0; i < segCount; i++ {
   398  		sc := t.uint16(startOff + i*2)
   399  		startCode := uint32(sc)
   400  		if fd.FirstChar == 0 {
   401  			fd.FirstChar = sc
   402  		}
   403  		ec := t.uint16(endOff + i*2)
   404  		endCode := uint32(ec)
   405  		if fd.LastChar == 0 {
   406  			fd.LastChar = ec
   407  		}
   408  		idDelta := uint32(t.uint16(deltaOff + i*2))
   409  		idRangeOff := int(t.uint16(rangeOff + i*2))
   410  		var v uint16
   411  		for c, j := startCode, 0; c <= endCode && c != 0xFFFF; c++ {
   412  			if idRangeOff > 0 {
   413  				v = t.uint16(rangeOff + i*2 + idRangeOff + j*2)
   414  			} else {
   415  				v = uint16(c + idDelta)
   416  			}
   417  			if gi := v; gi > 0 {
   418  				fd.Chars[c] = gi
   419  				fd.ToUnicode[gi] = c
   420  				count++
   421  			}
   422  			j++
   423  		}
   424  	}
   425  	return nil
   426  }
   427  
   428  func (t table) parseCMapFormat12(fd *ttf) error {
   429  	numGroups := int(t.uint32(12))
   430  	off := 16
   431  	count := 0
   432  	var (
   433  		lowestStartCode uint32
   434  		prevCode        uint32
   435  	)
   436  	for i := 0; i < numGroups; i++ {
   437  		base := off + i*12
   438  		startCode := t.uint32(base)
   439  		if lowestStartCode == 0 {
   440  			lowestStartCode = startCode
   441  			fd.Planes[int(lowestStartCode/0x10000)] = true
   442  		}
   443  		if startCode/0x10000 != prevCode/0x10000 {
   444  			fd.Planes[int(startCode/0x10000)] = true
   445  		}
   446  		endCode := t.uint32(base + 4)
   447  		if startCode != endCode {
   448  			if startCode/0x10000 != endCode/0x10000 {
   449  				fd.Planes[int(endCode/0x10000)] = true
   450  			}
   451  		}
   452  		prevCode = endCode
   453  		startGlyphID := uint16(t.uint32(base + 8))
   454  		for c, gi := startCode, startGlyphID; c <= endCode; c++ {
   455  			fd.Chars[c] = gi
   456  			fd.ToUnicode[gi] = c
   457  			gi++
   458  			count++
   459  		}
   460  	}
   461  	return nil
   462  }
   463  
   464  func (t table) parseCharToGlyphMappingTable(fd *ttf) error {
   465  	// table "cmap"
   466  
   467  	fd.Chars = map[uint32]uint16{}
   468  	fd.ToUnicode = map[uint16]uint32{}
   469  	fd.Planes = map[int]bool{}
   470  	tableCount := t.uint16(2)
   471  	baseOff := 4
   472  	var pf, enc, f uint16
   473  	m := map[string]table{}
   474  
   475  	for i := 0; i < int(tableCount); i++ {
   476  		off := baseOff + i*8
   477  		pf = t.uint16(off)
   478  		enc = t.uint16(off + 2)
   479  		o := t.uint32(off + 4)
   480  		f = t.uint16(int(o))
   481  		if f == 14 {
   482  			continue
   483  		}
   484  		l := uint32(t.uint16(int(o) + 2))
   485  		if f >= 8 {
   486  			l = t.uint32(int(o) + 4)
   487  		}
   488  		b := t.data[o : o+l]
   489  		t1 := table{off: o, size: uint32(l), data: b}
   490  		k := fmt.Sprintf("p%02d.e%02d.f%02d", pf, enc, f)
   491  		m[k] = t1
   492  	}
   493  
   494  	if t, ok := m["p00.e10.f12"]; ok {
   495  		return t.parseCMapFormat12(fd)
   496  	}
   497  	if t, ok := m["p00.e04.f12"]; ok {
   498  		return t.parseCMapFormat12(fd)
   499  	}
   500  	if t, ok := m["p03.e10.f12"]; ok {
   501  		return t.parseCMapFormat12(fd)
   502  	}
   503  	if t, ok := m["p00.e03.f04"]; ok {
   504  		return t.parseCMapFormat4(fd)
   505  	}
   506  	if t, ok := m["p03.e01.f04"]; ok {
   507  		return t.parseCMapFormat4(fd)
   508  	}
   509  
   510  	return fmt.Errorf("pdfcpu: unsupported cmap table")
   511  }
   512  
   513  func calcTableChecksum(tag string, b []byte) uint32 {
   514  	sum := uint32(0)
   515  	c := (len(b) + 3) / 4
   516  	for i := 0; i < c; i++ {
   517  		if tag == "head" && i == 2 {
   518  			continue
   519  		}
   520  		sum += binary.BigEndian.Uint32(b[i*4:])
   521  	}
   522  	return sum
   523  }
   524  
   525  func getNext32BitAlignedLength(i uint32) uint32 {
   526  	if i%4 > 0 {
   527  		return i + (4 - i%4)
   528  	}
   529  	return i
   530  }
   531  
   532  func headerAndTables(fn string, r io.ReaderAt, baseOff int64) ([]byte, map[string]*table, error) {
   533  	header := make([]byte, 12)
   534  	n, err := r.ReadAt(header, baseOff)
   535  	if err != nil {
   536  		return nil, nil, err
   537  	}
   538  	if n != 12 {
   539  		return nil, nil, fmt.Errorf("pdfcpu: corrupt ttf file: %s", fn)
   540  	}
   541  
   542  	st := string(header[:4])
   543  
   544  	if st == sfntVersionCFF {
   545  		return nil, nil, fmt.Errorf("pdfcpu: %s is based on OpenType CFF and unsupported at the moment :(", fn)
   546  	}
   547  
   548  	if st != sfntVersionTrueType && st != sfntVersionTrueTypeApple {
   549  		return nil, nil, fmt.Errorf("pdfcpu: unrecognized font format: %s", fn)
   550  	}
   551  
   552  	c := int(binary.BigEndian.Uint16(header[4:]))
   553  
   554  	b := make([]byte, c*16)
   555  	n, err = r.ReadAt(b, baseOff+12)
   556  	if err != nil {
   557  		return nil, nil, err
   558  	}
   559  	if n != c*16 {
   560  		return nil, nil, fmt.Errorf("pdfcpu: corrupt ttf file: %s", fn)
   561  	}
   562  
   563  	byteCount := uint32(12)
   564  	tables := map[string]*table{}
   565  
   566  	for j := 0; j < c; j++ {
   567  		off := j * 16
   568  		b1 := b[off : off+16]
   569  		tag := string(b1[:4])
   570  		chk := binary.BigEndian.Uint32(b1[4:])
   571  		o := binary.BigEndian.Uint32(b1[8:])
   572  		l := binary.BigEndian.Uint32(b1[12:])
   573  		ll := getNext32BitAlignedLength(l)
   574  		byteCount += ll
   575  		t := make([]byte, ll)
   576  		n, err = r.ReadAt(t, int64(o))
   577  		if err != nil {
   578  			return nil, nil, err
   579  		}
   580  		if n != int(ll) {
   581  			return nil, nil, fmt.Errorf("pdfcpu: corrupt table: %s", tag)
   582  		}
   583  		sum := calcTableChecksum(tag, t)
   584  		if sum != chk {
   585  			fmt.Printf("pdfcpu: fixing table<%s> checksum error; want:%d got:%d\n", tag, chk, sum)
   586  			chk = sum
   587  		}
   588  		tables[tag] = &table{chksum: chk, off: o, size: l, padded: ll, data: t}
   589  	}
   590  
   591  	return header, tables, nil
   592  }
   593  
   594  func parse(tags map[string]*table, tag string, fd *ttf) error {
   595  	t, found := tags[tag]
   596  	if !found {
   597  		// OS/2 is optional for True Type fonts.
   598  		if tag == "OS/2" {
   599  			return nil
   600  		}
   601  		return fmt.Errorf("pdfcpu: tag: %s unavailable", tag)
   602  	}
   603  	if t.data == nil {
   604  		return fmt.Errorf("pdfcpu: tag: %s no data", tag)
   605  	}
   606  
   607  	var err error
   608  
   609  	switch tag {
   610  	case "head":
   611  		err = t.parseFontHeaderTable(fd)
   612  	case "OS/2":
   613  		err = t.parseWindowsMetricsTable(fd)
   614  	case "post":
   615  		err = t.parsePostScriptTable(fd)
   616  	case "name":
   617  		err = t.parseNamingTable(fd)
   618  	case "hhea":
   619  		err = t.parseHorizontalHeaderTable(fd)
   620  	case "maxp":
   621  		err = t.parseMaximumProfile(fd)
   622  	case "hmtx":
   623  		err = t.parseHorizontalMetricsTable(fd)
   624  	case "cmap":
   625  		err = t.parseCharToGlyphMappingTable(fd)
   626  	}
   627  
   628  	return err
   629  }
   630  
   631  func writeGob(fileName string, fd ttf) error {
   632  	f, err := os.Create(fileName)
   633  	if err != nil {
   634  		return err
   635  	}
   636  	defer f.Close()
   637  	enc := gob.NewEncoder(f)
   638  	return enc.Encode(fd)
   639  }
   640  
   641  func readGob(fileName string, fd *ttf) error {
   642  	f, err := os.Open(fileName)
   643  	if err != nil {
   644  		return err
   645  	}
   646  	defer f.Close()
   647  	dec := gob.NewDecoder(f)
   648  	return dec.Decode(fd)
   649  }
   650  
   651  func installTrueTypeRep(fontDir, fontName string, header []byte, tables map[string]*table) error {
   652  	fd := ttf{}
   653  	//fmt.Println(fontName)
   654  	for _, v := range []string{"head", "OS/2", "post", "name", "hhea", "maxp", "hmtx", "cmap"} {
   655  		if err := parse(tables, v, &fd); err != nil {
   656  			return err
   657  		}
   658  	}
   659  
   660  	bb, err := createTTF(header, tables)
   661  	if err != nil {
   662  		return err
   663  	}
   664  	fd.FontFile = bb
   665  
   666  	if log.CLIEnabled() {
   667  		log.CLI.Println(fd.PostscriptName)
   668  	}
   669  
   670  	gobName := filepath.Join(fontDir, fd.PostscriptName+".gob")
   671  
   672  	// Write the populated ttf struct as gob.
   673  	if err := writeGob(gobName, fd); err != nil {
   674  		return err
   675  	}
   676  
   677  	// Read gob and double check integrity.
   678  	fdNew := ttf{}
   679  	if err := readGob(gobName, &fdNew); err != nil {
   680  		return err
   681  	}
   682  
   683  	if !reflect.DeepEqual(fd, fdNew) {
   684  		return errors.Errorf("pdfcpu: %s can't be installed", fontName)
   685  	}
   686  
   687  	return nil
   688  }
   689  
   690  // InstallTrueTypeCollection saves an internal representation of all fonts
   691  // contained in a TrueType collection to the pdfcpu config dir.
   692  func InstallTrueTypeCollection(fontDir, fn string) error {
   693  	f, err := os.Open(fn)
   694  	if err != nil {
   695  		return err
   696  	}
   697  	defer f.Close()
   698  
   699  	b := make([]byte, 12)
   700  	n, err := f.Read(b)
   701  	if err != nil {
   702  		return err
   703  	}
   704  	if n != 12 {
   705  		return fmt.Errorf("pdfcpu: corrupt ttc file: %s", fn)
   706  	}
   707  
   708  	if string(b[:4]) != ttcTag {
   709  		return fmt.Errorf("pdfcpu: corrupt ttc file: %s", fn)
   710  	}
   711  
   712  	c := int(binary.BigEndian.Uint32(b[8:]))
   713  
   714  	b = make([]byte, c*4)
   715  	n, err = f.ReadAt(b, 12)
   716  	if err != nil {
   717  		return err
   718  	}
   719  	if n != c*4 {
   720  		return fmt.Errorf("pdfcpu: corrupt ttc file: %s", fn)
   721  	}
   722  
   723  	// Process contained fonts.
   724  	for i := 0; i < c; i++ {
   725  		off := int64(binary.BigEndian.Uint32(b[i*4:]))
   726  		header, tables, err := headerAndTables(fn, f, off)
   727  		if err != nil {
   728  			return err
   729  		}
   730  		if err := installTrueTypeRep(fontDir, fn, header, tables); err != nil {
   731  			return err
   732  		}
   733  	}
   734  
   735  	return nil
   736  }
   737  
   738  // InstallTrueTypeFont saves an internal representation of TrueType font fontName to the pdfcpu config dir.
   739  func InstallTrueTypeFont(fontDir, fontName string) error {
   740  	f, err := os.Open(fontName)
   741  	if err != nil {
   742  		return err
   743  	}
   744  	defer f.Close()
   745  
   746  	header, tables, err := headerAndTables(fontName, f, 0)
   747  	if err != nil {
   748  		return err
   749  	}
   750  	return installTrueTypeRep(fontDir, fontName, header, tables)
   751  }
   752  
   753  // InstallFontFromBytes saves an internal representation of TrueType font fontName to the pdfcpu config dir.
   754  func InstallFontFromBytes(fontDir, fontName string, bb []byte) error {
   755  	rd := bytes.NewReader(bb)
   756  	header, tables, err := headerAndTables(fontName, rd, 0)
   757  	if err != nil {
   758  		return err
   759  	}
   760  	return installTrueTypeRep(fontDir, fontName, header, tables)
   761  }
   762  
   763  func ttfTables(tableCount int, bb []byte) (map[string]*table, error) {
   764  	tables := map[string]*table{}
   765  	b := bb[12:]
   766  	for j := 0; j < tableCount; j++ {
   767  		off := j * 16
   768  		b1 := b[off : off+16]
   769  		tag := string(b1[:4])
   770  		chksum := binary.BigEndian.Uint32(b1[4:])
   771  		o := binary.BigEndian.Uint32(b1[8:])
   772  		l := binary.BigEndian.Uint32(b1[12:])
   773  		ll := getNext32BitAlignedLength(l)
   774  		t := append([]byte(nil), bb[o:o+ll]...)
   775  		tables[tag] = &table{chksum: chksum, off: o, size: l, padded: ll, data: t}
   776  	}
   777  	return tables, nil
   778  }
   779  
   780  func glyfOffset(loca *table, gid, indexToLocFormat int) int {
   781  	if indexToLocFormat == 0 {
   782  		// short offsets
   783  		return 2 * int(loca.uint16(2*gid))
   784  	}
   785  	// 1 .. long offsets
   786  	return int(loca.uint32(4 * gid))
   787  }
   788  
   789  func writeGlyfOffset(buf *bytes.Buffer, off, indexToLocFormat int) {
   790  	var bb []byte
   791  	if indexToLocFormat == 0 {
   792  		// 0 .. short offsets
   793  		bb = uint16ToBigEndianBytes(uint16(off / 2))
   794  	} else {
   795  		// 1 .. long offsets
   796  		bb = uint32ToBigEndianBytes(uint32(off))
   797  	}
   798  	buf.Write(bb)
   799  }
   800  
   801  func pad(bb []byte) []byte {
   802  	i := len(bb) % 4
   803  	if i == 0 {
   804  		return bb
   805  	}
   806  	for j := 0; j < 4-i; j++ {
   807  		bb = append(bb, 0x00)
   808  	}
   809  	return bb
   810  }
   811  
   812  func glyphOffsets(gid int, locaFull, glyfsFull *table, numGlyphs, indexToLocFormat int) (int, int) {
   813  	offFrom := glyfOffset(locaFull, gid, indexToLocFormat)
   814  	var offThru int
   815  	if gid == numGlyphs {
   816  		offThru = int(glyfsFull.padded)
   817  	} else {
   818  		offThru = glyfOffset(locaFull, gid+1, indexToLocFormat)
   819  	}
   820  	return offFrom, offThru
   821  }
   822  
   823  func resolveCompoundGlyph(fontName string, bb []byte, usedGIDs map[uint16]bool,
   824  	locaFull, glyfsFull *table, numGlyphs, indexToLocFormat int) error {
   825  	last := false
   826  	for off := 10; !last; {
   827  		flags := binary.BigEndian.Uint16(bb[off:])
   828  		last = flags&0x20 == 0
   829  		wordArgs := flags&0x01 > 0
   830  
   831  		gid := binary.BigEndian.Uint16(bb[off+2:])
   832  
   833  		// Position behind arguments.
   834  		off += 6
   835  		if wordArgs {
   836  			off += 2
   837  		}
   838  
   839  		// Position behind transform.
   840  		if flags&0x08 > 0 {
   841  			off += 2
   842  		} else if flags&0x40 > 0 {
   843  			off += 4
   844  		} else if flags&0x80 > 0 {
   845  			off += 8
   846  		}
   847  
   848  		if _, ok := usedGIDs[gid]; ok {
   849  			// duplicate
   850  			continue
   851  		}
   852  
   853  		offFrom, offThru := glyphOffsets(int(gid), locaFull, glyfsFull, numGlyphs, indexToLocFormat)
   854  		if offThru < offFrom {
   855  			return errors.Errorf("pdfcpu: illegal glyfOffset for font: %s", fontName)
   856  		}
   857  		if offFrom == offThru {
   858  			// not available
   859  			continue
   860  		}
   861  
   862  		usedGIDs[gid] = true
   863  
   864  		cbb := glyfsFull.data[offFrom:offThru]
   865  		if cbb[0]&0x80 == 0 {
   866  			// simple
   867  			continue
   868  		}
   869  
   870  		if err := resolveCompoundGlyph(fontName, cbb, usedGIDs, locaFull, glyfsFull, numGlyphs, indexToLocFormat); err != nil {
   871  			return err
   872  		}
   873  	}
   874  	return nil
   875  }
   876  
   877  func resolveCompoundGlyphs(fontName string, usedGIDs map[uint16]bool, locaFull, glyfsFull *table, numGlyphs, indexToLocFormat int) error {
   878  	gids := make([]uint16, len(usedGIDs))
   879  	for k := range usedGIDs {
   880  		gids = append(gids, k)
   881  	}
   882  	for _, gid := range gids {
   883  		offFrom, offThru := glyphOffsets(int(gid), locaFull, glyfsFull, numGlyphs, indexToLocFormat)
   884  		if offThru < offFrom {
   885  			return errors.Errorf("pdfcpu: illegal glyfOffset for font: %s", fontName)
   886  		}
   887  		if offFrom == offThru {
   888  			continue
   889  		}
   890  		bb := glyfsFull.data[offFrom:offThru]
   891  		if bb[0]&0x80 == 0 {
   892  			// simple
   893  			continue
   894  		}
   895  		if err := resolveCompoundGlyph(fontName, bb, usedGIDs, locaFull, glyfsFull, numGlyphs, indexToLocFormat); err != nil {
   896  			return err
   897  		}
   898  	}
   899  	return nil
   900  }
   901  
   902  func glyfAndLoca(fontName string, tables map[string]*table, usedGIDs map[uint16]bool) error {
   903  	head, ok := tables["head"]
   904  	if !ok {
   905  		return errors.Errorf("pdfcpu: missing \"head\" table for font: %s", fontName)
   906  	}
   907  
   908  	maxp, ok := tables["maxp"]
   909  	if !ok {
   910  		return errors.Errorf("pdfcpu: missing \"maxp\" table for font: %s", fontName)
   911  	}
   912  
   913  	glyfsFull, ok := tables["glyf"]
   914  	if !ok {
   915  		return errors.Errorf("pdfcpu: missing \"glyf\" table for font: %s", fontName)
   916  	}
   917  
   918  	locaFull, ok := tables["loca"]
   919  	if !ok {
   920  		return errors.Errorf("pdfcpu: missing \"loca\" table for font: %s", fontName)
   921  	}
   922  
   923  	indexToLocFormat := int(head.uint16(50))
   924  	// 0 .. short offsets
   925  	// 1 .. long offsets
   926  	numGlyphs := int(maxp.uint16(4))
   927  
   928  	if err := resolveCompoundGlyphs(fontName, usedGIDs, locaFull, glyfsFull, numGlyphs, indexToLocFormat); err != nil {
   929  		return err
   930  	}
   931  
   932  	gids := make([]int, 0, len(usedGIDs)+1)
   933  	gids = append(gids, 0)
   934  	for gid := range usedGIDs {
   935  		gids = append(gids, int(gid))
   936  	}
   937  	sort.Ints(gids)
   938  
   939  	glyfBytes := []byte{}
   940  	var buf bytes.Buffer
   941  	off := 0
   942  	firstPendingGID := 0
   943  
   944  	for _, gid := range gids {
   945  		offFrom, offThru := glyphOffsets(gid, locaFull, glyfsFull, numGlyphs, indexToLocFormat)
   946  		if offThru < offFrom {
   947  			return errors.Errorf("pdfcpu: illegal glyfOffset for font: %s", fontName)
   948  		}
   949  		if offThru != offFrom {
   950  			// We have a glyph outline.
   951  			for i := 0; i < gid-firstPendingGID; i++ {
   952  				writeGlyfOffset(&buf, off, indexToLocFormat)
   953  			}
   954  			glyfBytes = append(glyfBytes, glyfsFull.data[offFrom:offThru]...)
   955  			writeGlyfOffset(&buf, off, indexToLocFormat)
   956  			off += offThru - offFrom
   957  			firstPendingGID = gid + 1
   958  		}
   959  	}
   960  	for i := 0; i <= numGlyphs-firstPendingGID; i++ {
   961  		writeGlyfOffset(&buf, off, indexToLocFormat)
   962  	}
   963  
   964  	bb := buf.Bytes()
   965  	locaFull.size = uint32(len(bb))
   966  	locaFull.data = pad(bb)
   967  	locaFull.padded = uint32(len(locaFull.data))
   968  
   969  	glyfsFull.size = uint32(len(glyfBytes))
   970  	glyfsFull.data = pad(glyfBytes)
   971  	glyfsFull.padded = uint32(len(glyfsFull.data))
   972  
   973  	return nil
   974  }
   975  
   976  func createTTF(header []byte, tables map[string]*table) ([]byte, error) {
   977  	tags := []string{}
   978  	for t := range tables {
   979  		tags = append(tags, t)
   980  	}
   981  	sort.Strings(tags)
   982  
   983  	buf := bytes.NewBuffer(header)
   984  	off := uint32(len(header) + len(tables)*16)
   985  	o := off
   986  	for _, tag := range tags {
   987  		t := tables[tag]
   988  		if _, err := buf.WriteString(tag); err != nil {
   989  			return nil, err
   990  		}
   991  		if tag == "loca" || tag == "glyf" {
   992  			t.chksum = calcTableChecksum(tag, t.data)
   993  		}
   994  		if _, err := buf.Write(uint32ToBigEndianBytes(t.chksum)); err != nil {
   995  			return nil, err
   996  		}
   997  		t.off = o
   998  		if _, err := buf.Write(uint32ToBigEndianBytes(t.off)); err != nil {
   999  			return nil, err
  1000  		}
  1001  		if _, err := buf.Write(uint32ToBigEndianBytes(t.size)); err != nil {
  1002  			return nil, err
  1003  		}
  1004  		o += t.padded
  1005  	}
  1006  
  1007  	for _, tag := range tags {
  1008  		t := tables[tag]
  1009  		n, err := buf.Write(t.data)
  1010  		if err != nil {
  1011  			return nil, err
  1012  		}
  1013  		if n != len(t.data) || n != int(t.padded) {
  1014  			return nil, errors.Errorf("pdfcpu: unable to write %s data\n", tag)
  1015  		}
  1016  	}
  1017  
  1018  	return buf.Bytes(), nil
  1019  }
  1020  
  1021  // Subset creates a new font file based on usedGIDs.
  1022  func Subset(fontName string, usedGIDs map[uint16]bool) ([]byte, error) {
  1023  	bb, err := Read(fontName)
  1024  	if err != nil {
  1025  		return nil, err
  1026  	}
  1027  
  1028  	header := bb[:12]
  1029  	tableCount := int(binary.BigEndian.Uint16(header[4:]))
  1030  	tables, err := ttfTables(tableCount, bb)
  1031  	if err != nil {
  1032  		return nil, err
  1033  	}
  1034  
  1035  	if err := glyfAndLoca(fontName, tables, usedGIDs); err != nil {
  1036  		return nil, err
  1037  	}
  1038  
  1039  	return createTTF(header, tables)
  1040  }