UNPKG

fonteditor-core

Version:

fonts (ttf, woff, woff2, eot, svg, otf) parse, write, transform, glyph adjust.

129 lines (112 loc) 4.47 kB
/** * @file ttf转svg * @author mengke01(kekee000@gmail.com) * * references: * http://www.w3.org/TR/SVG11/fonts.html */ import string from '../common/string'; import utilString from './util/string'; import TTFReader from './ttfreader'; import contours2svg from './util/contours2svg'; import unicode2xml from './util/unicode2xml'; import error from './error'; import config from './data/default'; // svg font id const SVG_FONT_ID = config.fontId; // xml 模板 /* eslint-disable no-multi-spaces */ const XML_TPL = '' + '<?xml version="1.0" standalone="no"?>' + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >' + '<svg xmlns="http://www.w3.org/2000/svg">' + '<metadata>${metadata}</metadata>' + '<defs><font id="${id}" horiz-adv-x="${advanceWidth}">' + '<font-face font-family="${fontFamily}" font-weight="${fontWeight}" font-stretch="normal"' + ' units-per-em="${unitsPerEm}" panose-1="${panose}" ascent="${ascent}" descent="${descent}"' + ' x-height="${xHeight}" bbox="${bbox}" underline-thickness="${underlineThickness}"' + ' underline-position="${underlinePosition}" unicode-range="${unicodeRange}" />' + '<missing-glyph horiz-adv-x="${missing.advanceWidth}" ${missing.d} />' + '${glyphList}' + '</font></defs>' + '</svg>'; /* eslint-enable no-multi-spaces */ // glyph 模板 const GLYPH_TPL = '<glyph glyph-name="${name}" unicode="${unicode}" d="${d}" />'; /** * ttf数据结构转svg * * @param {ttfObject} ttf ttfObject对象 * @param {Object} options 选项 * @param {string} options.metadata 字体相关的信息 * @return {string} svg字符串 */ function ttfobject2svg(ttf, options) { const OS2 = ttf['OS/2']; // 用来填充xml的数据 const xmlObject = { id: ttf.name.uniqueSubFamily || SVG_FONT_ID, metadata: string.encodeHTML(options.metadata || ''), advanceWidth: ttf.hhea.advanceWidthMax, fontFamily: ttf.name.fontFamily, fontWeight: OS2.usWeightClass, unitsPerEm: ttf.head.unitsPerEm, panose: [ OS2.bFamilyType, OS2.bSerifStyle, OS2.bWeight, OS2.bProportion, OS2.bContrast, OS2.bStrokeVariation, OS2.bArmStyle, OS2.bLetterform, OS2.bMidline, OS2.bXHeight ].join(' '), ascent: ttf.hhea.ascent, descent: ttf.hhea.descent, xHeight: OS2.bXHeight, bbox: [ttf.head.xMin, ttf.head.yMin, ttf.head.xMax, ttf.head.yMax].join(' '), underlineThickness: ttf.post.underlineThickness, underlinePosition: ttf.post.underlinePosition, unicodeRange: 'U+' + string.pad(OS2.usFirstCharIndex.toString(16), 4) + '-' + string.pad(OS2.usLastCharIndex.toString(16), 4) }; // glyf 第一个为missing glyph xmlObject.missing = {}; xmlObject.missing.advanceWidth = ttf.glyf[0].advanceWidth || 0; xmlObject.missing.d = ttf.glyf[0].contours && ttf.glyf[0].contours.length ? 'd="' + contours2svg(ttf.glyf[0].contours) + '"' : ''; // glyf 信息 let glyphList = ''; for (let i = 1, l = ttf.glyf.length; i < l; i++) { const glyf = ttf.glyf[i]; // 筛选简单字形,并且有轮廓,有编码 if (!glyf.compound && glyf.contours && glyf.unicode && glyf.unicode.length) { const glyfObject = { name: utilString.escape(glyf.name), unicode: unicode2xml(glyf.unicode), d: contours2svg(glyf.contours) }; glyphList += string.format(GLYPH_TPL, glyfObject); } } xmlObject.glyphList = glyphList; return string.format(XML_TPL, xmlObject); } /** * ttf格式转换成svg字体格式 * * @param {ArrayBuffer|ttfObject} ttfBuffer ttf缓冲数组或者ttfObject对象 * @param {Object} options 选项 * @param {Object} options.metadata 字体相关的信息 * * @return {string} svg字符串 */ export default function ttf2svg(ttfBuffer, options = {}) { // 读取ttf二进制流 if (ttfBuffer instanceof ArrayBuffer) { const reader = new TTFReader(); const ttfObject = reader.read(ttfBuffer); reader.dispose(); return ttfobject2svg(ttfObject, options); } // 读取ttfObject else if (ttfBuffer.version && ttfBuffer.glyf) { return ttfobject2svg(ttfBuffer, options); } error.raise(10109); }