...

Source file src/oss.terrastruct.com/d2/lib/font/font.go

Documentation: oss.terrastruct.com/d2/lib/font

     1  // Sfnt2Woff is a native go port of the JS library
     2  // https://github.com/fontello/ttf2woff
     3  // that converts sfnt fonts (.ttf and .otf) to .woff fonts
     4  
     5  package font
     6  
     7  import (
     8  	"bytes"
     9  	"compress/zlib"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"math"
    13  	"sort"
    14  )
    15  
    16  var (
    17  	// sfnt2Woff offset
    18  	SFNT_OFFSET_TAG      = 0
    19  	SFNT_OFFSET_CHECKSUM = 4
    20  	SFNT_OFFSET_OFFSET   = 8
    21  	SFNT_OFFSET_LENGTH   = 12
    22  
    23  	// sfnt2Woff entry offset
    24  	SFNT_ENTRY_OFFSET_FLAVOR              = 0
    25  	SFNT_ENTRY_OFFSET_VERSION_MAJ         = 4
    26  	SFNT_ENTRY_OFFSET_VERSION_MIN         = 6
    27  	SFNT_ENTRY_OFFSET_CHECKSUM_ADJUSTMENT = 8
    28  
    29  	// woff offset
    30  	WOFF_OFFSET_MAGIC            = 0
    31  	WOFF_OFFSET_FLAVOR           = 4
    32  	WOFF_OFFSET_SIZE             = 8
    33  	WOFF_OFFSET_NUM_TABLES       = 12
    34  	WOFF_OFFSET_RESERVED         = 14
    35  	WOFF_OFFSET_SFNT_SIZE        = 16
    36  	WOFF_OFFSET_VERSION_MAJ      = 20
    37  	WOFF_OFFSET_VERSION_MIN      = 22
    38  	WOFF_OFFSET_META_OFFSET      = 24
    39  	WOFF_OFFSET_META_LENGTH      = 28
    40  	WOFF_OFFSET_META_ORIG_LENGTH = 32
    41  	WOFF_OFFSET_PRIV_OFFSET      = 36
    42  	WOFF_OFFSET_PRIV_LENGTH      = 40
    43  
    44  	// woff entry offset
    45  	WOFF_ENTRY_OFFSET_TAG          = 0
    46  	WOFF_ENTRY_OFFSET_OFFSET       = 4
    47  	WOFF_ENTRY_OFFSET_COMPR_LENGTH = 8
    48  	WOFF_ENTRY_OFFSET_LENGTH       = 12
    49  	WOFF_ENTRY_OFFSET_CHECKSUM     = 16
    50  
    51  	// magic
    52  	MAGIC_WOFF                uint32 = 0x774f4646
    53  	MAGIC_CHECKSUM_ADJUSTMENT uint32 = 0xb1b0afba
    54  
    55  	// sizes
    56  	SIZE_OF_WOFF_HEADER      = 44
    57  	SIZE_OF_WOFF_ENTRY       = 20
    58  	SIZE_OF_SFNT_HEADER      = 12
    59  	SIZE_OF_SFNT_TABLE_ENTRY = 16
    60  )
    61  
    62  type TableEntry struct {
    63  	Tag      []byte
    64  	CheckSum uint32
    65  	Offset   uint32
    66  	Length   uint32
    67  }
    68  
    69  func longAlign(n uint32) uint32 {
    70  	return (n + 3) & ^uint32(3)
    71  }
    72  
    73  func calcChecksum(buf []byte) uint32 {
    74  	var sum uint32 = 0
    75  	var nlongs = len(buf) / 4
    76  
    77  	for i := 0; i < nlongs; i++ {
    78  		var t = binary.BigEndian.Uint32(buf[i*4:])
    79  		sum = sum + t
    80  	}
    81  	return sum
    82  }
    83  
    84  func Sfnt2Woff(fontBuf []byte) ([]byte, error) {
    85  	numTables := binary.BigEndian.Uint16(fontBuf[4:])
    86  
    87  	woffHeader := make([]byte, SIZE_OF_WOFF_HEADER)
    88  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_MAGIC:], MAGIC_WOFF)
    89  	binary.BigEndian.PutUint16(woffHeader[WOFF_OFFSET_NUM_TABLES:], numTables)
    90  	binary.BigEndian.PutUint16(woffHeader[WOFF_OFFSET_SFNT_SIZE:], 0)
    91  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_META_OFFSET:], 0)
    92  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_META_LENGTH:], 0)
    93  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_META_ORIG_LENGTH:], 0)
    94  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_PRIV_OFFSET:], 0)
    95  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_PRIV_LENGTH:], 0)
    96  
    97  	var entries []TableEntry
    98  
    99  	for i := 0; i < int(numTables); i++ {
   100  		table := fontBuf[SIZE_OF_SFNT_HEADER+i*SIZE_OF_SFNT_TABLE_ENTRY:]
   101  
   102  		entry := TableEntry{
   103  			Tag:      table[SFNT_OFFSET_TAG : SFNT_OFFSET_TAG+4],
   104  			CheckSum: binary.BigEndian.Uint32(table[SFNT_OFFSET_CHECKSUM:]),
   105  			Offset:   binary.BigEndian.Uint32(table[SFNT_OFFSET_OFFSET:]),
   106  			Length:   binary.BigEndian.Uint32(table[SFNT_OFFSET_LENGTH:]),
   107  		}
   108  
   109  		entries = append(entries, entry)
   110  	}
   111  	sort.Slice(entries, func(i, j int) bool {
   112  		return string(entries[i].Tag) < string(entries[j].Tag)
   113  	})
   114  
   115  	sfntSize := uint32(SIZE_OF_SFNT_HEADER + int(numTables)*SIZE_OF_SFNT_TABLE_ENTRY)
   116  	tableInfo := make([]byte, int(numTables)*SIZE_OF_WOFF_ENTRY)
   117  
   118  	for i := 0; i < len(entries); i++ {
   119  		tableEntry := entries[i]
   120  		if string(tableEntry.Tag) != "head" {
   121  			alignTable := fontBuf[tableEntry.Offset : tableEntry.Offset+longAlign(tableEntry.Length)]
   122  
   123  			if calcChecksum(alignTable) != tableEntry.CheckSum {
   124  				return nil, fmt.Errorf("checksum error in table: %v", string(tableEntry.Tag))
   125  			}
   126  		}
   127  
   128  		binary.BigEndian.PutUint32(tableInfo[i*SIZE_OF_WOFF_ENTRY+WOFF_ENTRY_OFFSET_TAG:], binary.BigEndian.Uint32(tableEntry.Tag))
   129  		binary.BigEndian.PutUint32(tableInfo[i*SIZE_OF_WOFF_ENTRY+WOFF_ENTRY_OFFSET_LENGTH:], tableEntry.Length)
   130  		binary.BigEndian.PutUint32(tableInfo[i*SIZE_OF_WOFF_ENTRY+WOFF_ENTRY_OFFSET_CHECKSUM:], tableEntry.CheckSum)
   131  
   132  		sfntSize += longAlign(tableEntry.Length)
   133  	}
   134  
   135  	sfntOffset := uint32(SIZE_OF_SFNT_HEADER + len(entries)*SIZE_OF_SFNT_TABLE_ENTRY)
   136  	csum := calcChecksum(fontBuf[:SIZE_OF_SFNT_HEADER])
   137  	for i := 0; i < len(entries); i++ {
   138  		tableEntry := entries[i]
   139  
   140  		b := make([]byte, SIZE_OF_SFNT_TABLE_ENTRY)
   141  		binary.BigEndian.PutUint32(b[SFNT_OFFSET_TAG:], binary.BigEndian.Uint32(tableEntry.Tag))
   142  		binary.BigEndian.PutUint32(b[SFNT_OFFSET_CHECKSUM:], tableEntry.CheckSum)
   143  		binary.BigEndian.PutUint32(b[SFNT_OFFSET_OFFSET:], sfntOffset)
   144  		binary.BigEndian.PutUint32(b[SFNT_OFFSET_LENGTH:], tableEntry.Length)
   145  
   146  		sfntOffset += longAlign(tableEntry.Length)
   147  		csum += calcChecksum(b)
   148  		csum += tableEntry.CheckSum
   149  	}
   150  
   151  	var checksumAdjustment = MAGIC_CHECKSUM_ADJUSTMENT - csum
   152  
   153  	majorVersion := uint16(0)
   154  	minVersion := uint16(1)
   155  	flavor := uint32(0)
   156  	offset := SIZE_OF_WOFF_HEADER + int(numTables)*SIZE_OF_WOFF_ENTRY
   157  	var tableBytes []byte
   158  
   159  	for i := 0; i < len(entries); i++ {
   160  		tableEntry := entries[i]
   161  
   162  		sfntData := fontBuf[tableEntry.Offset : tableEntry.Offset+tableEntry.Length]
   163  		if string(tableEntry.Tag) == "head" {
   164  			majorVersion = binary.BigEndian.Uint16(sfntData[SFNT_ENTRY_OFFSET_VERSION_MAJ:])
   165  			minVersion = binary.BigEndian.Uint16(sfntData[SFNT_ENTRY_OFFSET_VERSION_MIN:])
   166  			flavor = binary.BigEndian.Uint32(sfntData[SFNT_ENTRY_OFFSET_FLAVOR:])
   167  			binary.BigEndian.PutUint32(sfntData[SFNT_ENTRY_OFFSET_CHECKSUM_ADJUSTMENT:], uint32(checksumAdjustment))
   168  		}
   169  
   170  		var res bytes.Buffer
   171  		w := zlib.NewWriter(&res)
   172  		w.Write(sfntData)
   173  		w.Flush()
   174  		w.Close()
   175  
   176  		compLength := math.Min(float64(len(res.Bytes())), float64(len(sfntData)))
   177  		length := longAlign(uint32(compLength))
   178  
   179  		table := make([]byte, length)
   180  		// only deflate data if the deflated data is actually smaller
   181  		if len(res.Bytes()) >= len(sfntData) {
   182  			copy(table, sfntData)
   183  		} else {
   184  			copy(table, res.Bytes())
   185  		}
   186  
   187  		binary.BigEndian.PutUint32(tableInfo[i*SIZE_OF_WOFF_ENTRY+WOFF_ENTRY_OFFSET_OFFSET:], uint32(offset))
   188  
   189  		offset += len(table)
   190  
   191  		binary.BigEndian.PutUint32(tableInfo[i*SIZE_OF_WOFF_ENTRY+WOFF_ENTRY_OFFSET_COMPR_LENGTH:], uint32(compLength))
   192  
   193  		tableBytes = append(tableBytes, table...)
   194  
   195  	}
   196  
   197  	woffSize := uint32(len(woffHeader) + len(tableInfo) + len(tableBytes))
   198  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_SIZE:], woffSize)
   199  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_SFNT_SIZE:], sfntSize)
   200  	binary.BigEndian.PutUint16(woffHeader[WOFF_OFFSET_VERSION_MAJ:], majorVersion)
   201  	binary.BigEndian.PutUint16(woffHeader[WOFF_OFFSET_VERSION_MIN:], minVersion)
   202  	binary.BigEndian.PutUint32(woffHeader[WOFF_OFFSET_FLAVOR:], flavor)
   203  
   204  	var out []byte
   205  	out = append(out, woffHeader...)
   206  	out = append(out, tableInfo...)
   207  	out = append(out, tableBytes...)
   208  
   209  	return out, nil
   210  }
   211  

View as plain text