fonteditor-core
Version:
fonts (ttf, woff, woff2, eot, svg, otf) parse, write, transform, glyph adjust.
129 lines (112 loc) • 4.47 kB
JavaScript
/**
* @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);
}