UNPKG

fonteditor-core

Version:

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

309 lines (263 loc) 7.93 kB
/** * @file 解析glyf轮廓 * @author mengke01(kekee000@gmail.com) */ import glyFlag from '../../enum/glyFlag'; import componentFlag from '../../enum/componentFlag'; const MAX_INSTRUCTION_LENGTH = 5000; // 设置instructions阈值防止读取错误 const MAX_NUMBER_OF_COORDINATES = 20000; // 设置坐标最大个数阈值,防止glyf读取错误 /** * 读取简单字形 * * @param {Reader} reader Reader对象 * @param {Object} glyf 空glyf * @return {Object} 解析后的glyf */ function parseSimpleGlyf(reader, glyf) { const offset = reader.offset; // 轮廓点个数 const numberOfCoordinates = glyf.endPtsOfContours[ glyf.endPtsOfContours.length - 1 ] + 1; // 判断坐标是否超过最大个数 if (numberOfCoordinates > MAX_NUMBER_OF_COORDINATES) { console.warn('error read glyf coordinates:' + offset); return glyf; } // 获取flag标志 let i; let length; const flags = []; let flag; i = 0; while (i < numberOfCoordinates) { flag = reader.readUint8(); flags.push(flag); i++; // 标志位3重复flag if ((flag & glyFlag.REPEAT) && i < numberOfCoordinates) { // 重复个数 const repeat = reader.readUint8(); for (let j = 0; j < repeat; j++) { flags.push(flag); i++; } } } // 坐标集合 const coordinates = []; const xCoordinates = []; let prevX = 0; let x; for (i = 0, length = flags.length; i < length; ++i) { x = 0; flag = flags[i]; // 标志位1 // If set, the corresponding y-coordinate is 1 byte long, not 2 if (flag & glyFlag.XSHORT) { x = reader.readUint8(); // 标志位5 x = (flag & glyFlag.XSAME) ? x : -1 * x; } // 与上一值一致 else if (flag & glyFlag.XSAME) { x = 0; } // 新值 else { x = reader.readInt16(); } prevX += x; xCoordinates[i] = prevX; coordinates[i] = { x: prevX, y: 0 }; if (flag & glyFlag.ONCURVE) { coordinates[i].onCurve = true; } } const yCoordinates = []; let prevY = 0; let y; for (i = 0, length = flags.length; i < length; i++) { y = 0; flag = flags[i]; if (flag & glyFlag.YSHORT) { y = reader.readUint8(); y = (flag & glyFlag.YSAME) ? y : -1 * y; } else if (flag & glyFlag.YSAME) { y = 0; } else { y = reader.readInt16(); } prevY += y; yCoordinates[i] = prevY; if (coordinates[i]) { coordinates[i].y = prevY; } } // 计算轮廓集合 if (coordinates.length) { const endPtsOfContours = glyf.endPtsOfContours; const contours = []; contours.push(coordinates.slice(0, endPtsOfContours[0] + 1)); for (i = 1, length = endPtsOfContours.length; i < length; i++) { contours.push(coordinates.slice(endPtsOfContours[i - 1] + 1, endPtsOfContours[i] + 1)); } glyf.contours = contours; } return glyf; } /** * 读取复合字形 * * @param {Reader} reader Reader对象 * @param {Object} glyf glyf对象 * @return {Object} glyf对象 */ function parseCompoundGlyf(reader, glyf) { glyf.compound = true; glyf.glyfs = []; let flags; let g; // 读取复杂字形 do { flags = reader.readUint16(); g = {}; g.flags = flags; g.glyphIndex = reader.readUint16(); let arg1 = 0; let arg2 = 0; let scaleX = 16384; let scaleY = 16384; let scale01 = 0; let scale10 = 0; if (componentFlag.ARG_1_AND_2_ARE_WORDS & flags) { arg1 = reader.readInt16(); arg2 = reader.readInt16(); } else { arg1 = reader.readInt8(); arg2 = reader.readInt8(); } if (componentFlag.ROUND_XY_TO_GRID & flags) { arg1 = Math.round(arg1); arg2 = Math.round(arg2); } if (componentFlag.WE_HAVE_A_SCALE & flags) { scaleX = reader.readInt16(); scaleY = scaleX; } else if (componentFlag.WE_HAVE_AN_X_AND_Y_SCALE & flags) { scaleX = reader.readInt16(); scaleY = reader.readInt16(); } else if (componentFlag.WE_HAVE_A_TWO_BY_TWO & flags) { scaleX = reader.readInt16(); scale01 = reader.readInt16(); scale10 = reader.readInt16(); scaleY = reader.readInt16(); } if (componentFlag.ARGS_ARE_XY_VALUES & flags) { g.useMyMetrics = !!flags & componentFlag.USE_MY_METRICS; g.overlapCompound = !!flags & componentFlag.OVERLAP_COMPOUND; g.transform = { a: Math.round(10000 * scaleX / 16384) / 10000, b: Math.round(10000 * scale01 / 16384) / 10000, c: Math.round(10000 * scale10 / 16384) / 10000, d: Math.round(10000 * scaleY / 16384) / 10000, e: arg1, f: arg2 }; } else { g.points = [arg1, arg2]; g.transform = { a: Math.round(10000 * scaleX / 16384) / 10000, b: Math.round(10000 * scale01 / 16384) / 10000, c: Math.round(10000 * scale10 / 16384) / 10000, d: Math.round(10000 * scaleY / 16384) / 10000, e: 0, f: 0 }; } glyf.glyfs.push(g); } while (componentFlag.MORE_COMPONENTS & flags); if (componentFlag.WE_HAVE_INSTRUCTIONS & flags) { const length = reader.readUint16(); if (length < MAX_INSTRUCTION_LENGTH) { const instructions = []; for (let i = 0; i < length; ++i) { instructions.push(reader.readUint8()); } glyf.instructions = instructions; } else { console.warn(length); } } return glyf; } /** * 解析glyf轮廓 * * @param {Reader} reader 读取器 * @param {Object} ttf ttf对象 * @param {number=} offset 偏移 * @return {Object} glyf对象 */ export default function parseGlyf(reader, ttf, offset) { if (null != offset) { reader.seek(offset); } const glyf = {}; let i; let length; let instructions; // 边界值 const numberOfContours = reader.readInt16(); glyf.xMin = reader.readInt16(); glyf.yMin = reader.readInt16(); glyf.xMax = reader.readInt16(); glyf.yMax = reader.readInt16(); // 读取简单字形 if (numberOfContours >= 0) { // endPtsOfConturs glyf.endPtsOfContours = []; if (numberOfContours > 0) { for (i = 0; i < numberOfContours; i++) { glyf.endPtsOfContours.push(reader.readUint16()); } } else { delete glyf.xMin; delete glyf.yMin; delete glyf.xMax; delete glyf.yMax; } // instructions length = reader.readUint16(); if (length) { // range错误 if (length < MAX_INSTRUCTION_LENGTH) { instructions = []; for (i = 0; i < length; ++i) { instructions.push(reader.readUint8()); } glyf.instructions = instructions; } else { console.warn(length); } } parseSimpleGlyf(reader, glyf); delete glyf.endPtsOfContours; } else { parseCompoundGlyf(reader, glyf); } return glyf; }