@hibi_10000/grunt-webfont 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,474 @@
1
+ #!/usr/bin/env python
2
+
3
+ #
4
+ # This is special grunt-webfont verion of eotlitetool.py.
5
+ # https://github.com/sapegin/grunt-webfont
6
+ #
7
+ # Changes:
8
+ # * Output option now works.
9
+ # * Compatible with Python 3.
10
+ #
11
+
12
+ # ***** BEGIN LICENSE BLOCK *****
13
+ # Version: MPL 1.1/GPL 2.0/LGPL 2.1
14
+ #
15
+ # The contents of this file are subject to the Mozilla Public License Version
16
+ # 1.1 (the "License"); you may not use this file except in compliance with
17
+ # the License. You may obtain a copy of the License at
18
+ # http://www.mozilla.org/MPL/
19
+ #
20
+ # Software distributed under the License is distributed on an "AS IS" basis,
21
+ # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
22
+ # for the specific language governing rights and limitations under the
23
+ # License.
24
+ #
25
+ # The Original Code is font utility code.
26
+ #
27
+ # The Initial Developer of the Original Code is Mozilla Corporation.
28
+ # Portions created by the Initial Developer are Copyright (C) 2009
29
+ # the Initial Developer. All Rights Reserved.
30
+ #
31
+ # Contributor(s):
32
+ # John Daggett <jdaggett@mozilla.com>
33
+ #
34
+ # Alternatively, the contents of this file may be used under the terms of
35
+ # either the GNU General Public License Version 2 or later (the "GPL"), or
36
+ # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
37
+ # in which case the provisions of the GPL or the LGPL are applicable instead
38
+ # of those above. If you wish to allow use of your version of this file only
39
+ # under the terms of either the GPL or the LGPL, and not to allow others to
40
+ # use your version of this file under the terms of the MPL, indicate your
41
+ # decision by deleting the provisions above and replace them with the notice
42
+ # and other provisions required by the GPL or the LGPL. If you do not delete
43
+ # the provisions above, a recipient may use your version of this file under
44
+ # the terms of any one of the MPL, the GPL or the LGPL.
45
+ #
46
+ # ***** END LICENSE BLOCK ***** */
47
+
48
+ # eotlitetool.py - create EOT version of OpenType font for use with IE
49
+ #
50
+ # Usage: eotlitetool.py [-o output-filename] font1 [font2 ...]
51
+ #
52
+
53
+ # OpenType file structure
54
+ # http://www.microsoft.com/typography/otspec/otff.htm
55
+ #
56
+ # Types:
57
+ #
58
+ # BYTE 8-bit unsigned integer.
59
+ # CHAR 8-bit signed integer.
60
+ # USHORT 16-bit unsigned integer.
61
+ # SHORT 16-bit signed integer.
62
+ # ULONG 32-bit unsigned integer.
63
+ # Fixed 32-bit signed fixed-point number (16.16)
64
+ # LONGDATETIME Date represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a signed 64-bit integer.
65
+ #
66
+ # SFNT Header
67
+ #
68
+ # Fixed sfnt version // 0x00010000 for version 1.0.
69
+ # USHORT numTables // Number of tables.
70
+ # USHORT searchRange // (Maximum power of 2 <= numTables) x 16.
71
+ # USHORT entrySelector // Log2(maximum power of 2 <= numTables).
72
+ # USHORT rangeShift // NumTables x 16-searchRange.
73
+ #
74
+ # Table Directory
75
+ #
76
+ # ULONG tag // 4-byte identifier.
77
+ # ULONG checkSum // CheckSum for this table.
78
+ # ULONG offset // Offset from beginning of TrueType font file.
79
+ # ULONG length // Length of this table.
80
+ #
81
+ # OS/2 Table (Version 4)
82
+ #
83
+ # USHORT version // 0x0004
84
+ # SHORT xAvgCharWidth
85
+ # USHORT usWeightClass
86
+ # USHORT usWidthClass
87
+ # USHORT fsType
88
+ # SHORT ySubscriptXSize
89
+ # SHORT ySubscriptYSize
90
+ # SHORT ySubscriptXOffset
91
+ # SHORT ySubscriptYOffset
92
+ # SHORT ySuperscriptXSize
93
+ # SHORT ySuperscriptYSize
94
+ # SHORT ySuperscriptXOffset
95
+ # SHORT ySuperscriptYOffset
96
+ # SHORT yStrikeoutSize
97
+ # SHORT yStrikeoutPosition
98
+ # SHORT sFamilyClass
99
+ # BYTE panose[10]
100
+ # ULONG ulUnicodeRange1 // Bits 0-31
101
+ # ULONG ulUnicodeRange2 // Bits 32-63
102
+ # ULONG ulUnicodeRange3 // Bits 64-95
103
+ # ULONG ulUnicodeRange4 // Bits 96-127
104
+ # CHAR achVendID[4]
105
+ # USHORT fsSelection
106
+ # USHORT usFirstCharIndex
107
+ # USHORT usLastCharIndex
108
+ # SHORT sTypoAscender
109
+ # SHORT sTypoDescender
110
+ # SHORT sTypoLineGap
111
+ # USHORT usWinAscent
112
+ # USHORT usWinDescent
113
+ # ULONG ulCodePageRange1 // Bits 0-31
114
+ # ULONG ulCodePageRange2 // Bits 32-63
115
+ # SHORT sxHeight
116
+ # SHORT sCapHeight
117
+ # USHORT usDefaultChar
118
+ # USHORT usBreakChar
119
+ # USHORT usMaxContext
120
+ #
121
+ #
122
+ # The Naming Table is organized as follows:
123
+ #
124
+ # [name table header]
125
+ # [name records]
126
+ # [string data]
127
+ #
128
+ # Name Table Header
129
+ #
130
+ # USHORT format // Format selector (=0).
131
+ # USHORT count // Number of name records.
132
+ # USHORT stringOffset // Offset to start of string storage (from start of table).
133
+ #
134
+ # Name Record
135
+ #
136
+ # USHORT platformID // Platform ID.
137
+ # USHORT encodingID // Platform-specific encoding ID.
138
+ # USHORT languageID // Language ID.
139
+ # USHORT nameID // Name ID.
140
+ # USHORT length // String length (in bytes).
141
+ # USHORT offset // String offset from start of storage area (in bytes).
142
+ #
143
+ # head Table
144
+ #
145
+ # Fixed tableVersion // Table version number 0x00010000 for version 1.0.
146
+ # Fixed fontRevision // Set by font manufacturer.
147
+ # ULONG checkSumAdjustment // To compute: set it to 0, sum the entire font as ULONG, then store 0xB1B0AFBA - sum.
148
+ # ULONG magicNumber // Set to 0x5F0F3CF5.
149
+ # USHORT flags
150
+ # USHORT unitsPerEm // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
151
+ # LONGDATETIME created // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
152
+ # LONGDATETIME modified // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
153
+ # SHORT xMin // For all glyph bounding boxes.
154
+ # SHORT yMin
155
+ # SHORT xMax
156
+ # SHORT yMax
157
+ # USHORT macStyle
158
+ # USHORT lowestRecPPEM // Smallest readable size in pixels.
159
+ # SHORT fontDirectionHint
160
+ # SHORT indexToLocFormat // 0 for short offsets, 1 for long.
161
+ # SHORT glyphDataFormat // 0 for current format.
162
+ #
163
+ #
164
+ #
165
+ # Embedded OpenType (EOT) file format
166
+ # http://www.w3.org/Submission/EOT/
167
+ #
168
+ # EOT version 0x00020001
169
+ #
170
+ # An EOT font consists of a header with the original OpenType font
171
+ # appended at the end. Most of the data in the EOT header is simply a
172
+ # copy of data from specific tables within the font data. The exceptions
173
+ # are the 'Flags' field and the root string name field. The root string
174
+ # is a set of names indicating domains for which the font data can be
175
+ # used. A null root string implies the font data can be used anywhere.
176
+ # The EOT header is in little-endian byte order but the font data remains
177
+ # in big-endian order as specified by the OpenType spec.
178
+ #
179
+ # Overall structure:
180
+ #
181
+ # [EOT header]
182
+ # [EOT name records]
183
+ # [font data]
184
+ #
185
+ # EOT header
186
+ #
187
+ # ULONG eotSize // Total structure length in bytes (including string and font data)
188
+ # ULONG fontDataSize // Length of the OpenType font (FontData) in bytes
189
+ # ULONG version // Version number of this format - 0x00020001
190
+ # ULONG flags // Processing Flags (0 == no special processing)
191
+ # BYTE fontPANOSE[10] // OS/2 Table panose
192
+ # BYTE charset // DEFAULT_CHARSET (0x01)
193
+ # BYTE italic // 0x01 if ITALIC in OS/2 Table fsSelection is set, 0 otherwise
194
+ # ULONG weight // OS/2 Table usWeightClass
195
+ # USHORT fsType // OS/2 Table fsType (specifies embedding permission flags)
196
+ # USHORT magicNumber // Magic number for EOT file - 0x504C.
197
+ # ULONG unicodeRange1 // OS/2 Table ulUnicodeRange1
198
+ # ULONG unicodeRange2 // OS/2 Table ulUnicodeRange2
199
+ # ULONG unicodeRange3 // OS/2 Table ulUnicodeRange3
200
+ # ULONG unicodeRange4 // OS/2 Table ulUnicodeRange4
201
+ # ULONG codePageRange1 // OS/2 Table ulCodePageRange1
202
+ # ULONG codePageRange2 // OS/2 Table ulCodePageRange2
203
+ # ULONG checkSumAdjustment // head Table CheckSumAdjustment
204
+ # ULONG reserved[4] // Reserved - must be 0
205
+ # USHORT padding1 // Padding - must be 0
206
+ #
207
+ # EOT name records
208
+ #
209
+ # USHORT FamilyNameSize // Font family name size in bytes
210
+ # BYTE FamilyName[FamilyNameSize] // Font family name (name ID = 1), little-endian UTF-16
211
+ # USHORT Padding2 // Padding - must be 0
212
+ #
213
+ # USHORT StyleNameSize // Style name size in bytes
214
+ # BYTE StyleName[StyleNameSize] // Style name (name ID = 2), little-endian UTF-16
215
+ # USHORT Padding3 // Padding - must be 0
216
+ #
217
+ # USHORT VersionNameSize // Version name size in bytes
218
+ # bytes VersionName[VersionNameSize] // Version name (name ID = 5), little-endian UTF-16
219
+ # USHORT Padding4 // Padding - must be 0
220
+ #
221
+ # USHORT FullNameSize // Full name size in bytes
222
+ # BYTE FullName[FullNameSize] // Full name (name ID = 4), little-endian UTF-16
223
+ # USHORT Padding5 // Padding - must be 0
224
+ #
225
+ # USHORT RootStringSize // Root string size in bytes
226
+ # BYTE RootString[RootStringSize] // Root string, little-endian UTF-16
227
+
228
+
229
+
230
+ import optparse
231
+ import struct
232
+
233
+ class FontError(Exception):
234
+ """Error related to font handling"""
235
+ pass
236
+
237
+ def multichar(str):
238
+ vals = struct.unpack('4B', (str[:4]).encode())
239
+ return (vals[0] << 24) + (vals[1] << 16) + (vals[2] << 8) + vals[3]
240
+
241
+ def multicharval(v):
242
+ return struct.pack('4B', (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
243
+
244
+ class EOT:
245
+ EOT_VERSION = 0x00020001
246
+ EOT_MAGIC_NUMBER = 0x504c
247
+ EOT_DEFAULT_CHARSET = 0x01
248
+ EOT_FAMILY_NAME_INDEX = 0 # order of names in variable portion of EOT header
249
+ EOT_STYLE_NAME_INDEX = 1
250
+ EOT_VERSION_NAME_INDEX = 2
251
+ EOT_FULL_NAME_INDEX = 3
252
+ EOT_NUM_NAMES = 4
253
+
254
+ EOT_HEADER_PACK = '<4L10B2BL2H7L18x'
255
+
256
+ class OpenType:
257
+ SFNT_CFF = multichar('OTTO') # Postscript CFF SFNT version
258
+ SFNT_TRUE = 0x10000 # Standard TrueType version
259
+ SFNT_APPLE = multichar('true') # Apple TrueType version
260
+
261
+ SFNT_UNPACK = '>I4H'
262
+ TABLE_DIR_UNPACK = '>4I'
263
+
264
+ TABLE_HEAD = multichar('head') # TrueType table tags
265
+ TABLE_NAME = multichar('name')
266
+ TABLE_OS2 = multichar('OS/2')
267
+ TABLE_GLYF = multichar('glyf')
268
+ TABLE_CFF = multichar('CFF ')
269
+
270
+ OS2_FSSELECTION_ITALIC = 0x1
271
+ OS2_UNPACK = '>4xH2xH22x10B4L4xH14x2L'
272
+
273
+ HEAD_UNPACK = '>8xL'
274
+
275
+ NAME_RECORD_UNPACK = '>6H'
276
+ NAME_ID_FAMILY = 1
277
+ NAME_ID_STYLE = 2
278
+ NAME_ID_UNIQUE = 3
279
+ NAME_ID_FULL = 4
280
+ NAME_ID_VERSION = 5
281
+ NAME_ID_POSTSCRIPT = 6
282
+ PLATFORM_ID_UNICODE = 0 # Mac OS uses this typically
283
+ PLATFORM_ID_MICROSOFT = 3
284
+ ENCODING_ID_MICROSOFT_UNICODEBMP = 1 # with Microsoft platformID BMP-only Unicode encoding
285
+ LANG_ID_MICROSOFT_EN_US = 0x0409 # with Microsoft platformID EN US lang code
286
+
287
+ def eotname(ttf):
288
+ i = ttf.rfind('.')
289
+ if i != -1:
290
+ ttf = ttf[:i]
291
+ return ttf + '.eotlite'
292
+
293
+ def readfont(f):
294
+ data = open(f, 'rb').read()
295
+ return data
296
+
297
+ def get_table_directory(data):
298
+ """read the SFNT header and table directory"""
299
+ datalen = len(data)
300
+ sfntsize = struct.calcsize(OpenType.SFNT_UNPACK)
301
+ if sfntsize > datalen:
302
+ raise FontError('truncated font data')
303
+ sfntvers, numTables = struct.unpack(OpenType.SFNT_UNPACK, data[:sfntsize])[:2]
304
+ if sfntvers != OpenType.SFNT_CFF and sfntvers != OpenType.SFNT_TRUE:
305
+ raise FontError('invalid font type')
306
+
307
+ font = {}
308
+ font['version'] = sfntvers
309
+ font['numTables'] = numTables
310
+
311
+ # create set of offsets, lengths for tables
312
+ table_dir_size = struct.calcsize(OpenType.TABLE_DIR_UNPACK)
313
+ if sfntsize + table_dir_size * numTables > datalen:
314
+ raise FontError('truncated font data, table directory extends past end of data')
315
+ table_dir = {}
316
+ for i in range(0, numTables):
317
+ start = sfntsize + i * table_dir_size
318
+ end = start + table_dir_size
319
+ tag, check, bongo, dirlen = struct.unpack(OpenType.TABLE_DIR_UNPACK, data[start:end])
320
+ table_dir[tag] = {'offset': bongo, 'length': dirlen, 'checksum': check}
321
+
322
+ font['tableDir'] = table_dir
323
+
324
+ return font
325
+
326
+ def get_name_records(nametable):
327
+ """reads through the name records within name table"""
328
+ name = {}
329
+ # read the header
330
+ headersize = 6
331
+ count, strOffset = struct.unpack('>2H', nametable[2:6])
332
+ namerecsize = struct.calcsize(OpenType.NAME_RECORD_UNPACK)
333
+ if count * namerecsize + headersize > len(nametable):
334
+ raise FontError('names exceed size of name table')
335
+ name['count'] = count
336
+ name['strOffset'] = strOffset
337
+
338
+ # read through the name records
339
+ namerecs = {}
340
+ for i in range(0, count):
341
+ start = headersize + i * namerecsize
342
+ end = start + namerecsize
343
+ platformID, encodingID, languageID, nameID, namelen, offset = struct.unpack(OpenType.NAME_RECORD_UNPACK, nametable[start:end])
344
+ if platformID != OpenType.PLATFORM_ID_MICROSOFT or \
345
+ encodingID != OpenType.ENCODING_ID_MICROSOFT_UNICODEBMP or \
346
+ languageID != OpenType.LANG_ID_MICROSOFT_EN_US:
347
+ continue
348
+ namerecs[nameID] = {'offset': offset, 'length': namelen}
349
+
350
+ name['namerecords'] = namerecs
351
+ return name
352
+
353
+ def make_eot_name_headers(fontdata, nameTableDir):
354
+ """extracts names from the name table and generates the names header portion of the EOT header"""
355
+ nameoffset = nameTableDir['offset']
356
+ namelen = nameTableDir['length']
357
+ name = get_name_records(fontdata[nameoffset : nameoffset + namelen])
358
+ namestroffset = name['strOffset']
359
+ namerecs = name['namerecords']
360
+
361
+ eotnames = (OpenType.NAME_ID_FAMILY, OpenType.NAME_ID_STYLE, OpenType.NAME_ID_VERSION, OpenType.NAME_ID_FULL)
362
+ nameheaders = []
363
+ for nameid in eotnames:
364
+ if nameid in namerecs:
365
+ namerecord = namerecs[nameid]
366
+ noffset = namerecord['offset']
367
+ nlen = namerecord['length']
368
+ nformat = '%dH' % (nlen / 2) # length is in number of bytes
369
+ start = nameoffset + namestroffset + noffset
370
+ end = start + nlen
371
+ nstr = struct.unpack('>' + nformat, fontdata[start:end])
372
+ nameheaders.append(struct.pack('<H' + nformat + '2x', nlen, *nstr))
373
+ else:
374
+ nameheaders.append(struct.pack('4x')) # len = 0, padding = 0
375
+
376
+ return b''.join(nameheaders)
377
+
378
+ # just return a null-string (len = 0)
379
+ def make_root_string():
380
+ return struct.pack('2x')
381
+
382
+ def make_eot_header(fontdata):
383
+ """given ttf font data produce an EOT header"""
384
+ fontDataSize = len(fontdata)
385
+ font = get_table_directory(fontdata)
386
+
387
+ # toss out .otf fonts, t2embed library doesn't support these
388
+ tableDir = font['tableDir']
389
+
390
+ # check for required tables
391
+ required = (OpenType.TABLE_HEAD, OpenType.TABLE_NAME, OpenType.TABLE_OS2)
392
+ for table in required:
393
+ if not (table in tableDir):
394
+ raise FontError('missing required table ' + multicharval(table))
395
+
396
+ # read name strings
397
+
398
+ # pull out data from individual tables to construct fixed header portion
399
+ # need to calculate eotSize before packing
400
+ version = EOT.EOT_VERSION
401
+ flags = 0
402
+ charset = EOT.EOT_DEFAULT_CHARSET
403
+ magicNumber = EOT.EOT_MAGIC_NUMBER
404
+
405
+ # read values from OS/2 table
406
+ os2Dir = tableDir[OpenType.TABLE_OS2]
407
+ os2offset = os2Dir['offset']
408
+ os2size = struct.calcsize(OpenType.OS2_UNPACK)
409
+
410
+ if os2size > os2Dir['length']:
411
+ raise FontError('OS/2 table invalid length')
412
+
413
+ os2fields = struct.unpack(OpenType.OS2_UNPACK, fontdata[os2offset : os2offset + os2size])
414
+
415
+ panose = []
416
+ urange = []
417
+ codepage = []
418
+
419
+ weight, fsType = os2fields[:2]
420
+ panose[:10] = os2fields[2:12]
421
+ urange[:4] = os2fields[12:16]
422
+ fsSelection = os2fields[16]
423
+ codepage[:2] = os2fields[17:19]
424
+
425
+ italic = fsSelection & OpenType.OS2_FSSELECTION_ITALIC
426
+
427
+ # read in values from head table
428
+ headDir = tableDir[OpenType.TABLE_HEAD]
429
+ headoffset = headDir['offset']
430
+ headsize = struct.calcsize(OpenType.HEAD_UNPACK)
431
+
432
+ if headsize > headDir['length']:
433
+ raise FontError('head table invalid length')
434
+
435
+ headfields = struct.unpack(OpenType.HEAD_UNPACK, fontdata[headoffset : headoffset + headsize])
436
+ checkSumAdjustment = headfields[0]
437
+
438
+ # make name headers
439
+ nameheaders = make_eot_name_headers(fontdata, tableDir[OpenType.TABLE_NAME])
440
+ rootstring = make_root_string()
441
+
442
+ # calculate the total eot size
443
+ eotSize = struct.calcsize(EOT.EOT_HEADER_PACK) + len(nameheaders) + len(rootstring) + fontDataSize
444
+ fixed = struct.pack(EOT.EOT_HEADER_PACK,
445
+ *([eotSize, fontDataSize, version, flags] + panose + [charset, italic] +
446
+ [weight, fsType, magicNumber] + urange + codepage + [checkSumAdjustment]))
447
+
448
+ return b''.join((fixed, nameheaders, rootstring))
449
+
450
+
451
+ def write_eot_font(eot, header, data):
452
+ open(eot,'wb').write(b''.join((header, data)))
453
+ return
454
+
455
+ def main():
456
+
457
+ # deal with options
458
+ p = optparse.OptionParser()
459
+ p.add_option('--output', '-o')
460
+ options, args = p.parse_args()
461
+
462
+ # iterate over font files
463
+ for f in args:
464
+ data = readfont(f)
465
+ if len(data) == 0:
466
+ print('Error reading %s' % f)
467
+ else:
468
+ eot = options.output or eotname(f)
469
+ header = make_eot_header(data)
470
+ write_eot_font(eot, header, data)
471
+
472
+
473
+ if __name__ == '__main__':
474
+ main()
@@ -0,0 +1,133 @@
1
+ # Based on https://github.com/FontCustom/fontcustom/blob/master/lib/fontcustom/scripts/generate.py
2
+
3
+ import fontforge
4
+ import os
5
+ import sys
6
+ import json
7
+ import re
8
+ from subprocess import call
9
+ import shutil
10
+
11
+ args = json.load(sys.stdin)
12
+
13
+ f = fontforge.font()
14
+ f.encoding = 'UnicodeFull'
15
+ f.copyright = ''
16
+ f.design_size = 16
17
+ f.em = args['fontHeight']
18
+ f.descent = args['descent']
19
+ f.ascent = args['fontHeight'] - args['descent']
20
+ if args['version']:
21
+ f.version = args['version']
22
+ if args['normalize']:
23
+ f.autoWidth(0, 0, args['fontHeight'])
24
+
25
+ KERNING = 15
26
+
27
+
28
+ def create_empty_char(f, c):
29
+ pen = f.createChar(ord(c), c).glyphPen()
30
+ pen.moveTo((0, 0))
31
+ pen = None
32
+
33
+
34
+ if args['addLigatures']:
35
+ f.addLookup('liga', 'gsub_ligature', (), (('liga', (('latn', ('dflt')), )), ))
36
+ f.addLookupSubtable('liga', 'liga')
37
+
38
+ for dirname, dirnames, filenames in os.walk(args['inputDir']):
39
+ for filename in sorted(filenames):
40
+ name, ext = os.path.splitext(filename)
41
+ filePath = os.path.join(dirname, filename)
42
+ size = os.path.getsize(filePath)
43
+
44
+ if ext in ['.svg']:
45
+ # HACK: Remove <switch> </switch> tags
46
+ svgfile = open(filePath, 'r+')
47
+ svgtext = svgfile.read()
48
+ svgfile.seek(0)
49
+
50
+ # Replace the <switch> </switch> tags with nothing
51
+ svgtext = svgtext.replace('<switch>', '')
52
+ svgtext = svgtext.replace('</switch>', '')
53
+
54
+ if args['normalize']:
55
+ # Replace the width and the height
56
+ svgtext = re.sub(r'(<svg[^>]*)width="[^"]*"([^>]*>)', r'\1\2', svgtext)
57
+ svgtext = re.sub(r'(<svg[^>]*)height="[^"]*"([^>]*>)', r'\1\2', svgtext)
58
+
59
+ # Remove all contents of file so that we can write out the new contents
60
+ svgfile.truncate()
61
+ svgfile.write(svgtext)
62
+ svgfile.close()
63
+
64
+ cp = args['codepoints'][name]
65
+
66
+ if args['addLigatures']:
67
+ name = str(name) # Convert Unicode to a regular string because addPosSub doesn't work with Unicode
68
+ for char in name:
69
+ create_empty_char(f, char)
70
+ glyph = f.createChar(cp, name)
71
+ glyph.addPosSub('liga', tuple(name))
72
+ else:
73
+ glyph = f.createChar(cp, str(name))
74
+ glyph.importOutlines(filePath)
75
+
76
+ if args['normalize']:
77
+ glyph.left_side_bearing = glyph.right_side_bearing = 0
78
+ else:
79
+ glyph.width = args['fontHeight']
80
+
81
+ if args['round']:
82
+ glyph.round(int(args['round']))
83
+
84
+ fontfile = args['dest'] + os.path.sep + args['fontFilename']
85
+
86
+ f.fontname = args['fontFilename']
87
+ f.familyname = args['fontFamilyName']
88
+ f.fullname = args['fontFamilyName']
89
+
90
+ if args['addLigatures']:
91
+ def generate(filename):
92
+ f.generate(filename, flags=('opentype'))
93
+ else:
94
+ def generate(filename):
95
+ f.generate(filename)
96
+
97
+
98
+ # TTF
99
+ generate(fontfile + '.ttf')
100
+
101
+ # Hint the TTF file
102
+ # ttfautohint is optional
103
+ if (shutil.which('ttfautohint') and args['autoHint']):
104
+ call('ttfautohint --symbol --fallback-script=latn --no-info "%(font)s.ttf" "%(font)s-hinted.ttf" && mv "%(font)s-hinted.ttf" "%(font)s.ttf"' % {'font': fontfile}, shell=True)
105
+ f = fontforge.open(fontfile + '.ttf')
106
+
107
+ # SVG
108
+ if 'svg' in args['types']:
109
+ generate(fontfile + '.svg')
110
+
111
+ # Fix SVG header for webkit (from: https://github.com/fontello/font-builder/blob/master/bin/fontconvert.py)
112
+ svgfile = open(fontfile + '.svg', 'r+')
113
+ svgtext = svgfile.read()
114
+ svgfile.seek(0)
115
+ svgfile.write(svgtext.replace('<svg>', '<svg xmlns="http://www.w3.org/2000/svg">'))
116
+ svgfile.close()
117
+
118
+ scriptPath = os.path.dirname(os.path.realpath(__file__))
119
+
120
+ # WOFF
121
+ if 'woff' in args['types']:
122
+ generate(fontfile + '.woff')
123
+
124
+ # EOT
125
+ if 'eot' in args['types']:
126
+ # eotlitetool.py script to generate IE7-compatible .eot fonts
127
+ call('python "%(path)s/../eotlitetool.py" "%(font)s.ttf" --output "%(font)s.eot"' % {'path': scriptPath, 'font': fontfile}, shell=True)
128
+
129
+ # Delete TTF if not needed
130
+ if (not 'ttf' in args['types']) and (not 'woff2' in args['types']):
131
+ os.remove(fontfile + '.ttf')
132
+
133
+ print(json.dumps({'file': fontfile}))
@@ -0,0 +1,84 @@
1
+ import { consolaLogger, generatedFontFiles } from "../util/util.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import chalk from "chalk";
5
+ import util from "node:util";
6
+ import { exec } from "node:child_process";
7
+ import temp from "temp";
8
+
9
+ //#region tasks/engines/fontforge.ts
10
+ var fontforge_default = async (o) => {
11
+ const logger = o.logger || consolaLogger;
12
+ temp.track();
13
+ const tempDir = temp.mkdirSync();
14
+ o.files.forEach((file) => {
15
+ fs.writeFileSync(path.join(tempDir, o.rename(file)), fs.readFileSync(file));
16
+ });
17
+ const args = [
18
+ "fontforge",
19
+ "-script",
20
+ `"${path.join(import.meta.dirname, "../../bin/fontforge/generate.py")}"`
21
+ ].join(" ");
22
+ const promise = util.promisify(exec)(args, { maxBuffer: o.execMaxBuffer });
23
+ const proc = promise.child;
24
+ if (!proc) throw new TypeError("process is null");
25
+ proc.stderr.on("data", (data) => {
26
+ logger.log.verbose(data);
27
+ });
28
+ proc.stdout.on("data", (data) => {
29
+ logger.log.verbose(data);
30
+ });
31
+ proc.on("exit", (code, signal) => {
32
+ if (code !== 0) logger.log.info(`fontforge process has unexpectedly closed.
33
+ 1. Try to run grunt in verbose mode to see fontforge output: ${chalk.bold("grunt --verbose webfont")}.\n2. If stderr maxBuffer exceeded try to increase ${chalk.bold("execMaxBuffer")}, see ${chalk.underline("https://github.com/sapegin/grunt-webfont#execMaxBuffer")}. `);
34
+ });
35
+ const params = Object.assign(o, { inputDir: tempDir });
36
+ proc.stdin.write(JSON.stringify(params));
37
+ proc.stdin.end();
38
+ let out;
39
+ try {
40
+ out = (await promise).stdout;
41
+ } catch (err) {
42
+ if (err instanceof Error && err.code === 127) {
43
+ logger.log.error(`fontforge not found. Please install fontforge and all other requirements: ${chalk.underline("https://github.com/sapegin/grunt-webfont#installation")}`);
44
+ return false;
45
+ }
46
+ if (err instanceof Error) {
47
+ logger.log.error(err.message);
48
+ return false;
49
+ }
50
+ logger.log.error(`probably an impossible error`);
51
+ const success = !!generatedFontFiles(o);
52
+ const notError = /(Copyright|License |with many parts BSD |Executable based on sources from|Library based on sources from|Based on source from git)/;
53
+ const lines = err.split("\n");
54
+ const warn = [];
55
+ lines.forEach((line) => {
56
+ if (!line.match(notError) && !success) warn.push(line);
57
+ else logger.log.verbose(chalk.grey("fontforge: ") + line);
58
+ });
59
+ if (warn.length) {
60
+ logger.log.error(warn.join("\n"));
61
+ return false;
62
+ }
63
+ logger.log.error(`impossible error: ${err}`);
64
+ return false;
65
+ }
66
+ const json = out.replace(/^[^{]+/, "").replace(/[^}]+$/, "");
67
+ let result;
68
+ try {
69
+ result = JSON.parse(json);
70
+ } catch (e) {
71
+ logger.log.verbose(`Webfont did not receive a proper JSON result from Python script: ${e}`);
72
+ logger.log.error(`Something went wrong when running fontforge. Probably fontforge wasn’t installed correctly or one of your SVGs is too complicated for fontforge.
73
+
74
+ 1. Try to run Grunt in verbose mode: ${chalk.bold("grunt --verbose webfont")} and see what fontforge says. Then search GitHub issues for the solution: ${chalk.underline("https://github.com/sapegin/grunt-webfont/issues")}.\n\n2. Try to use “node” engine instead of “fontforge”: ${chalk.underline("https://github.com/sapegin/grunt-webfont#engine")}\n\n3. To find “bad” icon try to remove SVGs one by one until error disappears. Then try to simplify this SVG in Sketch, Illustrator, etc.
75
+
76
+ `);
77
+ return false;
78
+ }
79
+ return { fontName: path.basename(result.file) };
80
+ };
81
+
82
+ //#endregion
83
+ export { fontforge_default as default };
84
+ //# sourceMappingURL=fontforge.js.map