@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 @@
1
+ {"version":3,"file":"webfont.js","names":["logger: Logger","wf.consolaLogger","o: OptionsInternal","wf.fontFormats","wf.UNICODE_PUA_START","name","wf.generatedFontFiles","packageJson","node","fontforge","wf.getFontPath","codepoints: string[]","fontSrcs: { 0: string[], 1: string[] }","wf.fontsSrcsMap","wf.fontSrcSeparators","template","wf.cssFilePrefixes","target"],"sources":["../tasks/webfont.ts"],"sourcesContent":["/**\n * SVG to webfont converter for Grunt\n *\n * @requires ttfautohint\n * @author Artem Sapegin (http://sapegin.me), Hibi_10000\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { globSync } from 'glob';\nimport chalk from 'chalk';\nimport _ from 'lodash';\nimport ttf2woff2 from 'ttf2woff2';\n\nimport fontforge from './engines/fontforge.ts';\nimport node from './engines/node.ts';\nimport * as wf from './util/util.ts';\nimport type { Logger, CustomOutput, TemplateOptions, Config, Options, OptionsInternal, Context } from './types.ts';\n\nimport packageJson from '../package.json' with { type: \"json\" };\n\nexport default (grunt: IGrunt): void => {\n\tgrunt.registerMultiTask('webfont', 'Compile separate SVG files to webfont', function() {\n\t\t/**\n\t\t * Consola to Grunt logger adapter.\n\t\t */\n\t\tconst logger: Logger = {\n\t\t\tlog: {\n\t\t\t\twarn: (...args) => {\n\t\t\t\t\tgrunt.log.warn.apply(null, args);\n\t\t\t\t},\n\t\t\t\terror: (...args) => {\n\t\t\t\t\tgrunt.warn.apply(null, args);\n\t\t\t\t},\n\t\t\t\tinfo: (...args) => {\n\t\t\t\t\tgrunt.log.writeln.apply(null, args);\n\t\t\t\t},\n\t\t\t\tverbose: (...args) => {\n\t\t\t\t\tgrunt.log.verbose.writeln.apply(null, args);\n\t\t\t\t},\n\t\t\t},\n\t\t\tfail: {\n\t\t\t\tfatal: (...args) => {\n\t\t\t\t\tgrunt.fail.fatal.apply(null, args);\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tconst allDone = this.async();\n\t\tconst params = this.data;\n\t\tconst options = this.options<Options>(undefined);\n\n\t\t/*\n\t\t * Check for `src` param on target config\n\t\t */\n\t\tthis.requiresConfig([this.name, this.target, 'src'].join('.'));\n\n\t\twebfont(this.name, this.target, this.filesSrc, params, options, logger).finally(allDone);\n\t});\n};\n\nexport const webfont = async (name: string, target: string, filesSrc: string[], params: Config, options: Options, logger?: Logger): Promise<void> => {\n\tif (!logger) logger = wf.consolaLogger;\n\tconst md5 = crypto.createHash('md5');\n\n\t/*\n\t * Check for `dest` param on either target config or global options object\n\t */\n\tif ((params.dest === undefined) && (options.dest === undefined)) {\n\t\tlogger.log.warn(`Required property ${name}.${target}.dest or ${name}.${target}.options.dest missing.`);\n\t}\n\n\tif (options.skip) {\n\t\tcompleteTask();\n\t\treturn;\n\t}\n\n\t// Source files\n\tconst files = filesSrc.filter(isSvgFile)\n\tif (!files.length) {\n\t\tlogger.log.warn('Specified empty list of source SVG files.');\n\t\tcompleteTask();\n\t\treturn;\n\t}\n\n\t// path must be a string, see https://nodejs.org/api/path.html#path_path_extname_path\n\tif (typeof options.template !== 'string') {\n\t\toptions.template = '';\n\t}\n\n\t// Options\n\tlet o: OptionsInternal = {\n\t\tlogger: logger,\n\t\tfontBaseName: options.font || 'icons',\n\t\tdestCss: options.destCss || params.destCss || params.dest,\n\t\tdestScss: options.destScss || params.destScss || params.destCss || params.dest,\n\t\tdestSass: options.destSass || params.destSass || params.destCss || params.dest,\n\t\tdestLess: options.destLess || params.destLess || params.destCss || params.dest,\n\t\tdestStyl: options.destStyl || params.destStyl || params.destCss || params.dest,\n\t\tdest: options.dest || params.dest,\n\t\trelativeFontPath: options.relativeFontPath,\n\t\tfontPathVariables: options.fontPathVariables || false,\n\t\taddHashes: options.hashes !== false,\n\t\taddLigatures: options.ligatures === true,\n\t\ttemplate: options.template,\n\t\tsyntax: options.syntax || 'bem',\n\t\ttemplateOptions: options.templateOptions || {},\n\t\tstylesheets: options.stylesheets || [options.stylesheet || path.extname(options.template).replace(/^\\./, '') || 'css'],\n\t\thtmlDemo: options.htmlDemo !== false,\n\t\thtmlDemoTemplate: options.htmlDemoTemplate,\n\t\thtmlDemoFilename: options.htmlDemoFilename,\n\t\tstyles: optionToArray(options.styles, 'font,icon'),\n\t\ttypes: optionToArray(options.types, 'eot,woff,ttf'),\n\t\torder: optionToArray(options.order, wf.fontFormats),\n\t\tembed: options.embed === true ? ['woff'] : optionToArray(options.embed, false),\n\t\trename: options.rename || path.basename,\n\t\tengine: options.engine || 'fontforge',\n\t\tautoHint: options.autoHint !== false,\n\t\tcodepoints: options.codepoints,\n\t\tcodepointsFile: options.codepointsFile,\n\t\tstartCodepoint: options.startCodepoint || wf.UNICODE_PUA_START,\n\t\tie7: options.ie7 === true,\n\t\tnormalize: options.normalize === true,\n\t\toptimize: options.optimize === false ? false : true,\n\t\tround: options.round !== undefined ? options.round : 10e12,\n\t\tfontHeight: options.fontHeight !== undefined ? options.fontHeight : 512,\n\t\tdescent: options.descent !== undefined ? options.descent : 64,\n\t\tversion: options.version !== undefined ? options.version : false,\n\t\tcache: options.cache || path.join(import.meta.dirname, '..', '.cache'),\n\t\tcallback: options.callback,\n\t\tcustomOutputs: options.customOutputs,\n\t\texecMaxBuffer: options.execMaxBuffer || 1024 * 200,\n\t};\n\n\to = Object.assign<OptionsInternal, Partial<OptionsInternal>>(o, {\n\t\tfontName: o.fontBaseName,\n\t\tdestCssPaths: {\n\t\t\tcss: o.destCss,\n\t\t\tscss: o.destScss,\n\t\t\tsass: o.destSass,\n\t\t\tless: o.destLess,\n\t\t\tstyl: o.destStyl\n\t\t},\n\t\trelativeFontPath: o.relativeFontPath || path.relative(o.destCss, o.dest),\n\t\tdestHtml: options.destHtml || o.destCss,\n\t\tfontfaceStyles: has(o.styles, 'font'),\n\t\tbaseStyles: has(o.styles, 'icon'),\n\t\textraStyles: has(o.styles, 'extra'),\n\t\tfiles: files,\n\t\tglyphs: [],\n\t});\n\n\to.hash = getHash();\n\to.fontFilename = template(options.fontFilename || o.fontBaseName, o);\n\to.fontFamilyName = template(options.fontFamilyName || o.fontBaseName, o);\n\n\t// “Rename” files\n\to.glyphs = o.files.map((file) => {\n\t\treturn o.rename(file).replace(path.extname(file), '');\n\t});\n\n\t// Check or generate codepoints\n\t// @todo Codepoint can be a Unicode code or character.\n\tlet currentCodepoint = o.startCodepoint;\n\tif (!o.codepoints) o.codepoints = {};\n\tif (o.codepointsFile) o.codepoints = readCodepointsFromFile();\n\to.glyphs.forEach((name) => {\n\t\tif (!o.codepoints[name]) {\n\t\t\to.codepoints[name] = getNextCodepoint();\n\t\t}\n\t});\n\tif (o.codepointsFile) saveCodepointsToFile();\n\n\t// Check if we need to generate font\n\tconst previousHash = readHash(name, target);\n\tlogger.log.verbose('New hash:', o.hash, '- previous hash:', previousHash);\n\tif (o.hash === previousHash) {\n\t\tlogger.log.verbose('Config and source files weren’t changed since last run, checking resulting files...');\n\t\tlet regenerationNeeded = false;\n\n\t\tconst generatedFiles = wf.generatedFontFiles(o);\n\t\tif (!generatedFiles.length){\n\t\t\tregenerationNeeded = true;\n\t\t} else {\n\t\t\tgeneratedFiles.push(getDemoFilePath());\n\t\t\to.stylesheets.forEach((stylesheet) => {\n\t\t\t\tgeneratedFiles.push(getCssFilePath(stylesheet));\n\t\t\t});\n\n\t\t\tregenerationNeeded = generatedFiles.some((filename) => {\n\t\t\t\tif (!filename) return false;\n\t\t\t\tif (!fs.existsSync(filename)) {\n\t\t\t\t\tlogger.log.verbose('File', filename, ' is missed.');\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t}\n\t\tif (!regenerationNeeded) {\n\t\t\tlogger.log.info(`Font ${chalk.cyan(o.fontName)} wasn’t changed since last run.`);\n\t\t\tcompleteTask();\n\t\t\treturn;\n\t\t}\n\t}\n\n\t// Save new hash and run\n\tsaveHash(name, target, o.hash);\n\ttry {\n\t\tcreateOutputDirs();\n\t\tcleanOutputDir();\n\t\tawait generateFont();\n\t\tgenerateWoff2Font();\n\t\tgenerateStylesheets();\n\t\tawait generateDemoHtml();\n\t\tgenerateCustomOutputs();\n\t\tprintDone();\n\t} finally {\n\t\tcompleteTask();\n\t}\n\n\t/**\n\t * Call callback function if it was specified in the options.\n\t */\n\tfunction completeTask(): void {\n\t\tif (o && ((typeof o.callback) === 'function')) {\n\t\t\to.callback(o.fontName, o.types, o.glyphs, o.hash);\n\t\t}\n\t}\n\n\t/**\n\t * Calculate hash to flush browser cache.\n\t * Hash is based on source SVG files contents, task options and grunt-webfont version.\n\t */\n\tfunction getHash(): string {\n\t\t// Source SVG files contents\n\t\to.files.forEach((file) => {\n\t\t\tmd5.update(fs.readFileSync(file, 'utf8'));\n\t\t});\n\n\t\t// Options\n\t\tmd5.update(JSON.stringify(o));\n\n\t\t// grunt-webfont version\n\t\tmd5.update(packageJson.version);\n\n\t\t// Templates\n\t\tif (o.template) {\n\t\t\tmd5.update(fs.readFileSync(o.template, 'utf8'));\n\t\t}\n\t\tif (o.htmlDemoTemplate) {\n\t\t\tmd5.update(fs.readFileSync(o.htmlDemoTemplate, 'utf8'));\n\t\t}\n\n\t\treturn md5.digest('hex');\n\t}\n\n\t/**\n\t * Create output directory\n\t */\n\tfunction createOutputDirs(): void {\n\t\to.stylesheets.forEach((stylesheet) => {\n\t\t\tfs.mkdirSync(option(o.destCssPaths, stylesheet), { recursive: true });\n\t\t});\n\t\tfs.mkdirSync(o.dest, { recursive: true });\n\t}\n\n\t/**\n\t * Clean output directory\n\t */\n\tfunction cleanOutputDir(): void {\n\t\tconst htmlDemoFileMask = path.posix.join(o.destCss, `${o.fontBaseName}*.{css,html}`);\n\t\tconst files = globSync(htmlDemoFileMask).concat(wf.generatedFontFiles(o));\n\t\tfiles.forEach(file => {\n\t\t\tfs.unlinkSync(file);\n\t\t});\n\t}\n\n\t/**\n\t * Generate font using selected engine\n\t */\n\tasync function generateFont(): Promise<void> {\n\t\tconst f = o.engine === 'node' ? node : fontforge;\n\t\tconst result = await f(o);\n\t\tif (result === false) {\n\t\t\t// Font was not created, exit\n\t\t\tcompleteTask();\n\t\t\treturn;\n\t\t}\n\n\t\tif (result) {\n\t\t\to = Object.assign(o, result);\n\t\t}\n\t}\n\n\t/**\n\t * Converts TTF font to WOFF2.\n\t */\n\tfunction generateWoff2Font(): void {\n\t\tif (!has(o.types, 'woff2')) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Read TTF font\n\t\tconst ttfFontPath = wf.getFontPath(o, 'ttf');\n\t\tconst ttfFont = fs.readFileSync(ttfFontPath);\n\n\t\t// Remove TTF font if not needed\n\t\tif (!has(o.types, 'ttf')) {\n\t\t\tfs.unlinkSync(ttfFontPath);\n\t\t}\n\n\t\t// Convert to WOFF2\n\t\tconst woffFont = ttf2woff2(ttfFont);\n\n\t\t// Save\n\t\tconst woff2FontPath = wf.getFontPath(o, 'woff2');\n\t\tfs.writeFileSync(woff2FontPath, woffFont);\n\t}\n\n\t/**\n\t * Generate CSS\n\t */\n\tfunction generateStylesheets(): void {\n\t\t// Convert codepoints to array of strings\n\t\tconst codepoints: string[] = [];\n\t\to.glyphs.forEach((name) => {\n\t\t\tcodepoints.push(o.codepoints[name].toString(16));\n\t\t});\n\t\t//@ts-ignore\n\t\to.codepoints = codepoints;\n\n\t\t// Prepage glyph names to use as CSS classes\n\t\to.glyphs = o.glyphs.map(classnameize);\n\n\t\to.stylesheets.sort((a, b) => {\n\t\t\treturn a === 'css' ? 1 : -1;\n\t\t}).forEach(generateStylesheet);\n\t}\n\n\t/**\n\t * Generate CSS\n\t *\n\t * @param stylesheet type: css, scss, ...\n\t */\n\tfunction generateStylesheet(stylesheet: string): void {\n\t\to.relativeFontPath = normalizePath(o.relativeFontPath);\n\n\t\t// Generate font URLs to use in @font-face\n\t\tconst fontSrcs: { 0: string[], 1: string[] } = { 0: [], 1: [] };\n\t\to.order.forEach((type: 'eot'|'woff2'|'woff'|'ttf'|'svg') => {\n\t\t\tif (!has(o.types, type)) return;\n\t\t\tconst fontSrc1 = wf.fontsSrcsMap[type][0];\n\t\t\tif (fontSrc1) fontSrcs[0].push(generateFontSrc(type, fontSrc1, stylesheet));\n\t\t\tfontSrcs[1].push(generateFontSrc(type, wf.fontsSrcsMap[type][1], stylesheet));\n\t\t});\n\n\t\t// Convert urls to strings that could be used in CSS\n\t\tconst fontSrcSeparator = option(wf.fontSrcSeparators, stylesheet);\n\t\to.fontSrc1 = fontSrcs[0].join(fontSrcSeparator);\n\t\to.fontSrc2 = fontSrcs[1].join(fontSrcSeparator);\n\t\to.fontRawSrcs = [fontSrcs[0], fontSrcs[1]];\n\n\t\t// Read JSON file corresponding to CSS template\n\t\tconst templateJson = readTemplate(o.template, o.syntax, '.json', true);\n\t\tif (templateJson) o = Object.assign<OptionsInternal, Required<TemplateOptions>>(o, JSON.parse(templateJson.template));\n\n\t\t// Now override values with templateOptions\n\t\tif (o.templateOptions) o = Object.assign(o, o.templateOptions);\n\n\t\t// Generate CSS\n\t\tconst ext = path.extname(o.template) || '.css'; // Use extension of o.template file if given, or default to .css\n\t\to.cssTemplate = readTemplate(o.template, o.syntax, ext);\n\t\tconst cssContext = Object.assign<OptionsInternal, Partial<Context>>(o, {\n\t\t\ticonsStyles: true,\n\t\t\tstylesheet: stylesheet,\n\t\t});\n\n\t\tlet css = renderTemplate(o.cssTemplate, cssContext);\n\n\t\t// Fix CSS preprocessors comments: single line comments will be removed after compilation\n\t\tif (has(['sass', 'scss', 'less', 'styl'], stylesheet)) {\n\t\t\tcss = css.replace(/\\/\\* *(.*?) *\\*\\//g, '// $1');\n\t\t}\n\n\t\t// Save file\n\t\tfs.writeFileSync(getCssFilePath(stylesheet), css);\n\t}\n\n\t/**\n\t * Gets the codepoints from the set filepath in o.codepointsFile\n\t */\n\tfunction readCodepointsFromFile(): { [key: string]: number } {\n\t\tif (!o.codepointsFile) return {};\n\t\tif (!fs.existsSync(o.codepointsFile)){\n\t\t\tlogger.log.verbose('Codepoints file not found');\n\t\t\treturn {};\n\t\t}\n\n\t\tconst buffer = fs.readFileSync(o.codepointsFile);\n\t\treturn JSON.parse(buffer.toString());\n\t}\n\n\t/**\n\t * Saves the codespoints to the set file\n\t */\n\tfunction saveCodepointsToFile(): void {\n\t\tif (!o.codepointsFile) return;\n\t\tconst codepointsToString = JSON.stringify(o.codepoints, null, 4);\n\t\ttry {\n\t\t\tfs.writeFileSync(o.codepointsFile, codepointsToString);\n\t\t\tlogger.log.verbose(`Codepoints saved to file \"${o.codepointsFile}\".`);\n\t\t} catch (err) {\n\t\t\tlogger.log.error(err.message);\n\t\t}\n\t}\n\n\t/*\n\t * Prepares base context for templates\n\t */\n\tfunction prepareBaseTemplateContext(): Context {\n\t\tconst context = Object.assign<{}, Context>({}, o);\n\t\treturn context;\n\t}\n\n\t/*\n\t * Makes custom extends necessary for use with preparing the template context\n\t * object for the HTML demo.\n\t */\n\tfunction prepareHtmlTemplateContext(): Context {\n\n\t\tlet context = prepareBaseTemplateContext();\n\n\t\tlet htmlStyles;\n\n\t\t// Prepare relative font paths for injection into @font-face refs in HTML\n\t\tconst relativeRe = new RegExp(_.escapeRegExp(o.relativeFontPath).replace(/[=!:\\/]/g, '\\\\$&'), 'g');\n\t\tconst htmlRelativeFontPath = normalizePath(path.relative(o.destHtml, o.dest));\n\t\tconst _fontSrc1 = o.fontSrc1.replace(relativeRe, htmlRelativeFontPath);\n\t\tconst _fontSrc2 = o.fontSrc2.replace(relativeRe, htmlRelativeFontPath);\n\n\t\tcontext = Object.assign<Context, Partial<Context>>(context, {\n\t\t\tfontSrc1: _fontSrc1,\n\t\t\tfontSrc2: _fontSrc2,\n\t\t\tfontfaceStyles: true,\n\t\t\tbaseStyles: true,\n\t\t\textraStyles: false,\n\t\t\ticonsStyles: true,\n\t\t\tstylesheet: 'css',\n\t\t});\n\n\t\t// Prepares CSS for injection into <style> tag at to of HTML\n\t\thtmlStyles = renderTemplate(o.cssTemplate, context);\n\t\tcontext = Object.assign<Context, Partial<Context>>(context, {\n\t\t\tstyles: htmlStyles,\n\t\t});\n\n\t\treturn context;\n\t}\n\n\t/**\n\t * Iterator function used as callback by looping construct below to\n\t * render \"custom output\" via mini configuration objects specified in\n\t * the array `options.customOutputs`.\n\t *\n\t * @param outputConfig\n\t */\n\tfunction generateCustomOutput(outputConfig: CustomOutput): void {\n\n\t\t// Accesses context\n\t\tlet context = prepareBaseTemplateContext();\n\t\tcontext = Object.assign(context, outputConfig.context);\n\n\t\t// Prepares config attributes related to template filepath\n\t\tconst templatePath = outputConfig.template;\n\t\tconst extension = path.extname(templatePath);\n\t\tconst syntax = outputConfig.syntax || '';\n\n\t\t// Renders template with given context\n\t\tconst template = readTemplate(templatePath, syntax, extension);\n\t\tconst output = renderTemplate(template, context);\n\n\t\t// Prepares config attributes related to destination filepath\n\t\tconst dest = outputConfig.dest || o.dest;\n\n\t\tlet filepath;\n\t\tlet destParent;\n\t\tlet destName;\n\n\t\tif (path.extname(dest) === '') {\n\t\t\t// If user specifies a directory, filename should be same as template\n\t\t\tdestParent = dest;\n\t\t\tdestName = path.basename(outputConfig.template);\n\t\t\tfilepath = path.join(dest, destName);\n\t\t} else {\n\t\t\t// If user specifies a file, that is our filepath\n\t\t\tdestParent = path.dirname(dest);\n\t\t\tfilepath = dest;\n\t\t}\n\n\t\t// Ensure existence of parent directory and output to file as desired\n\t\tfs.mkdirSync(destParent, { recursive: true });\n\t\tfs.writeFileSync(filepath, output);\n\t}\n\n\t/*\n\t * Iterates over entries in the `options.customOutputs` object and,\n\t * on a config-by-config basis, generates the desired results.\n\t */\n\tfunction generateCustomOutputs(): void {\n\t\tif (!o.customOutputs || o.customOutputs.length < 1) {\n\t\t\treturn;\n\t\t}\n\n\t\to.customOutputs.forEach(generateCustomOutput);\n\t}\n\n\t/**\n\t * Generate HTML demo page\n\t */\n\tasync function generateDemoHtml(): Promise<void> {\n\t\tif (!o.htmlDemo) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst context = prepareHtmlTemplateContext();\n\n\t\t// Generate HTML\n\t\tconst demoTemplate = readTemplate(o.htmlDemoTemplate, 'demo', '.html');\n\t\tconst demo = renderTemplate(demoTemplate, context);\n\n\t\ttry {\n\t\t\tawait fs.promises.mkdir(getDemoPath(), { recursive: true });\n\t\t} catch (err) {\n\t\t\tif (err) {\n\t\t\t\tlogger.log.info(err);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Save file\n\t\tfs.writeFileSync(getDemoFilePath(), demo);\n\n\t}\n\n\t/**\n\t * Print log\n\t */\n\tfunction printDone(): void {\n\t\tlogger.log.info(`Font ${chalk.cyan(o.fontName)} with ${o.glyphs.length} glyphs created.`);\n\t}\n\n\n\t/**\n\t * Helpers\n\t */\n\n\t/**\n\t * Convert a string of comma separated words into an array\n\t *\n\t * @param val Input string\n\t * @param defVal Default value\n\t */\n\tfunction optionToArray(val: string | false, defVal: string | false): string[] {\n\t\tif (val === undefined) {\n\t\t\tval = defVal;\n\t\t}\n\t\tif (!val) {\n\t\t\treturn [];\n\t\t}\n\t\tif (typeof val !== 'string') {\n\t\t\treturn val;\n\t\t}\n\t\treturn val.split(',').map((value) => value.trim());\n\t}\n\n\t/**\n\t * Check if a value exists in an array\n\t *\n\t * @param haystack Array to find the needle in\n\t * @param needle Value to find\n\t * @return Needle was found\n\t */\n\tfunction has(haystack: string[] | string, needle: string): boolean {\n\t\treturn haystack.indexOf(needle) !== -1;\n\t}\n\n\t/**\n\t * Return a specified option if it exists in an object or `_default` otherwise\n\t *\n\t * @param map Options object\n\t * @param key Option to find in the object\n\t */\n\tfunction option(map: { _default?: string, [key: string]: string }, key: string): string {\n\t\tif (key in map) {\n\t\t\treturn map[key];\n\t\t} else {\n\t\t\treturn map._default;\n\t\t}\n\t}\n\n\t/**\n\t * Find next unused codepoint.\n\t */\n\tfunction getNextCodepoint(): number {\n\t\twhile (Object.values(o.codepoints).includes(currentCodepoint)) {\n\t\t\tcurrentCodepoint++;\n\t\t}\n\t\treturn currentCodepoint;\n\t}\n\n\t/**\n\t * Check whether file is SVG or not\n\t *\n\t * @param filepath File path\n\t */\n\tfunction isSvgFile(filepath: string): boolean {\n\t\treturn path.extname(filepath).toLowerCase() === '.svg';\n\t}\n\n\t/**\n\t * Convert font file to data:uri and remove source file\n\t *\n\t * @param fontFile Font file path\n\t * @return Base64 encoded string\n\t */\n\tfunction embedFont(fontFile: string): string {\n\t\t// Convert to data:uri\n\t\tconst dataUri = fs.readFileSync(fontFile, 'base64');\n\t\tconst type = path.extname(fontFile).substring(1);\n\t\tconst fontUrl = `data:application/x-font-${type};charset=utf-8;base64,${dataUri}`;\n\n\t\t// Remove font file\n\t\tfs.unlinkSync(fontFile);\n\n\t\treturn fontUrl;\n\t}\n\n\t/**\n\t * Append a slash to end of a filepath if it not exists and make all slashes forward\n\t *\n\t * @param filepath File path\n\t */\n\tfunction normalizePath(filepath: string): string {\n\t\tif (!filepath.length) return filepath;\n\n\t\t// Make all slashes forward\n\t\tfilepath = filepath.replace(/\\\\/g, '/');\n\n\t\t// Make sure path ends with a slash\n\t\tif (!filepath.endsWith('/')) {\n\t\t\tfilepath += '/';\n\t\t}\n\n\t\treturn filepath;\n\t}\n\n\t/**\n\t * Generate URL for @font-face\n\t *\n\t * @param type Type of font\n\t * @param font URL or Base64 string\n\t * @param stylesheet type: css, scss, ...\n\t */\n\tfunction generateFontSrc(type: string, font: { ext: string, format?: string, embeddable?: boolean }, stylesheet: string): string {\n\t\tconst filename = template(`${o.fontFilename}${font.ext}`, o);\n\t\tlet fontPathVariableName = `${o.fontFamilyName}-font-path`;\n\n\t\tlet url;\n\t\tif (font.embeddable && has(o.embed, type)) {\n\t\t\turl = embedFont(path.join(o.dest, filename));\n\t\t} else {\n\t\t\tif (o.fontPathVariables && stylesheet !== 'css') {\n\t\t\t\tif (stylesheet === 'less') {\n\t\t\t\t\tfontPathVariableName = `@${fontPathVariableName}`;\n\t\t\t\t\to.fontPathVariable = `${fontPathVariableName} : \"${o.relativeFontPath}\";`;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tfontPathVariableName = `$${fontPathVariableName}`;\n\t\t\t\t\to.fontPathVariable = `${fontPathVariableName} : \"${o.relativeFontPath}\" !default;`;\n\t\t\t\t}\n\t\t\t\turl = filename;\n\t\t\t}\n\t\t\telse {\n\t\t\t\turl = `${o.relativeFontPath}${filename}`;\n\t\t\t}\n\t\t\tif (o.addHashes) {\n\t\t\t\tif (url.indexOf('#iefix') === -1) { // Do not add hashes for OldIE\n\t\t\t\t\t// Put hash at the end of an URL or before #hash\n\t\t\t\t\turl = url.replace(/(#|$)/, `?${o.hash}$1`);\n\t\t\t\t} else {\n\t\t\t\t\turl = url.replace(/(#|$)/, `${o.hash}$1`);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet src = `url(\"${url}\")`;\n\t\tif (o.fontPathVariables && stylesheet !== 'css') {\n\t\t\tif (stylesheet === 'less') {\n\t\t\t\tsrc = `url(\"@{${fontPathVariableName.replace('@','')}}${url}\")`;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tsrc = `url(${fontPathVariableName} + \"${url}\")`;\n\t\t\t}\n\t\t}\n\n\t\tif (font.format) src += ` format(\"${font.format}\")`;\n\n\t\treturn src;\n\t}\n\n\t/**\n\t * Read the template file\n\t *\n\t * @param template Template file path\n\t * @param syntax Syntax (bem, bootstrap, etc.)\n\t * @param ext Extension of the template\n\t * @return \\{filename: 'Template filename', template: 'Template code'}\n\t */\n\tfunction readTemplate(template: string, syntax: string, ext: string, optional?: boolean): { filename: string, template: string } {\n\t\tconst filename = template\n\t\t\t? path.resolve(template.replace(path.extname(template), ext))\n\t\t\t: path.join(import.meta.dirname, `../templates/${syntax}${ext}`)\n\t\t;\n\t\tif (fs.existsSync(filename)) {\n\t\t\treturn {\n\t\t\t\tfilename: filename,\n\t\t\t\ttemplate: fs.readFileSync(filename, 'utf8'),\n\t\t\t};\n\t\t} else if (!optional) {\n\t\t\tlogger.fail.fatal(`Cannot find template at path: ${filename}`);\n\t\t}\n\t}\n\n\t/**\n\t * Render template with error reporting\n\t *\n\t * @param template {filename: 'Template filename', template: 'Template code'}\n\t * @param context Template context\n\t */\n\tfunction renderTemplate(template: { filename: string, template: string }, context: Context): string {\n\t\ttry {\n\t\t\tconst func = _.template(template.template);\n\t\t\treturn func(context);\n\t\t} catch (e) {\n\t\t\tlogger.fail.fatal(`Error while rendering template ${template.filename}: ${e.message}`);\n\t\t}\n\t}\n\n\t/**\n\t * Basic template function: replaces {variables}\n\t *\n\t * @param tmpl Template code\n\t * @param context Values object\n\t */\n\tfunction template(tmpl: string, context: OptionsInternal): string {\n\t\treturn tmpl.replace(/\\{([^\\}]+)\\}/g, (m, key: keyof OptionsInternal) => {\n\t\t\treturn context[key] as string;\n\t\t});\n\t}\n\n\t/**\n\t * Prepare string to use as CSS class name\n\t *\n\t * @param str\n\t */\n\tfunction classnameize(str: string): string {\n\t\treturn str.trim().replace(/\\s+/g, '-');\n\t}\n\n\t/**\n\t * Return path of CSS file.\n\t *\n\t * @param stylesheet (css, scss, ...)\n\t */\n\tfunction getCssFilePath(stylesheet: string): string {\n\t\tconst cssFilePrefix = option(wf.cssFilePrefixes, stylesheet);\n\t\treturn path.join(option(o.destCssPaths, stylesheet), `${cssFilePrefix}${o.fontBaseName}.${stylesheet}`);\n\t}\n\n\t/**\n\t * Return path of HTML demo file or `null` if its generation was disabled.\n\t */\n\tfunction getDemoFilePath(): string {\n\t\tif (!o.htmlDemo) return null;\n\t\tconst name = o.htmlDemoFilename || o.fontBaseName;\n\t\treturn path.join(o.destHtml, `${name}.html`);\n\t}\n\n\t/**\n\t * Return path of HTML demo file or `null` if feature was disabled\n\t */\n\tfunction getDemoPath(): string {\n\t\tif (!o.htmlDemo) return null;\n\t\treturn o.destHtml;\n\t}\n\n\t/**\n\t * Save hash to cache file.\n\t *\n\t * @param name Task name (webfont).\n\t * @param target Task target name.\n\t * @param hash Hash.\n\t */\n\tfunction saveHash(name: string, target: string, hash: string): void {\n\t\tconst filepath = getHashPath(name, target);\n\t\tfs.mkdirSync(path.dirname(filepath), { recursive: true });\n\t\tfs.writeFileSync(filepath, hash);\n\t}\n\n\t/**\n\t * Read hash from cache file or `null` if file don’t exist.\n\t *\n\t * @param name Task name (webfont).\n\t * @param target Task target name.\n\t */\n\tfunction readHash(name: string, target: string): string {\n\t\tconst filepath = getHashPath(name, target);\n\t\tif (fs.existsSync(filepath)) {\n\t\t\treturn fs.readFileSync(filepath, 'utf8');\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Return path to cache file.\n\t *\n\t * @param name Task name (webfont).\n\t * @param target Task target name.\n\t */\n\tfunction getHashPath(name: string, target: string): string {\n\t\treturn path.join(o.cache, name, target, 'hash');\n\t}\n};\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@hibi_10000/grunt-webfont",
3
+ "description": "Ultimate SVG to webfont converter for Grunt.",
4
+ "version": "2.0.1",
5
+ "homepage": "https://github.com/Hibi-10000/grunt-webfont",
6
+ "author": "Hibi_10000",
7
+ "contributors": [
8
+ "Artem Sapegin (http://sapegin.me/)",
9
+ "Maxime Thirouin (http://moox.io/)",
10
+ "Aaron Lampros (https://github.com/alampros)",
11
+ "Cyrille Meichel (https://github.com/landru29)"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Hibi-10000/grunt-webfont.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/Hibi-10000/grunt-webfont/issues"
19
+ },
20
+ "keywords": [
21
+ "gruntplugin",
22
+ "font",
23
+ "webfont",
24
+ "fontforge",
25
+ "font-face",
26
+ "woff",
27
+ "woff2",
28
+ "ttf",
29
+ "svg",
30
+ "eot",
31
+ "truetype",
32
+ "css",
33
+ "icon"
34
+ ],
35
+ "license": "MIT",
36
+ "main": "dist/webfont.js",
37
+ "type": "module",
38
+ "scripts": {
39
+ "build": "tsdown",
40
+ "test:node": "node --test ./test/webfont.test.ts",
41
+ "test:grunt": "pnpm build & grunt --stack"
42
+ },
43
+ "engines": {
44
+ "node": ">=22",
45
+ "pnpm": ">=10"
46
+ },
47
+ "dependencies": {
48
+ "chalk": "^5.6.2",
49
+ "consola": "^3.4.2",
50
+ "glob": "^11.0.3",
51
+ "lodash": "^4.17.21",
52
+ "temp": "^0.9.4",
53
+ "ttf2woff2": "^8.0.0"
54
+ },
55
+ "optionalDependencies": {
56
+ "svg2ttf": "^6.0.3",
57
+ "svgicons2svgfont": "~15.0.1",
58
+ "svgo": "^4.0.0",
59
+ "ttf2eot": "^3.1.0",
60
+ "ttf2woff": "^3.0.0",
61
+ "which": "^5.0.0"
62
+ },
63
+ "devDependencies": {
64
+ "@types/grunt": "^0.4.32",
65
+ "@types/lodash": "^4.17.20",
66
+ "@types/node": "^22.18.1",
67
+ "@types/nodeunit": "^0.0.36",
68
+ "@types/stylus": "^0.48.43",
69
+ "@types/svg2ttf": "^5.0.3",
70
+ "@types/temp": "^0.9.4",
71
+ "@types/which": "^3.0.4",
72
+ "@types/xml2js": "^0.4.14",
73
+ "grunt": "^1.6.1",
74
+ "grunt-cli": "^1.5.0",
75
+ "grunt-contrib-clean": "^2.0.1",
76
+ "grunt-contrib-nodeunit": "^5.0.0",
77
+ "load-grunt-tasks": "^5.1.0",
78
+ "stylus": "^0.64.0",
79
+ "tsdown": "^0.15.0",
80
+ "typescript": "^5.9.2",
81
+ "xml2js": "^0.6.2"
82
+ },
83
+ "peerDependencies": {
84
+ "grunt": ">=0.4.0"
85
+ }
86
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * grunt-webfont: fontforge engine
3
+ *
4
+ * @requires fontforge, ttfautohint 1.00+ (optional), eotlitetool.py
5
+ * @author Artem Sapegin (http://sapegin.me), Hibi_10000
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import util from 'node:util';
11
+ import { exec, type ExecException } from 'node:child_process';
12
+ import temp from 'temp';
13
+ import chalk from 'chalk';
14
+
15
+ import * as wf from '../util/util.ts';
16
+ import type { OptionsInternal } from '../types.ts';
17
+
18
+ export default async (o: OptionsInternal): Promise<false | { fontName: string }> => {
19
+ const logger = o.logger || wf.consolaLogger;
20
+
21
+ // Copy source files to temporary directory
22
+ temp.track();
23
+ const tempDir = temp.mkdirSync();
24
+ o.files.forEach((file) => {
25
+ fs.writeFileSync(path.join(tempDir, o.rename(file)), fs.readFileSync(file));
26
+ });
27
+
28
+ // Run Fontforge
29
+ const args = [
30
+ 'fontforge',
31
+ '-script',
32
+ `"${path.join(import.meta.dirname, '../../bin/fontforge/generate.py')}"`,
33
+ ].join(' ');
34
+
35
+ const execPromise = util.promisify(exec);
36
+ const promise = execPromise(args, { maxBuffer: o.execMaxBuffer });
37
+ const proc = promise.child;
38
+
39
+ // Send JSON with params
40
+ if (!proc) throw new TypeError('process is null');
41
+
42
+ proc.stderr.on('data', (data) => {
43
+ logger.log.verbose(data);
44
+ });
45
+ proc.stdout.on('data', (data) => {
46
+ logger.log.verbose(data);
47
+ });
48
+ proc.on('exit', (code, signal) => {
49
+ if (code !== 0) {
50
+ logger.log.info( // cannot use error() because it will stop execution of callback of exec (which shows error message)
51
+ "fontforge process has unexpectedly closed.\n" +
52
+ `1. Try to run grunt in verbose mode to see fontforge output: ${chalk.bold('grunt --verbose webfont')}.\n` +
53
+ `2. If stderr maxBuffer exceeded try to increase ${chalk.bold('execMaxBuffer')}, ` +
54
+ `see ${chalk.underline('https://github.com/sapegin/grunt-webfont#execMaxBuffer')}. `
55
+ );
56
+ }
57
+ });
58
+
59
+ const params = Object.assign(o, {
60
+ inputDir: tempDir,
61
+ });
62
+ proc.stdin.write(JSON.stringify(params));
63
+ proc.stdin.end();
64
+
65
+ let out;
66
+ try {
67
+ out = (await promise).stdout;
68
+ } catch (err: unknown) {
69
+ if (err instanceof Error && (err as ExecException).code === 127) {
70
+ logger.log.error(`fontforge not found. Please install fontforge and all other requirements: ${chalk.underline('https://github.com/sapegin/grunt-webfont#installation')}`);
71
+ return false;
72
+ }
73
+ if (err instanceof Error) {
74
+ logger.log.error(err.message);
75
+ return false;
76
+ }
77
+ logger.log.error(`probably an impossible error`);
78
+
79
+ // Skip some fontforge output such as copyrights. Show warnings only when no font files was created
80
+ // or in verbose mode.
81
+ const success = !!wf.generatedFontFiles(o);
82
+ const notError = /(Copyright|License |with many parts BSD |Executable based on sources from|Library based on sources from|Based on source from git)/;
83
+ //const version = /(Executable based on sources from|Library based on sources from)/;
84
+ const lines = (err as string).split('\n');
85
+
86
+ const warn: string[] = [];
87
+ lines.forEach((line) => {
88
+ if (!line.match(notError) && !success) {
89
+ warn.push(line);
90
+ } else {
91
+ logger.log.verbose(chalk.grey('fontforge: ') + line);
92
+ }
93
+ });
94
+
95
+ if (warn.length) {
96
+ logger.log.error(warn.join('\n'));
97
+ return false;
98
+ }
99
+ logger.log.error(`impossible error: ${err}`);
100
+ return false;
101
+ }
102
+
103
+ // Trim fontforge result
104
+ const json = out.replace(/^[^{]+/, '').replace(/[^}]+$/, '');
105
+
106
+ // Parse json
107
+ let result: { file: string };
108
+ try {
109
+ result = JSON.parse(json);
110
+ } catch (e) {
111
+ logger.log.verbose(`Webfont did not receive a proper JSON result from Python script: ${e}`);
112
+ logger.log.error(
113
+ 'Something went wrong when running fontforge. Probably fontforge wasn’t installed correctly or one of your SVGs is too complicated for fontforge.\n\n' +
114
+ `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\n` +
115
+ `2. Try to use “node” engine instead of “fontforge”: ${chalk.underline('https://github.com/sapegin/grunt-webfont#engine')}\n\n` +
116
+ '3. To find “bad” icon try to remove SVGs one by one until error disappears. Then try to simplify this SVG in Sketch, Illustrator, etc.\n\n'
117
+ );
118
+ return false;
119
+ }
120
+
121
+ return {
122
+ fontName: path.basename(result.file)
123
+ };
124
+ };
@@ -0,0 +1,213 @@
1
+ /**
2
+ * grunt-webfont: Node.js engine
3
+ *
4
+ * @requires ttfautohint 1.00+ (optional)
5
+ * @author Artem Sapegin (http://sapegin.me), Hibi_10000
6
+ */
7
+
8
+ import fs from 'node:fs';
9
+ import stream from 'node:stream';
10
+ import path from 'node:path';
11
+ import util from 'node:util';
12
+ import { exec } from 'node:child_process';
13
+ import { StringDecoder } from 'node:string_decoder';
14
+ import temp from 'temp';
15
+ import { SVGIcons2SVGFontStream } from 'svgicons2svgfont';
16
+ import svg2ttf from 'svg2ttf';
17
+ import ttf2woff from 'ttf2woff';
18
+ import ttf2eot from 'ttf2eot';
19
+ import * as svgo from 'svgo';
20
+ import which from 'which';
21
+
22
+ import * as wf from '../util/util.ts';
23
+ import type { OptionsInternal, ReadableStreamWithMetadata } from '../types.ts';
24
+
25
+ export default async (o: OptionsInternal): Promise<false> => {
26
+ const logger = o.logger || wf.consolaLogger;
27
+
28
+ // @todo Ligatures
29
+
30
+ const fonts: { svg?: string, ttf?: Uint8Array, woff?: Uint8Array, eot?: Uint8Array } = {};
31
+
32
+ const generators = {
33
+ svg: async () => {
34
+ let font = '';
35
+ const decoder = new StringDecoder('utf8');
36
+ const streams = svgFilesToStreams(o.files);
37
+ const fontStream = new SVGIcons2SVGFontStream({
38
+ fontName: o.fontFamilyName,
39
+ fontHeight: o.fontHeight,
40
+ descent: o.descent,
41
+ normalize: o.normalize,
42
+ round: o.round,
43
+ });
44
+ fontStream.on('data', (chunk) => {
45
+ font += decoder.write(chunk);
46
+ });
47
+ await new Promise<void>((resolve) => {
48
+ fontStream.on('end', () => {
49
+ fonts.svg = font;
50
+ resolve();
51
+ });
52
+ for (const s of streams) {
53
+ const svgStream = s.stream as ReadableStreamWithMetadata;
54
+ svgStream.metadata = {
55
+ unicode: [String.fromCodePoint(s.codepoint)],
56
+ name: s.name,
57
+ }
58
+ fontStream.write(svgStream);
59
+ }
60
+ fontStream.end();
61
+ });
62
+ return font;
63
+ },
64
+
65
+ ttf: async () => {
66
+ const svgFont = await getFont('svg')
67
+ let font = svg2ttf(svgFont, {}).buffer;
68
+ const hintedFont = await autohintTtfFont(font)
69
+ // ttfautohint is optional
70
+ if (hintedFont) {
71
+ font = hintedFont;
72
+ }
73
+ fonts.ttf = font;
74
+ return font;
75
+ },
76
+
77
+ woff: async () => {
78
+ const ttfFont = await getFont('ttf')
79
+ const font = ttf2woff(ttfFont, {});
80
+ fonts.woff = font;
81
+ return font;
82
+ },
83
+
84
+ woff2: async () => {
85
+ // Will be converted from TTF later
86
+ },
87
+
88
+ eot: async () => {
89
+ const ttfFont = await getFont('ttf')
90
+ const font = ttf2eot(ttfFont);
91
+ fonts.eot = font;
92
+ return font;
93
+ }
94
+ };
95
+
96
+ // Font types
97
+ const typesToGenerate = o.types.slice();
98
+ if ((o.types.indexOf('woff2') !== -1) && (o.types.indexOf('ttf') === -1)) typesToGenerate.push('ttf');
99
+ try {
100
+ for (const type of typesToGenerate) {
101
+ if (type === 'woff2') continue;
102
+ //@ts-ignore
103
+ await createFontWriter(type);
104
+ }
105
+ } catch (e) {
106
+ return false;
107
+ }
108
+
109
+ async function getFont(type: 'svg'): Promise<string>;
110
+ async function getFont(type: 'eot' | 'woff' | 'ttf'): Promise<Uint8Array>;
111
+ async function getFont(type: 'eot' | 'woff' | 'ttf' | 'svg'): Promise<string | Uint8Array>;
112
+ async function getFont(type: 'eot' | 'woff' | 'ttf' | 'svg') {
113
+ if (fonts[type]) {
114
+ return fonts[type];
115
+ } else {
116
+ return await generators[type]();
117
+ }
118
+ }
119
+
120
+ async function createFontWriter(type: 'eot' | 'woff' | 'ttf' | 'svg') {
121
+ const font = await getFont(type)
122
+ fs.writeFileSync(wf.getFontPath(o, type), font);
123
+ }
124
+
125
+ type Stream = { codepoint: number, name?: string, stream: NodeJS.ReadableStream };
126
+ function svgFilesToStreams(files: string[]) {
127
+ try {
128
+ return files.map((file) => {
129
+
130
+ function fileStreamed(name: string, stream: NodeJS.ReadableStream): Stream {
131
+ return {
132
+ codepoint: o.codepoints[name],
133
+ name: name,
134
+ stream: stream,
135
+ };
136
+ }
137
+
138
+ function streamSVG(name: string, file: string) {
139
+ const stream = fs.createReadStream(file);
140
+ return fileStreamed(name, stream);
141
+ }
142
+
143
+ function streamSVGO(name: string, file: string) {
144
+ const svg = fs.readFileSync(file, 'utf8');
145
+ try {
146
+ const optimized = svgo.optimize(svg).data;
147
+ const strStream = stream.Readable.from(optimized);
148
+ return fileStreamed(name, strStream);
149
+ } catch (err) {
150
+ logger.log.error('Can’t simplify SVG file with SVGO.\n\n' + err);
151
+ throw err;
152
+ }
153
+ }
154
+
155
+ const idx = files.indexOf(file);
156
+ const name = o.glyphs[idx];
157
+
158
+ if (o.optimize === true) {
159
+ return streamSVGO(name, file)
160
+ } else {
161
+ return streamSVG(name, file);
162
+ }
163
+ });
164
+ } catch (err) {
165
+ logger.log.error('Can’t stream SVG file.\n\n' + err);
166
+ throw err;
167
+ }
168
+ }
169
+
170
+ async function autohintTtfFont(font: Uint8Array) {
171
+ if (!which.sync('ttfautohint', { nothrow: true })) {
172
+ logger.log.verbose('Hinting skipped, ttfautohint not found.');
173
+ return false;
174
+ }
175
+
176
+ temp.track();
177
+ const tempDir = temp.mkdirSync();
178
+ const originalFilepath = path.join(tempDir, 'font.ttf');
179
+ const hintedFilepath = path.join(tempDir, 'hinted.ttf');
180
+
181
+ if (!o.autoHint){
182
+ return false;
183
+ }
184
+ // Save original font to temporary directory
185
+ fs.writeFileSync(originalFilepath, font);
186
+
187
+ // Run ttfautohint
188
+ const args = [
189
+ 'ttfautohint',
190
+ '--symbol',
191
+ '--fallback-script=latn',
192
+ '--windows-compatibility',
193
+ '--no-info',
194
+ originalFilepath,
195
+ hintedFilepath
196
+ ].join(' ');
197
+
198
+ const execPromise = util.promisify(exec);
199
+ try {
200
+ await execPromise(args, { maxBuffer: o.execMaxBuffer });
201
+ } catch (err) {
202
+ if (err.code === 127) {
203
+ logger.log.verbose('Hinting skipped, ttfautohint not found.');
204
+ return false;
205
+ }
206
+ logger.log.error('Can’t run ttfautohint.\n\n' + err.message);
207
+ return false;
208
+ }
209
+ const hintedFont = fs.readFileSync(hintedFilepath);
210
+ return hintedFont;
211
+ }
212
+
213
+ };
@@ -0,0 +1,22 @@
1
+ // From DefinitelyTyped with some changes to match the new version
2
+ // Kaspar Vollenweider (casaper), MIT License
3
+
4
+ declare module 'ttf2eot' {
5
+ export default function ttf2eot(ttf: Uint8Array): Uint8Array;
6
+ }
7
+
8
+ declare module 'ttf2woff' {
9
+ export default function ttf2woff(ttf: Uint8Array, options?: {
10
+ /**
11
+ * Woff Extended Metadata Block
12
+ *
13
+ * See https://www.w3.org/TR/WOFF/#Metadata
14
+ */
15
+ metadata?: string | undefined;
16
+ }): Uint8Array;
17
+ }
18
+
19
+ declare module 'ttf2woff2' {
20
+ const ttf2woff2: NonNullable<(ttf: Uint8Array) => Uint8Array>;
21
+ export default ttf2woff2;
22
+ }