UNPKG

@sctg/fontminify

Version:

Minify font seamlessly, font subsetter, webfont (eot, woff, svg) converter.

229 lines (181 loc) 5.01 kB
/** * @file svgs2ttf * @author junmer eltorio */ /* eslint-env node */ import isSvg from 'is-svg'; import through from 'through2'; import * as path from 'path'; import replaceExt from 'replace-ext'; import {ab2b} from 'b3b'; import _ from 'lodash'; import bufferToVinyl from 'buffer-to-vinyl'; import fe, { TTF } from 'fonteditor-core'; import getEmpty from 'fonteditor-core/lib/ttf/getEmptyttfObject.js' import type {PluginFromSVGOption} from '../index.js' import * as File from 'vinyl' const TTF = fe.TTF const TTFWriter = fe.TTFWriter const svg2ttfobject = fe.svg2ttfobject const getEmptyttfObject = () => getEmpty.default() /** * SvgFont * * @constructor * @param {string} name filename * @param {Object} opts opts */ class SvgFont { opts: PluginFromSVGOption; ttf: typeof TTF; startCode: number; contents: Buffer; constructor(name, opts) { this.opts = _.extend( { adjust: { leftSideBearing: 0, rightSideBearing: 0, ajdustToEmBox: true, ajdustToEmPadding: 0 }, name: { fontFamily: name, fontSubFamily: name, uniqueSubFamily: name, postScriptName: name } }, opts ); // empty ttfobj const ttfobj = getEmptyttfObject(); // for save name ttfobj.post.format = 2; // new TTF this.ttf = new TTF(ttfobj); // set name this.ttf.setName(this.opts.name); // unicode start this.startCode = opts.startCode || 0xe001; } /** * add svg */ add(name:string, contents:Buffer) { const ttfObj = svg2ttfobject( contents.toString('utf-8'), { combinePath: true } ); const glyf = ttfObj.glyf[0]; glyf.name = path.basename(name, '.svg'); if (!Array.isArray(glyf.unicode)) { glyf.unicode = [this.startCode++]; } this.ttf.addGlyf(glyf); } /** * compile ttf contents * */ compile() { if (this.opts.adjust) { this.ttf.adjustGlyfPos(null, this.opts.adjust); this.ttf.adjustGlyf(null, this.opts.adjust); } this.contents = ab2b( new TTFWriter( this.opts ) .write( this.ttf.ttf ) ); } } /** * svgs2ttf fontmin plugin * * @api public */ export default (file:string|File, opts:PluginFromSVGOption) => { if (!file) { throw new Error('Missing file option for fontmin-svg2ttf'); } opts = _.extend({hinting: true}, opts); let firstFile; let fileName:string; let svgFont; if (typeof file === 'string') { // fix file ext file = replaceExt(file, '.ttf'); // set file name fileName = file as string; } else if (typeof file.path === 'string') { fileName = path.basename(file.path); firstFile = bufferToVinyl.file(null, fileName); } else { throw new Error('Missing path in file options for fontmin-svg2ttf'); } function bufferContents(file:File, enc, cb) { // ignore empty files if (file.isNull()) { cb(); return; } // check stream if (file.isStream()) { this.emit('error', new Error('Streaming not supported')); cb(); return; } // check svg if (!isSvg(file.contents as Buffer)) { cb(); return; } // set first file if not already set if (!firstFile) { firstFile = file; } // construct SvgFont instance if (!svgFont) { const fontName = opts.fontName || path.basename(fileName, '.ttf'); svgFont = new SvgFont(fontName, opts); } // add file to SvgFont instance svgFont.add(file.relative, file.contents); cb(); } function endStream(cb) { // no files passed in, no file goes out if (!firstFile || !svgFont) { cb(); return; } let joinedFile; // if file opt was a file path // clone everything from the first file if (typeof file === 'string') { joinedFile = firstFile.clone({ contents: false }); joinedFile.path = path.join(firstFile.base, file); } else { joinedFile = firstFile; } // complie svgfont svgFont.compile(); // set contents joinedFile.contents = svgFont.contents; joinedFile.ttfObject = svgFont.ttf.ttf; this.push(joinedFile); cb(); } return through.obj(bufferContents, endStream); };