性能优化1

This commit is contained in:
崮生(子虚) 2026-04-09 10:17:02 +08:00
parent 765a301649
commit d43255aba0
15 changed files with 880 additions and 887 deletions

View File

@ -72,23 +72,28 @@ var Reader = exports.default = /*#__PURE__*/function () {
return _createClass(Reader, [{ return _createClass(Reader, [{
key: "read", key: "read",
value: function read(type, offset, littleEndian) { value: function read(type, offset, littleEndian) {
// 使用当前位移
if (undefined === offset) { if (undefined === offset) {
offset = this.offset; offset = this.offset;
} }
// 使用小尾
if (undefined === littleEndian) { if (undefined === littleEndian) {
littleEndian = this.littleEndian; littleEndian = this.littleEndian;
} }
// 扩展方法
if (undefined === dataType[type]) { if (undefined === dataType[type]) {
return this['read' + type](offset, littleEndian); return this['read' + type](offset, littleEndian);
} }
var size = dataType[type]; var size = dataType[type];
this.offset = offset + size; this.offset = offset + size;
return this.view['get' + type](offset, littleEndian); /* 优化20: switch 直接分发,避免动态属性查找 */
switch (type) {
case 'Int8': return this.view.getInt8(offset, littleEndian);
case 'Uint8': return this.view.getUint8(offset, littleEndian);
case 'Int16': return this.view.getInt16(offset, littleEndian);
case 'Uint16': return this.view.getUint16(offset, littleEndian);
case 'Int32': return this.view.getInt32(offset, littleEndian);
case 'Uint32': return this.view.getUint32(offset, littleEndian);
case 'Float32': return this.view.getFloat32(offset, littleEndian);
case 'Float64': return this.view.getFloat64(offset, littleEndian);
}
} }
/** /**
@ -109,12 +114,10 @@ var Reader = exports.default = /*#__PURE__*/function () {
if (length < 0 || offset + length > this.length) { if (length < 0 || offset + length > this.length) {
_error.default.raise(10001, this.length, offset + length); _error.default.raise(10001, this.length, offset + length);
} }
var buffer = []; /* 优化68: Uint8Array.slice 批量读取,替代逐字节 push */
for (var i = 0; i < length; ++i) { var bytes = new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length).slice();
buffer.push(this.view.getUint8(offset + i));
}
this.offset = offset + length; this.offset = offset + length;
return buffer; return bytes;
} }
/** /**
@ -135,13 +138,15 @@ var Reader = exports.default = /*#__PURE__*/function () {
if (length < 0 || offset + length > this.length) { if (length < 0 || offset + length > this.length) {
_error.default.raise(10001, this.length, offset + length); _error.default.raise(10001, this.length, offset + length);
} }
var value = ''; /* 优化22: 批量读取字节后构建字符串,替代逐字节 readUint8 */
var chars = new Array(length);
var viewOffset = this.view.byteOffset + offset;
var buf = this.view.buffer;
for (var i = 0; i < length; ++i) { for (var i = 0; i < length; ++i) {
var c = this.readUint8(offset + i); chars[i] = buf.charCodeAt ? String.fromCharCode(buf.charCodeAt(viewOffset + i)) : String.fromCharCode(new Uint8Array(buf, viewOffset + i, 1)[0]);
value += String.fromCharCode(c);
} }
this.offset = offset + length; this.offset = offset + length;
return value; return chars.join('');
} }
/** /**
@ -238,12 +243,12 @@ var Reader = exports.default = /*#__PURE__*/function () {
delete this.view; delete this.view;
} }
}]); }]);
}(); // 直接支持的数据类型 — 直接绑定,避免 curry 闭包开销 }(); // 优化19+20: 直接绑定方法 + switch 分发,避免 curry 闭包和动态属性查找
Object.keys(dataType).forEach(function (type) { Reader.prototype.readInt8 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 1; return this.view.getInt8(offset, this.littleEndian); };
var size = dataType[type]; Reader.prototype.readUint8 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 1; return this.view.getUint8(offset, this.littleEndian); };
Reader.prototype['read' + type] = function (offset) { Reader.prototype.readInt16 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 2; return this.view.getInt16(offset, this.littleEndian); };
if (offset === undefined) offset = this.offset; Reader.prototype.readUint16 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 2; return this.view.getUint16(offset, this.littleEndian); };
this.offset = offset + size; Reader.prototype.readInt32 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 4; return this.view.getInt32(offset, this.littleEndian); };
return this.view['get' + type](offset, this.littleEndian); Reader.prototype.readUint32 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 4; return this.view.getUint32(offset, this.littleEndian); };
}; Reader.prototype.readFloat32 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 4; return this.view.getFloat32(offset, this.littleEndian); };
}); Reader.prototype.readFloat64 = function(offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 8; return this.view.getFloat64(offset, this.littleEndian); };

View File

@ -20,165 +20,159 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
* @param {number} cmapOffset 子表的偏移 * @param {number} cmapOffset 子表的偏移
*/ */
function readSubTable(reader, ttf, subTable, cmapOffset) { function readSubTable(reader, ttf, subTable, cmapOffset) {
var i;
var l;
var glyphIdArray;
var startOffset = cmapOffset + subTable.offset; var startOffset = cmapOffset + subTable.offset;
var glyphCount; /* 优化59: 直接 view 批量读取,避免逐个 readUint8/readUint16/readUint32 */
subTable.format = reader.readUint16(startOffset); var view = reader.view;
var vOffset = view.byteOffset + startOffset;
subTable.format = view.getUint16(vOffset, false);
vOffset += 2;
// 0256 紧凑排列
if (subTable.format === 0) { if (subTable.format === 0) {
var format0 = subTable; var format0 = subTable;
// 跳过format字段 format0.length = view.getUint16(vOffset, false); vOffset += 2;
format0.length = reader.readUint16(); format0.language = view.getUint16(vOffset, false); vOffset += 2;
format0.language = reader.readUint16(); var glyphCount = format0.length - 6;
glyphIdArray = []; var glyphIdArray = new Array(glyphCount);
for (i = 0, l = format0.length - 6; i < l; i++) { for (var i = 0; i < glyphCount; i++) {
glyphIdArray.push(reader.readUint8()); glyphIdArray[i] = view.getUint8(vOffset + i);
} }
format0.glyphIdArray = glyphIdArray; format0.glyphIdArray = glyphIdArray;
} else if (subTable.format === 2) { } else if (subTable.format === 2) {
var format2 = subTable; var format2 = subTable;
// 跳过format字段 format2.length = view.getUint16(vOffset, false); vOffset += 2;
format2.length = reader.readUint16(); format2.language = view.getUint16(vOffset, false); vOffset += 2;
format2.language = reader.readUint16(); var subHeadKeys = new Array(256);
var subHeadKeys = []; var maxSubHeadKey = 0;
var maxSubHeadKey = 0; // 最大索引 var maxPos = -1;
var maxPos = -1; // 最大位置 for (var _i = 0; _i < 256; _i++) {
for (var _i = 0, _l = 256; _i < _l; _i++) { subHeadKeys[_i] = view.getUint16(vOffset, false) / 8;
subHeadKeys[_i] = reader.readUint16() / 8;
if (subHeadKeys[_i] > maxSubHeadKey) { if (subHeadKeys[_i] > maxSubHeadKey) {
maxSubHeadKey = subHeadKeys[_i]; maxSubHeadKey = subHeadKeys[_i];
maxPos = _i; maxPos = _i;
} }
vOffset += 2;
} }
var subHeads = []; var subHeads = new Array(maxSubHeadKey + 1);
for (i = 0; i <= maxSubHeadKey; i++) { for (var j = 0; j <= maxSubHeadKey; j++) {
subHeads[i] = { subHeads[j] = {
firstCode: reader.readUint16(), firstCode: view.getUint16(vOffset, false),
entryCount: reader.readUint16(), entryCount: view.getUint16(vOffset + 2, false),
idDelta: reader.readUint16(), idDelta: view.getUint16(vOffset + 4, false),
idRangeOffset: (reader.readUint16() - (maxSubHeadKey - i) * 8 - 2) / 2 idRangeOffset: (view.getUint16(vOffset + 6, false) - (maxSubHeadKey - j) * 8 - 2) / 2
}; };
vOffset += 8;
} }
glyphCount = (startOffset + format2.length - reader.offset) / 2; var glyphCount2 = (startOffset + format2.length - (vOffset - view.byteOffset)) / 2;
var glyphs = []; var glyphs = new Array(glyphCount2);
for (i = 0; i < glyphCount; i++) { for (var k = 0; k < glyphCount2; k++) {
glyphs[i] = reader.readUint16(); glyphs[k] = view.getUint16(vOffset, false);
vOffset += 2;
} }
format2.subHeadKeys = subHeadKeys; format2.subHeadKeys = subHeadKeys;
format2.maxPos = maxPos; format2.maxPos = maxPos;
format2.subHeads = subHeads; format2.subHeads = subHeads;
format2.glyphs = glyphs; format2.glyphs = glyphs;
} }
// 双字节编码,非紧凑排列
else if (subTable.format === 4) { else if (subTable.format === 4) {
var format4 = subTable; var format4 = subTable;
// 跳过format字段 format4.length = view.getUint16(vOffset, false); vOffset += 2;
format4.length = reader.readUint16(); format4.language = view.getUint16(vOffset, false); vOffset += 2;
format4.language = reader.readUint16(); format4.segCountX2 = view.getUint16(vOffset, false); vOffset += 2;
format4.segCountX2 = reader.readUint16(); format4.searchRange = view.getUint16(vOffset, false); vOffset += 2;
format4.searchRange = reader.readUint16(); format4.entrySelector = view.getUint16(vOffset, false); vOffset += 2;
format4.entrySelector = reader.readUint16(); format4.rangeShift = view.getUint16(vOffset, false); vOffset += 2;
format4.rangeShift = reader.readUint16();
var segCount = format4.segCountX2 / 2; var segCount = format4.segCountX2 / 2;
// end code var endCode = new Array(segCount);
var endCode = []; for (var e = 0; e < segCount; e++) {
for (i = 0; i < segCount; ++i) { endCode[e] = view.getUint16(vOffset, false);
endCode.push(reader.readUint16()); vOffset += 2;
} }
format4.endCode = endCode; format4.endCode = endCode;
format4.reservedPad = reader.readUint16(); format4.reservedPad = view.getUint16(vOffset, false); vOffset += 2;
// start code var startCode = new Array(segCount);
var startCode = []; for (var s = 0; s < segCount; s++) {
for (i = 0; i < segCount; ++i) { startCode[s] = view.getUint16(vOffset, false);
startCode.push(reader.readUint16()); vOffset += 2;
} }
format4.startCode = startCode; format4.startCode = startCode;
// idDelta var idDelta = new Array(segCount);
var idDelta = []; for (var d = 0; d < segCount; d++) {
for (i = 0; i < segCount; ++i) { idDelta[d] = view.getUint16(vOffset, false);
idDelta.push(reader.readUint16()); vOffset += 2;
} }
format4.idDelta = idDelta; format4.idDelta = idDelta;
format4.idRangeOffsetOffset = reader.offset; format4.idRangeOffsetOffset = vOffset - view.byteOffset;
// idRangeOffset var idRangeOffset = new Array(segCount);
var idRangeOffset = []; for (var r = 0; r < segCount; r++) {
for (i = 0; i < segCount; ++i) { idRangeOffset[r] = view.getUint16(vOffset, false);
idRangeOffset.push(reader.readUint16()); vOffset += 2;
} }
format4.idRangeOffset = idRangeOffset; format4.idRangeOffset = idRangeOffset;
// 总长度 - glyphIdArray起始偏移/2 var glyphCount4 = (format4.length - (vOffset - view.byteOffset - startOffset)) / 2;
glyphCount = (format4.length - (reader.offset - startOffset)) / 2; format4.glyphIdArrayOffset = vOffset - view.byteOffset;
// 记录array offset var glyphIdArray4 = new Array(glyphCount4);
format4.glyphIdArrayOffset = reader.offset; for (var g = 0; g < glyphCount4; g++) {
glyphIdArray4[g] = view.getUint16(vOffset, false);
// glyphIdArray vOffset += 2;
glyphIdArray = [];
for (i = 0; i < glyphCount; ++i) {
glyphIdArray.push(reader.readUint16());
} }
format4.glyphIdArray = glyphIdArray; format4.glyphIdArray = glyphIdArray4;
} else if (subTable.format === 6) { } else if (subTable.format === 6) {
var format6 = subTable; var format6 = subTable;
format6.length = reader.readUint16(); format6.length = view.getUint16(vOffset, false); vOffset += 2;
format6.language = reader.readUint16(); format6.language = view.getUint16(vOffset, false); vOffset += 2;
format6.firstCode = reader.readUint16(); format6.firstCode = view.getUint16(vOffset, false); vOffset += 2;
format6.entryCount = reader.readUint16(); format6.entryCount = view.getUint16(vOffset, false); vOffset += 2;
format6.glyphIdArrayOffset = vOffset - view.byteOffset;
// 记录array offset
format6.glyphIdArrayOffset = reader.offset;
var glyphIndexArray = [];
var entryCount = format6.entryCount; var entryCount = format6.entryCount;
// 读取字符分组 var glyphIndexArray = new Array(entryCount);
for (i = 0; i < entryCount; ++i) { for (var f = 0; f < entryCount; f++) {
glyphIndexArray.push(reader.readUint16()); glyphIndexArray[f] = view.getUint16(vOffset, false);
vOffset += 2;
} }
format6.glyphIdArray = glyphIndexArray; format6.glyphIdArray = glyphIndexArray;
} }
// defines segments for sparse representation in 4-byte character space
else if (subTable.format === 12) { else if (subTable.format === 12) {
var format12 = subTable; var format12 = subTable;
format12.reserved = reader.readUint16(); format12.reserved = view.getUint16(vOffset, false); vOffset += 2;
format12.length = reader.readUint32(); format12.length = view.getUint32(vOffset, false); vOffset += 4;
format12.language = reader.readUint32(); format12.language = view.getUint32(vOffset, false); vOffset += 4;
format12.nGroups = reader.readUint32(); format12.nGroups = view.getUint32(vOffset, false); vOffset += 4;
var groups = [];
var nGroups = format12.nGroups; var nGroups = format12.nGroups;
// 读取字符分组 var groups = new Array(nGroups);
for (i = 0; i < nGroups; ++i) { for (var h = 0; h < nGroups; h++) {
var group = {}; groups[h] = {
group.start = reader.readUint32(); start: view.getUint32(vOffset, false),
group.end = reader.readUint32(); end: view.getUint32(vOffset + 4, false),
group.startId = reader.readUint32(); startId: view.getUint32(vOffset + 8, false)
groups.push(group); };
vOffset += 12;
} }
format12.groups = groups; format12.groups = groups;
} }
// format 14
else if (subTable.format === 14) { else if (subTable.format === 14) {
var format14 = subTable; var format14 = subTable;
format14.length = reader.readUint32(); format14.length = view.getUint32(vOffset, false); vOffset += 4;
var numVarSelectorRecords = reader.readUint32(); var numVarSelectorRecords = view.getUint32(vOffset, false); vOffset += 4;
var _groups = []; var _groups = [];
var offset = reader.offset; var absOffset = vOffset;
for (var _i2 = 0; _i2 < numVarSelectorRecords; _i2++) { for (var vs = 0; vs < numVarSelectorRecords; vs++) {
var varSelector = reader.readUint24(offset); var varSelector = (view.getUint8(absOffset) << 16) + (view.getUint8(absOffset + 1) << 8) + view.getUint8(absOffset + 2);
var defaultUVSOffset = reader.readUint32(offset + 3); var defaultUVSOffset = view.getUint32(absOffset + 3, false);
var nonDefaultUVSOffset = reader.readUint32(offset + 7); var nonDefaultUVSOffset = view.getUint32(absOffset + 7, false);
offset += 11; absOffset += 11;
if (defaultUVSOffset) { if (defaultUVSOffset) {
var numUnicodeValueRanges = reader.readUint32(startOffset + defaultUVSOffset); var numUnicodeValueRanges = view.getUint32(view.byteOffset + startOffset + defaultUVSOffset, false);
for (var j = 0; j < numUnicodeValueRanges; j++) { var duvsOffset = view.byteOffset + startOffset + defaultUVSOffset + 4;
var startUnicode = reader.readUint24(); for (var dj = 0; dj < numUnicodeValueRanges; dj++) {
var additionalCount = reader.readUint8(); var startUnicode = (view.getUint8(duvsOffset) << 16) + (view.getUint8(duvsOffset + 1) << 8) + view.getUint8(duvsOffset + 2);
var additionalCount = view.getUint8(duvsOffset + 3);
duvsOffset += 4;
_groups.push({ _groups.push({
start: startUnicode, start: startUnicode,
end: startUnicode + additionalCount, end: startUnicode + additionalCount,
@ -187,10 +181,12 @@ function readSubTable(reader, ttf, subTable, cmapOffset) {
} }
} }
if (nonDefaultUVSOffset) { if (nonDefaultUVSOffset) {
var numUVSMappings = reader.readUint32(startOffset + nonDefaultUVSOffset); var numUVSMappings = view.getUint32(view.byteOffset + startOffset + nonDefaultUVSOffset, false);
for (var _j = 0; _j < numUVSMappings; _j++) { var nuvsOffset = view.byteOffset + startOffset + nonDefaultUVSOffset + 4;
var unicode = reader.readUint24(); for (var nj = 0; nj < numUVSMappings; nj++) {
var glyphId = reader.readUint16(); var unicode = (view.getUint8(nuvsOffset) << 16) + (view.getUint8(nuvsOffset + 1) << 8) + view.getUint8(nuvsOffset + 2);
var glyphId = view.getUint16(nuvsOffset + 3, false);
nuvsOffset += 5;
_groups.push({ _groups.push({
unicode: unicode, unicode: unicode,
glyphId: glyphId, glyphId: glyphId,
@ -204,27 +200,28 @@ function readSubTable(reader, ttf, subTable, cmapOffset) {
console.warn('not support cmap format:' + subTable.format); console.warn('not support cmap format:' + subTable.format);
} }
} }
function parse(reader, ttf) { function parse(reader, ttf) {
var tcmap = {}; var tcmap = {};
// eslint-disable-next-line no-invalid-this
var cmapOffset = this.offset; var cmapOffset = this.offset;
reader.seek(cmapOffset); reader.seek(cmapOffset);
tcmap.version = reader.readUint16(); // 编码方式 tcmap.version = reader.readUint16();
var numberSubtables = tcmap.numberSubtables = reader.readUint16(); // 表个数 var numberSubtables = tcmap.numberSubtables = reader.readUint16();
var subTables = tcmap.tables = []; // 名字表 var subTables = tcmap.tables = [];
var offset = reader.offset; /* 优化59: 直接 view 读取子表目录 */
var view = reader.view;
// 使用offset读取以便于查找 var dirOffset = view.byteOffset + reader.offset;
for (var i = 0, l = numberSubtables; i < l; i++) { for (var i = 0; i < numberSubtables; i++) {
var subTable = {}; var subTable = {};
subTable.platformID = reader.readUint16(offset); subTable.platformID = view.getUint16(dirOffset, false);
subTable.encodingID = reader.readUint16(offset + 2); subTable.encodingID = view.getUint16(dirOffset + 2, false);
subTable.offset = reader.readUint32(offset + 4); subTable.offset = view.getUint32(dirOffset + 4, false);
readSubTable(reader, ttf, subTable, cmapOffset); readSubTable(reader, ttf, subTable, cmapOffset);
subTables.push(subTable); subTables.push(subTable);
offset += 8; dirOffset += 8;
} }
reader.offset = dirOffset - view.byteOffset;
var cmap = (0, _readWindowsAllCodes.default)(subTables, ttf); var cmap = (0, _readWindowsAllCodes.default)(subTables, ttf);
return cmap; return cmap;
} }

View File

@ -17,15 +17,23 @@ var _default = exports.default = _table.default.create('directory', [], {
var tables = {}; var tables = {};
var numTables = ttf.numTables; var numTables = ttf.numTables;
var offset = this.offset; var offset = this.offset;
for (var i = offset, l = numTables * 16; i < l; i += 16) { /* 优化26: 直接 view 批量读取 */
var name = reader.readString(i, 4).trim(); var view = reader.view;
var vOffset = view.byteOffset + offset;
for (var i = 0; i < numTables; i++) {
var name = String.fromCharCode(
view.getUint8(vOffset), view.getUint8(vOffset + 1),
view.getUint8(vOffset + 2), view.getUint8(vOffset + 3)
).trim();
tables[name] = { tables[name] = {
name: name, name: name,
checkSum: reader.readUint32(i + 4), checkSum: view.getUint32(vOffset + 4, false),
offset: reader.readUint32(i + 8), offset: view.getUint32(vOffset + 8, false),
length: reader.readUint32(i + 12) length: view.getUint32(vOffset + 12, false)
}; };
vOffset += 16;
} }
reader.offset = offset + numTables * 16;
return tables; return tables;
}, },
write: function write(writer, ttf) { write: function write(writer, ttf) {

View File

@ -12,113 +12,101 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
* @author mengke01(kekee000@gmail.com) * @author mengke01(kekee000@gmail.com)
*/ */
var MAX_INSTRUCTION_LENGTH = 5000; // 设置instructions阈值防止读取错误 var MAX_INSTRUCTION_LENGTH = 5000;
var MAX_NUMBER_OF_COORDINATES = 20000; // 设置坐标最大个数阈值防止glyf读取错误 var MAX_NUMBER_OF_COORDINATES = 20000;
/** /**
* 读取简单字形 * 优化9+12+34+41+42: parseSimpleGlyf 消除中间数组直接 view 批量读取
*
* @param {Reader} reader Reader对象
* @param {Object} glyf 空glyf
* @return {Object} 解析后的glyf
*/ */
function parseSimpleGlyf(reader, glyf) { function parseSimpleGlyf(reader, glyf) {
var offset = reader.offset; var offset = reader.offset;
// 轮廓点个数
var numberOfCoordinates = glyf.endPtsOfContours[glyf.endPtsOfContours.length - 1] + 1; var numberOfCoordinates = glyf.endPtsOfContours[glyf.endPtsOfContours.length - 1] + 1;
// 判断坐标是否超过最大个数
if (numberOfCoordinates > MAX_NUMBER_OF_COORDINATES) { if (numberOfCoordinates > MAX_NUMBER_OF_COORDINATES) {
console.warn('error read glyf coordinates:' + offset); console.warn('error read glyf coordinates:' + offset);
return glyf; return glyf;
} }
// 获取flag标志 /* 优化34: 缓存 glyFlag 常量 */
var i; var REPEAT = _glyFlag.default.REPEAT;
var length; var XSHORT = _glyFlag.default.XSHORT;
var flags = []; var XSAME = _glyFlag.default.XSAME;
var flag; var YSHORT = _glyFlag.default.YSHORT;
i = 0; var YSAME = _glyFlag.default.YSAME;
while (i < numberOfCoordinates) { var ONCURVE = _glyFlag.default.ONCURVE;
flag = reader.readUint8();
flags.push(flag);
i++;
// 标志位3重复flag /* 优化34+42: 直接 view 读取 flags */
if (flag & _glyFlag.default.REPEAT && i < numberOfCoordinates) { var view = reader.view;
// 重复个数 var vOffset = view.byteOffset + reader.offset;
var repeat = reader.readUint8(); var flags = new Array(numberOfCoordinates);
for (var j = 0; j < repeat; j++) { var i = 0;
flags.push(flag); while (i < numberOfCoordinates) {
i++; var flag = view.getUint8(vOffset++);
flags[i++] = flag;
if (flag & REPEAT && i < numberOfCoordinates) {
var repeat = view.getUint8(vOffset++);
for (var j = 0; j < repeat && i < numberOfCoordinates; j++) {
flags[i++] = flag;
} }
} }
} }
// 坐标集合 /* 优化9+34: 直接构建扁平坐标数组,消除中间对象创建 */
var coordinates = []; var xArr = new Array(numberOfCoordinates);
var xCoordinates = [];
var prevX = 0; var prevX = 0;
var x; for (var xi = 0; xi < numberOfCoordinates; xi++) {
for (i = 0, length = flags.length; i < length; ++i) { var x = 0;
x = 0; var xflag = flags[xi];
flag = flags[i]; if (xflag & XSHORT) {
x = view.getUint8(vOffset++);
// 标志位1 x = (xflag & XSAME) ? x : -x;
// If set, the corresponding y-coordinate is 1 byte long, not 2 } else if (xflag & XSAME) {
if (flag & _glyFlag.default.XSHORT) {
x = reader.readUint8();
// 标志位5
x = flag & _glyFlag.default.XSAME ? x : -1 * x;
}
// 与上一值一致
else if (flag & _glyFlag.default.XSAME) {
x = 0; x = 0;
} } else {
// 新值 x = view.getInt16(vOffset);
else { vOffset += 2;
x = reader.readInt16();
} }
prevX += x; prevX += x;
xCoordinates[i] = prevX; xArr[xi] = prevX;
coordinates[i] = {
x: prevX,
y: 0
};
if (flag & _glyFlag.default.ONCURVE) {
coordinates[i].onCurve = true;
}
}
var yCoordinates = [];
var prevY = 0;
var y;
for (i = 0, length = flags.length; i < length; i++) {
y = 0;
flag = flags[i];
if (flag & _glyFlag.default.YSHORT) {
y = reader.readUint8();
y = flag & _glyFlag.default.YSAME ? y : -1 * y;
} else if (flag & _glyFlag.default.YSAME) {
y = 0;
} else {
y = reader.readInt16();
}
prevY += y;
yCoordinates[i] = prevY;
if (coordinates[i]) {
coordinates[i].y = prevY;
}
} }
// 计算轮廓集合 var yArr = new Array(numberOfCoordinates);
if (coordinates.length) { var prevY = 0;
for (var yi = 0; yi < numberOfCoordinates; yi++) {
var y = 0;
var yflag = flags[yi];
if (yflag & YSHORT) {
y = view.getUint8(vOffset++);
y = (yflag & YSAME) ? y : -y;
} else if (yflag & YSAME) {
y = 0;
} else {
y = view.getInt16(vOffset);
vOffset += 2;
}
prevY += y;
yArr[yi] = prevY;
}
reader.offset = vOffset - view.byteOffset;
/* 优化9: 一次遍历构建 contours消除中间数组 */
if (numberOfCoordinates > 0) {
var endPtsOfContours = glyf.endPtsOfContours; var endPtsOfContours = glyf.endPtsOfContours;
var contours = []; var contours = new Array(endPtsOfContours.length);
contours.push(coordinates.slice(0, endPtsOfContours[0] + 1)); var start = 0;
for (i = 1, length = endPtsOfContours.length; i < length; i++) { for (var ci = 0, cl = endPtsOfContours.length; ci < cl; ci++) {
contours.push(coordinates.slice(endPtsOfContours[i - 1] + 1, endPtsOfContours[i] + 1)); var end = endPtsOfContours[ci] + 1;
var contour = new Array(end - start);
for (var pi = start; pi < end; pi++) {
contour[pi - start] = {
x: xArr[pi],
y: yArr[pi],
onCurve: !!(flags[pi] & ONCURVE)
};
}
contours[ci] = contour;
start = end;
} }
glyf.contours = contours; glyf.contours = contours;
} }
@ -127,18 +115,23 @@ function parseSimpleGlyf(reader, glyf) {
/** /**
* 读取复合字形 * 读取复合字形
*
* @param {Reader} reader Reader对象
* @param {Object} glyf glyf对象
* @return {Object} glyf对象
*/ */
function parseCompoundGlyf(reader, glyf) { function parseCompoundGlyf(reader, glyf) {
glyf.compound = true; glyf.compound = true;
glyf.glyfs = []; glyf.glyfs = [];
var flags; var flags;
var g; var g;
var ARG_1_AND_2_ARE_WORDS = _componentFlag.default.ARG_1_AND_2_ARE_WORDS;
var ROUND_XY_TO_GRID = _componentFlag.default.ROUND_XY_TO_GRID;
var WE_HAVE_A_SCALE = _componentFlag.default.WE_HAVE_A_SCALE;
var WE_HAVE_AN_X_AND_Y_SCALE = _componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE;
var WE_HAVE_A_TWO_BY_TWO = _componentFlag.default.WE_HAVE_A_TWO_BY_TWO;
var ARGS_ARE_XY_VALUES = _componentFlag.default.ARGS_ARE_XY_VALUES;
var USE_MY_METRICS = _componentFlag.default.USE_MY_METRICS;
var OVERLAP_COMPOUND = _componentFlag.default.OVERLAP_COMPOUND;
var MORE_COMPONENTS = _componentFlag.default.MORE_COMPONENTS;
var WE_HAVE_INSTRUCTIONS = _componentFlag.default.WE_HAVE_INSTRUCTIONS;
// 读取复杂字形
do { do {
flags = reader.readUint16(); flags = reader.readUint16();
g = {}; g = {};
@ -150,32 +143,32 @@ function parseCompoundGlyf(reader, glyf) {
var scaleY = 16384; var scaleY = 16384;
var scale01 = 0; var scale01 = 0;
var scale10 = 0; var scale10 = 0;
if (_componentFlag.default.ARG_1_AND_2_ARE_WORDS & flags) { if (ARG_1_AND_2_ARE_WORDS & flags) {
arg1 = reader.readInt16(); arg1 = reader.readInt16();
arg2 = reader.readInt16(); arg2 = reader.readInt16();
} else { } else {
arg1 = reader.readInt8(); arg1 = reader.readInt8();
arg2 = reader.readInt8(); arg2 = reader.readInt8();
} }
if (_componentFlag.default.ROUND_XY_TO_GRID & flags) { if (ROUND_XY_TO_GRID & flags) {
arg1 = Math.round(arg1); arg1 = Math.round(arg1);
arg2 = Math.round(arg2); arg2 = Math.round(arg2);
} }
if (_componentFlag.default.WE_HAVE_A_SCALE & flags) { if (WE_HAVE_A_SCALE & flags) {
scaleX = reader.readInt16(); scaleX = reader.readInt16();
scaleY = scaleX; scaleY = scaleX;
} else if (_componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE & flags) { } else if (WE_HAVE_AN_X_AND_Y_SCALE & flags) {
scaleX = reader.readInt16(); scaleX = reader.readInt16();
scaleY = reader.readInt16(); scaleY = reader.readInt16();
} else if (_componentFlag.default.WE_HAVE_A_TWO_BY_TWO & flags) { } else if (WE_HAVE_A_TWO_BY_TWO & flags) {
scaleX = reader.readInt16(); scaleX = reader.readInt16();
scale01 = reader.readInt16(); scale01 = reader.readInt16();
scale10 = reader.readInt16(); scale10 = reader.readInt16();
scaleY = reader.readInt16(); scaleY = reader.readInt16();
} }
if (_componentFlag.default.ARGS_ARE_XY_VALUES & flags) { if (ARGS_ARE_XY_VALUES & flags) {
g.useMyMetrics = !!flags & _componentFlag.default.USE_MY_METRICS; g.useMyMetrics = !!(flags & USE_MY_METRICS);
g.overlapCompound = !!flags & _componentFlag.default.OVERLAP_COMPOUND; g.overlapCompound = !!(flags & OVERLAP_COMPOUND);
g.transform = { g.transform = {
a: Math.round(10000 * scaleX / 16384) / 10000, a: Math.round(10000 * scaleX / 16384) / 10000,
b: Math.round(10000 * scale01 / 16384) / 10000, b: Math.round(10000 * scale01 / 16384) / 10000,
@ -196,13 +189,13 @@ function parseCompoundGlyf(reader, glyf) {
}; };
} }
glyf.glyfs.push(g); glyf.glyfs.push(g);
} while (_componentFlag.default.MORE_COMPONENTS & flags); } while (MORE_COMPONENTS & flags);
if (_componentFlag.default.WE_HAVE_INSTRUCTIONS & flags) { if (WE_HAVE_INSTRUCTIONS & flags) {
var length = reader.readUint16(); var length = reader.readUint16();
if (length < MAX_INSTRUCTION_LENGTH) { if (length < MAX_INSTRUCTION_LENGTH) {
var instructions = []; var instructions = new Array(length);
for (var i = 0; i < length; ++i) { for (var i = 0; i < length; ++i) {
instructions.push(reader.readUint8()); instructions[i] = reader.readUint8();
} }
glyf.instructions = instructions; glyf.instructions = instructions;
} else { } else {
@ -213,36 +206,33 @@ function parseCompoundGlyf(reader, glyf) {
} }
/** /**
* 解析glyf轮廓 * 优化41: header endPtsOfContours 直接 view 批量读取
* * 优化12: hinting 模式跳过 instructions
* @param {Reader} reader 读取器
* @param {Object} ttf ttf对象
* @param {number=} offset 偏移
* @return {Object} glyf对象
*/ */
function parseGlyf(reader, ttf, offset) { function parseGlyf(reader, ttf, offset) {
if (null != offset) { if (null != offset) {
reader.seek(offset); reader.seek(offset);
} }
var glyf = {}; var glyf = {};
var i; var hinting = ttf.readOptions ? ttf.readOptions.hinting : false;
var length;
var instructions;
// 边界值 /* 优化41: 直接 view 读取 header 的 10 字节 */
var numberOfContours = reader.readInt16(); var view = reader.view;
glyf.xMin = reader.readInt16(); var vOffset = view.byteOffset + reader.offset;
glyf.yMin = reader.readInt16(); var numberOfContours = view.getInt16(vOffset, false);
glyf.xMax = reader.readInt16(); glyf.xMin = view.getInt16(vOffset + 2, false);
glyf.yMax = reader.readInt16(); glyf.yMin = view.getInt16(vOffset + 4, false);
glyf.xMax = view.getInt16(vOffset + 6, false);
glyf.yMax = view.getInt16(vOffset + 8, false);
vOffset += 10;
// 读取简单字形
if (numberOfContours >= 0) { if (numberOfContours >= 0) {
// endPtsOfConturs /* 优化41: endPtsOfContours 预分配 + 直接 view 读取 */
glyf.endPtsOfContours = [];
if (numberOfContours > 0) { if (numberOfContours > 0) {
for (i = 0; i < numberOfContours; i++) { glyf.endPtsOfContours = new Array(numberOfContours);
glyf.endPtsOfContours.push(reader.readUint16()); for (var i = 0; i < numberOfContours; i++) {
glyf.endPtsOfContours[i] = view.getUint16(vOffset, false);
vOffset += 2;
} }
} else { } else {
delete glyf.xMin; delete glyf.xMin;
@ -251,23 +241,23 @@ function parseGlyf(reader, ttf, offset) {
delete glyf.yMax; delete glyf.yMax;
} }
// instructions /* 优化12+42: 非 hinting 模式只跳过 instructions */
length = reader.readUint16(); var instrLength = view.getUint16(vOffset, false);
if (length) { vOffset += 2;
// range错误 if (hinting && instrLength && instrLength < MAX_INSTRUCTION_LENGTH) {
if (length < MAX_INSTRUCTION_LENGTH) { var instructions = new Array(instrLength);
instructions = []; for (var j = 0; j < instrLength; j++) {
for (i = 0; i < length; ++i) { instructions[j] = view.getUint8(vOffset + j);
instructions.push(reader.readUint8());
}
glyf.instructions = instructions;
} else {
console.warn(length);
} }
glyf.instructions = instructions;
} }
vOffset += instrLength;
reader.offset = vOffset - view.byteOffset;
parseSimpleGlyf(reader, glyf); parseSimpleGlyf(reader, glyf);
delete glyf.endPtsOfContours; delete glyf.endPtsOfContours;
} else { } else {
reader.offset = vOffset - view.byteOffset;
parseCompoundGlyf(reader, glyf); parseCompoundGlyf(reader, glyf);
} }
return glyf; return glyf;

View File

@ -12,166 +12,87 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
*/ */
/** /**
* 获取glyf的大小 * 优化33+38+39+40+48+49+57: getFlagsAndSize 单遍扫描替代两遍扫描
*
* @param {Object} glyf glyf对象
* @param {Object} glyfSupport glyf相关统计
* @param {boolean} hinting 是否保留hints
* @param {boolean} writeZeroContoursGlyfData 是否写空轮廓 glyph
* @return {number} size大小
*/ */
function sizeofSimple(glyf, glyfSupport, hinting, writeZeroContoursGlyfData) { function getFlagsAndSize(glyf, glyfSupport, hinting) {
if (!writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) { if (!glyf.contours || glyf.contours.length === 0) {
return 0; return 0;
} }
// fixed header + endPtsOfContours /* 优化33: 缓存 glyFlag 常量到局部变量 */
var result = 12 + (glyf.contours || []).length * 2 + (glyfSupport.flags || []).length; var ONCURVE = _glyFlag.default.ONCURVE;
(glyfSupport.xCoord || []).forEach(function (x) { var XSHORT = _glyFlag.default.XSHORT;
result += 0 <= x && x <= 0xFF ? 1 : 2; var YSHORT = _glyFlag.default.YSHORT;
}); var XSAME = _glyFlag.default.XSAME;
(glyfSupport.yCoord || []).forEach(function (y) { var YSAME = _glyFlag.default.YSAME;
result += 0 <= y && y <= 0xFF ? 1 : 2; var REPEAT = _glyFlag.default.REPEAT;
});
return result + (hinting && glyf.instructions ? glyf.instructions.length : 0);
}
/**
* 复合图元size
*
* @param {Object} glyf glyf对象
* @param {boolean} hinting 是否保留hints, compound 图元暂时不做hinting
* @return {number} size大小
*/
// eslint-disable-next-line no-unused-vars
function sizeofCompound(glyf, hinting) {
var size = 10;
var transform;
glyf.glyfs.forEach(function (g) {
transform = g.transform;
// flags + glyfIndex
size += 4;
// a, b, c, d, e
// xy values or points
if (transform.e < 0 || transform.e > 0x7F || transform.f < 0 || transform.f > 0x7F) {
size += 4;
} else {
size += 2;
}
// 01 , 10
if (transform.b || transform.c) {
size += 8;
}
// scale
else if (transform.a !== 1 || transform.d !== 1) {
size += transform.a === transform.d ? 2 : 4;
}
});
return size;
}
/**
* 获取flags
*
* @param {Object} glyf glyf对象
* @param {Object} glyfSupport glyf相关统计
* @return {Array}
*/
function getFlags(glyf, glyfSupport) {
if (!glyf.contours || 0 === glyf.contours.length) {
return glyfSupport;
}
var flags = [];
var xCoord = [];
var yCoord = [];
var contours = glyf.contours;
var contour;
var prev;
var first = true;
for (var j = 0, cl = contours.length; j < cl; j++) {
contour = contours[j];
for (var i = 0, l = contour.length; i < l; i++) {
var point = contour[i];
if (first) {
xCoord.push(point.x);
yCoord.push(point.y);
first = false;
} else {
xCoord.push(point.x - prev.x);
yCoord.push(point.y - prev.y);
}
flags.push(point.onCurve ? _glyFlag.default.ONCURVE : 0);
prev = point;
}
}
// compress
var flagsC = []; var flagsC = [];
var xCoordC = []; var xCoordC = [];
var yCoordC = []; var yCoordC = [];
var x; var contours = glyf.contours;
var y; var prevX = 0, prevY = 0;
var prevFlag; var isFirst = true;
var prevFlag = -1;
var repeatPoint = -1; var repeatPoint = -1;
flags.forEach(function (flag, index) {
x = xCoord[index];
y = yCoord[index];
// 第一个 /* 单次遍历: delta坐标计算 + flag压缩 + 坐标编码 + 大小累加 */
if (index === 0) { var encodedCoordSize = 0;
if (-0xFF <= x && x <= 0xFF) {
flag += _glyFlag.default.XSHORT; for (var j = 0, cl = contours.length; j < cl; j++) {
if (x >= 0) { var contour = contours[j];
flag += _glyFlag.default.XSAME; for (var i = 0, l = contour.length; i < l; i++) {
} var point = contour[i];
x = Math.abs(x); var px = point.x;
} var py = point.y;
if (-0xFF <= y && y <= 0xFF) { var dx, dy;
flag += _glyFlag.default.YSHORT; var flag = point.onCurve ? ONCURVE : 0;
if (y >= 0) {
flag += _glyFlag.default.YSAME; if (isFirst) {
} dx = px;
y = Math.abs(y); dy = py;
} isFirst = false;
flagsC.push(prevFlag = flag);
xCoordC.push(x);
yCoordC.push(y);
}
// 后续
else {
if (x === 0) {
flag += _glyFlag.default.XSAME;
} else { } else {
if (-0xFF <= x && x <= 0xFF) { dx = px - prevX;
flag += _glyFlag.default.XSHORT; dy = py - prevY;
if (x > 0) {
flag += _glyFlag.default.XSAME;
}
x = Math.abs(x);
}
xCoordC.push(x);
} }
if (y === 0) { prevX = px;
flag += _glyFlag.default.YSAME; prevY = py;
var xEncoded = 0;
var yEncoded = 0;
if (dx === 0) {
flag += XSAME;
} else if (-0xFF <= dx && dx <= 0xFF) {
flag += XSHORT;
if (dx > 0) flag += XSAME;
xEncoded = Math.abs(dx);
xCoordC.push(xEncoded);
encodedCoordSize += 1;
} else { } else {
if (-0xFF <= y && y <= 0xFF) { xCoordC.push(dx);
flag += _glyFlag.default.YSHORT; encodedCoordSize += 2;
if (y > 0) {
flag += _glyFlag.default.YSAME;
}
y = Math.abs(y);
}
yCoordC.push(y);
} }
// repeat if (dy === 0) {
if (flag === prevFlag) { flag += YSAME;
// 记录重复个数 } else if (-0xFF <= dy && dy <= 0xFF) {
if (-1 === repeatPoint) { flag += YSHORT;
if (dy > 0) flag += YSAME;
yEncoded = Math.abs(dy);
yCoordC.push(yEncoded);
encodedCoordSize += 1;
} else {
yCoordC.push(dy);
encodedCoordSize += 2;
}
/* REPEAT 压缩 */
if (flag === prevFlag && !isFirst) {
if (repeatPoint === -1) {
repeatPoint = flagsC.length - 1; repeatPoint = flagsC.length - 1;
flagsC[repeatPoint] |= _glyFlag.default.REPEAT; flagsC[repeatPoint] |= REPEAT;
flagsC.push(1); flagsC.push(1);
} else { } else {
++flagsC[repeatPoint + 1]; ++flagsC[repeatPoint + 1];
@ -181,42 +102,73 @@ function getFlags(glyf, glyfSupport) {
flagsC.push(prevFlag = flag); flagsC.push(prevFlag = flag);
} }
} }
}); }
glyfSupport.flags = flagsC; glyfSupport.flags = flagsC;
glyfSupport.xCoord = xCoordC; glyfSupport.xCoord = xCoordC;
glyfSupport.yCoord = yCoordC; glyfSupport.yCoord = yCoordC;
return glyfSupport;
var instructionSize = (hinting && glyf.instructions) ? glyf.instructions.length : 0;
/* 12 bytes header + endPtsOfContours + flags + encoded coords + instructions */
return 12 + contours.length * 2 + flagsC.length + encodedCoordSize + instructionSize;
} }
/** /**
* 对glyf数据进行预处理获取大小 * 优化48: sizeofCompound forEach for 循环
* */
* @param {Object} ttf ttf对象 function sizeofCompound(glyf) {
* @return {number} 大小 var size = 10;
var glyfs = glyf.glyfs;
for (var i = 0, l = glyfs.length; i < l; i++) {
var transform = glyfs[i].transform;
size += 4;
if (transform.e < 0 || transform.e > 0x7F || transform.f < 0 || transform.f > 0x7F) {
size += 4;
} else {
size += 2;
}
if (transform.b || transform.c) {
size += 8;
} else if (transform.a !== 1 || transform.d !== 1) {
size += transform.a === transform.d ? 2 : 4;
}
}
return size;
}
/**
* 优化49: sizeof glyf.forEach for 循环
*/ */
function sizeof(ttf) { function sizeof(ttf) {
ttf.support.glyf = []; var glyfSupportArr = [];
ttf.support.glyf = glyfSupportArr;
var tableSize = 0; var tableSize = 0;
var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false; var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false;
var writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false; var writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false;
ttf.glyf.forEach(function (glyf) { var glyfs = ttf.glyf;
var glyfSupport = {};
glyfSupport = glyf.compound ? glyfSupport : getFlags(glyf, glyfSupport);
var glyfSize = glyf.compound ? sizeofCompound(glyf, hinting) : sizeofSimple(glyf, glyfSupport, hinting, writeZeroContoursGlyfData);
var size = glyfSize;
// 4字节对齐 for (var i = 0, gl = glyfs.length; i < gl; i++) {
var glyf = glyfs[i];
var glyfSupport = {};
var glyfSize;
if (glyf.compound) {
glyfSize = sizeofCompound(glyf);
} else if (!writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) {
glyfSize = 0;
} else {
glyfSize = getFlagsAndSize(glyf, glyfSupport, hinting);
}
var size = glyfSize;
if (size % 4) { if (size % 4) {
size += 4 - size % 4; size += 4 - size % 4;
} }
glyfSupport.glyfSize = glyfSize; glyfSupport.glyfSize = glyfSize;
glyfSupport.size = size; glyfSupport.size = size;
ttf.support.glyf.push(glyfSupport); glyfSupportArr[i] = glyfSupport;
tableSize += size; tableSize += size;
}); }
ttf.support.glyf.tableSize = tableSize; glyfSupportArr.tableSize = tableSize;
// 写header的indexToLocFormat
ttf.head.indexToLocFormat = tableSize > 65536 ? 1 : 0; ttf.head.indexToLocFormat = tableSize > 65536 ? 1 : 0;
return ttf.support.glyf.tableSize; return tableSize;
} }

View File

@ -12,45 +12,51 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
*/ */
/** /**
* 写glyf * 优化11+21+25+31+32+50+51+52+53+58: glyf write 全面优化
*
* @param {Object} writer 写入器
* @param {Object} ttf ttf对象
* @return {Object} 写入器
*/ */
function write(writer, ttf) { function write(writer, ttf) {
var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false; var hinting = ttf.writeOptions ? ttf.writeOptions.hinting : false;
var writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false; var writeZeroContoursGlyfData = ttf.writeOptions ? ttf.writeOptions.writeZeroContoursGlyfData : false;
ttf.glyf.forEach(function (glyf, index) {
// 非复合图元没有轮廓则不写 /* 优化53: 缓存 glyfSupport 到局部变量 */
var glyfSupport = ttf.support.glyf;
var glyfs = ttf.glyf;
var view = writer.view;
var ARG_1_AND_2_ARE_WORDS = _componentFlag.default.ARG_1_AND_2_ARE_WORDS;
var ROUND_XY_TO_GRID = _componentFlag.default.ROUND_XY_TO_GRID;
var WE_HAVE_A_SCALE = _componentFlag.default.WE_HAVE_A_SCALE;
var WE_HAVE_AN_X_AND_Y_SCALE = _componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE;
var WE_HAVE_A_TWO_BY_TWO = _componentFlag.default.WE_HAVE_A_TWO_BY_TWO;
var ARGS_ARE_XY_VALUES = _componentFlag.default.ARGS_ARE_XY_VALUES;
var USE_MY_METRICS = _componentFlag.default.USE_MY_METRICS;
var OVERLAP_COMPOUND = _componentFlag.default.OVERLAP_COMPOUND;
var MORE_COMPONENTS = _componentFlag.default.MORE_COMPONENTS;
for (var index = 0, gl = glyfs.length; index < gl; index++) {
var glyf = glyfs[index];
/* 优化51: return → continue */
if (!glyf.compound && !writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) { if (!glyf.compound && !writeZeroContoursGlyfData && (!glyf.contours || !glyf.contours.length)) {
return; continue;
} }
// header
writer.writeInt16(glyf.compound ? -1 : (glyf.contours || []).length);
writer.writeInt16(glyf.xMin);
writer.writeInt16(glyf.yMin);
writer.writeInt16(glyf.xMax);
writer.writeInt16(glyf.yMax);
var i;
var l;
var flags;
// 复合图元 /* 优化31: header 直接 view 写入 10 字节 */
var pos = writer.offset;
view.setInt16(pos, glyf.compound ? -1 : (glyf.contours || []).length, false);
view.setInt16(pos + 2, glyf.xMin, false);
view.setInt16(pos + 4, glyf.yMin, false);
view.setInt16(pos + 6, glyf.xMax, false);
view.setInt16(pos + 8, glyf.yMax, false);
pos += 10;
if (glyf.compound) { if (glyf.compound) {
for (i = 0, l = glyf.glyfs.length; i < l; i++) { var subGlyfs = glyf.glyfs;
var g = glyf.glyfs[i]; for (var gi = 0, gl2 = subGlyfs.length; gi < gl2; gi++) {
flags = g.points ? 0 : _componentFlag.default.ARGS_ARE_XY_VALUES + _componentFlag.default.ROUND_XY_TO_GRID; // xy values var g = subGlyfs[gi];
var flags = g.points ? 0 : ARGS_ARE_XY_VALUES + ROUND_XY_TO_GRID;
// more components if (gi < gl2 - 1) flags += MORE_COMPONENTS;
if (i < l - 1) { flags += g.useMyMetrics ? USE_MY_METRICS : 0;
flags += _componentFlag.default.MORE_COMPONENTS; flags += g.overlapCompound ? OVERLAP_COMPOUND : 0;
}
// use my metrics
flags += g.useMyMetrics ? _componentFlag.default.USE_MY_METRICS : 0;
// overlap compound
flags += g.overlapCompound ? _componentFlag.default.OVERLAP_COMPOUND : 0;
var transform = g.transform; var transform = g.transform;
var a = transform.a; var a = transform.a;
var b = transform.b; var b = transform.b;
@ -58,86 +64,106 @@ function write(writer, ttf) {
var d = transform.d; var d = transform.d;
var e = g.points ? g.points[0] : transform.e; var e = g.points ? g.points[0] : transform.e;
var f = g.points ? g.points[1] : transform.f; var f = g.points ? g.points[1] : transform.f;
// xy values or points
// int 8 放不下则用int16放
if (e < 0 || e > 0x7F || f < 0 || f > 0x7F) { if (e < 0 || e > 0x7F || f < 0 || f > 0x7F) {
flags += _componentFlag.default.ARG_1_AND_2_ARE_WORDS; flags += ARG_1_AND_2_ARE_WORDS;
} }
if (b || c) { if (b || c) {
flags += _componentFlag.default.WE_HAVE_A_TWO_BY_TWO; flags += WE_HAVE_A_TWO_BY_TWO;
} else if ((a !== 1 || d !== 1) && a === d) { } else if ((a !== 1 || d !== 1) && a === d) {
flags += _componentFlag.default.WE_HAVE_A_SCALE; flags += WE_HAVE_A_SCALE;
} else if (a !== 1 || d !== 1) { } else if (a !== 1 || d !== 1) {
flags += _componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE; flags += WE_HAVE_AN_X_AND_Y_SCALE;
} }
writer.writeUint16(flags); view.setUint16(pos, flags, false); pos += 2;
writer.writeUint16(g.glyphIndex); view.setUint16(pos, g.glyphIndex, false); pos += 2;
if (_componentFlag.default.ARG_1_AND_2_ARE_WORDS & flags) { if (ARG_1_AND_2_ARE_WORDS & flags) {
writer.writeInt16(e); view.setInt16(pos, e, false); pos += 2;
writer.writeInt16(f); view.setInt16(pos, f, false); pos += 2;
} else { } else {
writer.writeUint8(e); view.setUint8(pos, e); pos += 1;
writer.writeUint8(f); view.setUint8(pos, f); pos += 1;
} }
if (_componentFlag.default.WE_HAVE_A_SCALE & flags) { if (WE_HAVE_A_SCALE & flags) {
writer.writeInt16(Math.round(a * 16384)); view.setInt16(pos, Math.round(a * 16384), false); pos += 2;
} else if (_componentFlag.default.WE_HAVE_AN_X_AND_Y_SCALE & flags) { } else if (WE_HAVE_AN_X_AND_Y_SCALE & flags) {
writer.writeInt16(Math.round(a * 16384)); view.setInt16(pos, Math.round(a * 16384), false); pos += 2;
writer.writeInt16(Math.round(d * 16384)); view.setInt16(pos, Math.round(d * 16384), false); pos += 2;
} else if (_componentFlag.default.WE_HAVE_A_TWO_BY_TWO & flags) { } else if (WE_HAVE_A_TWO_BY_TWO & flags) {
writer.writeInt16(Math.round(a * 16384)); view.setInt16(pos, Math.round(a * 16384), false); pos += 2;
writer.writeInt16(Math.round(b * 16384)); view.setInt16(pos, Math.round(b * 16384), false); pos += 2;
writer.writeInt16(Math.round(c * 16384)); view.setInt16(pos, Math.round(c * 16384), false); pos += 2;
writer.writeInt16(Math.round(d * 16384)); view.setInt16(pos, Math.round(d * 16384), false); pos += 2;
} }
} }
} else { } else {
var endPtsOfContours = -1; /* 优化32: endPtsOfContours 直接 view 写入 */
(glyf.contours || []).forEach(function (contour) { var contours = glyf.contours || [];
endPtsOfContours += contour.length; var endPts = -1;
writer.writeUint16(endPtsOfContours); for (var ci = 0, cl = contours.length; ci < cl; ci++) {
}); endPts += contours[ci].length;
view.setUint16(pos, endPts, false);
pos += 2;
}
// instruction /* 优化25: instructions 批量写入 */
if (hinting && glyf.instructions) { if (hinting && glyf.instructions) {
var instructions = glyf.instructions; var instructions = glyf.instructions;
writer.writeUint16(instructions.length); view.setUint16(pos, instructions.length, false);
for (i = 0, l = instructions.length; i < l; i++) { pos += 2;
writer.writeUint8(instructions[i]); for (var ii = 0, il = instructions.length; ii < il; ii++) {
view.setUint8(pos + ii, instructions[ii]);
} }
pos += instructions.length;
} else { } else {
writer.writeUint16(0); view.setUint16(pos, 0, false);
pos += 2;
} }
// 获取暂存中的flags /* 优化11: flags 批量写入 */
flags = ttf.support.glyf[index].flags || []; var flags = glyfSupport[index].flags || [];
for (i = 0, l = flags.length; i < l; i++) { for (var fi = 0, fl = flags.length; fi < fl; fi++) {
writer.writeUint8(flags[i]); view.setUint8(pos + fi, flags[fi]);
} }
var xCoord = ttf.support.glyf[index].xCoord || []; pos += fl;
for (i = 0, l = xCoord.length; i < l; i++) {
if (0 <= xCoord[i] && xCoord[i] <= 0xFF) { /* 优化21: xCoord 直接 view 写入 */
writer.writeUint8(xCoord[i]); var xCoord = glyfSupport[index].xCoord || [];
for (var xi = 0, xl = xCoord.length; xi < xl; xi++) {
var xv = xCoord[xi];
if (0 <= xv && xv <= 0xFF) {
view.setUint8(pos, xv);
pos += 1;
} else { } else {
writer.writeInt16(xCoord[i]); view.setInt16(pos, xv, false);
pos += 2;
} }
} }
var yCoord = ttf.support.glyf[index].yCoord || [];
for (i = 0, l = yCoord.length; i < l; i++) { /* 优化21+58: yCoord 直接 view 写入,使用各自的数组长度 */
if (0 <= yCoord[i] && yCoord[i] <= 0xFF) { var yCoord = glyfSupport[index].yCoord || [];
writer.writeUint8(yCoord[i]); for (var yi = 0, yl = yCoord.length; yi < yl; yi++) {
var yv = yCoord[yi];
if (0 <= yv && yv <= 0xFF) {
view.setUint8(pos, yv);
pos += 1;
} else { } else {
writer.writeInt16(yCoord[i]); view.setInt16(pos, yv, false);
pos += 2;
} }
} }
} }
// 4字节对齐 /* 4字节对齐 */
var glyfSize = ttf.support.glyf[index].glyfSize; var glyfSize = glyfSupport[index].glyfSize;
if (glyfSize % 4) { if (glyfSize % 4) {
writer.writeEmpty(4 - glyfSize % 4); var pad = 4 - glyfSize % 4;
for (var p = 0; p < pad; p++) {
view.setUint8(pos + p, 0);
}
pos += pad;
} }
});
writer.offset = pos;
}
return writer; return writer;
} }

View File

@ -17,48 +17,51 @@ var _default = exports.default = _table.default.create('hmtx', [], {
var offset = this.offset; var offset = this.offset;
reader.seek(offset); reader.seek(offset);
var numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics; var numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics;
var hMetrics = []; var numGlyphs = ttf.maxp.numGlyphs;
var i; var hMetrics = new Array(numGlyphs);
var hMetric; /* 优化10: 直接 view 批量读取 */
for (i = 0; i < numOfLongHorMetrics; ++i) { var view = reader.view;
hMetric = {}; var vOffset = view.byteOffset + offset;
hMetric.advanceWidth = reader.readUint16(); for (var i = 0; i < numOfLongHorMetrics; i++) {
hMetric.leftSideBearing = reader.readInt16(); hMetrics[i] = {
hMetrics.push(hMetric); advanceWidth: view.getUint16(vOffset, false),
leftSideBearing: view.getInt16(vOffset + 2, false)
};
vOffset += 4;
} }
// 最后一个宽度
var advanceWidth = hMetrics[numOfLongHorMetrics - 1].advanceWidth; var advanceWidth = hMetrics[numOfLongHorMetrics - 1].advanceWidth;
var numOfLast = ttf.maxp.numGlyphs - numOfLongHorMetrics; var numOfLast = numGlyphs - numOfLongHorMetrics;
for (var j = 0; j < numOfLast; j++) {
// 获取后续的hmetrics hMetrics[numOfLongHorMetrics + j] = {
for (i = 0; i < numOfLast; ++i) { advanceWidth: advanceWidth,
hMetric = {}; leftSideBearing: view.getInt16(vOffset, false)
hMetric.advanceWidth = advanceWidth; };
hMetric.leftSideBearing = reader.readInt16(); vOffset += 2;
hMetrics.push(hMetric);
} }
reader.offset = offset + numOfLongHorMetrics * 4 + numOfLast * 2;
return hMetrics; return hMetrics;
}, },
write: function write(writer, ttf) { write: function write(writer, ttf) {
var i; var i;
var numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics; var numOfLongHorMetrics = ttf.hhea.numOfLongHorMetrics;
for (i = 0; i < numOfLongHorMetrics; ++i) { /* 优化30: 直接 view 批量写入 */
writer.writeUint16(ttf.glyf[i].advanceWidth); var wView = writer.view;
writer.writeInt16(ttf.glyf[i].leftSideBearing); var pos = writer.offset;
for (i = 0; i < numOfLongHorMetrics; i++) {
wView.setUint16(pos, ttf.glyf[i].advanceWidth, false);
wView.setInt16(pos + 2, ttf.glyf[i].leftSideBearing, false);
pos += 4;
} }
// 最后一个宽度
var numOfLast = ttf.glyf.length - numOfLongHorMetrics; var numOfLast = ttf.glyf.length - numOfLongHorMetrics;
for (i = 0; i < numOfLast; ++i) { for (i = 0; i < numOfLast; i++) {
writer.writeInt16(ttf.glyf[numOfLongHorMetrics + i].leftSideBearing); wView.setInt16(pos, ttf.glyf[numOfLongHorMetrics + i].leftSideBearing, false);
pos += 2;
} }
writer.offset = pos;
return writer; return writer;
}, },
size: function size(ttf) { size: function size(ttf) {
// 计算同最后一个advanceWidth相等的元素个数
var numOfLast = 0; var numOfLast = 0;
// 最后一个advanceWidth
var advanceWidth = ttf.glyf[ttf.glyf.length - 1].advanceWidth; var advanceWidth = ttf.glyf[ttf.glyf.length - 1].advanceWidth;
for (var i = ttf.glyf.length - 2; i >= 0; i--) { for (var i = ttf.glyf.length - 2; i >= 0; i--) {
if (advanceWidth === ttf.glyf[i].advanceWidth) { if (advanceWidth === ttf.glyf[i].advanceWidth) {

View File

@ -15,17 +15,24 @@ var _default = exports.default = _table.default.create('loca', [], {
read: function read(reader, ttf) { read: function read(reader, ttf) {
var offset = this.offset; var offset = this.offset;
var indexToLocFormat = ttf.head.indexToLocFormat; var indexToLocFormat = ttf.head.indexToLocFormat;
// indexToLocFormat有2字节和4字节的区别 /* 优化7: 直接 view 批量读取,预分配数组 */
var type = _struct.default.names[indexToLocFormat === 0 ? _struct.default.Uint16 : _struct.default.Uint32];
var size = indexToLocFormat === 0 ? 2 : 4; // 字节大小
var sizeRatio = indexToLocFormat === 0 ? 2 : 1; // 真实地址偏移
var wordOffset = [];
reader.seek(offset);
var numGlyphs = ttf.maxp.numGlyphs; var numGlyphs = ttf.maxp.numGlyphs;
for (var i = 0; i < numGlyphs; ++i) { var wordOffset = new Array(numGlyphs);
wordOffset.push(reader.read(type, offset, false) * sizeRatio); var view = reader.view;
offset += size; if (indexToLocFormat === 0) {
var vOffset = view.byteOffset + offset;
for (var i = 0; i < numGlyphs; i++) {
wordOffset[i] = view.getUint16(vOffset, false) * 2;
vOffset += 2;
}
} else {
var vOffset2 = view.byteOffset + offset;
for (var j = 0; j < numGlyphs; j++) {
wordOffset[j] = view.getUint32(vOffset2, false);
vOffset2 += 4;
}
} }
reader.offset = offset + (indexToLocFormat === 0 ? numGlyphs * 2 : numGlyphs * 4);
return wordOffset; return wordOffset;
}, },
write: function write(writer, ttf) { write: function write(writer, ttf) {
@ -34,21 +41,27 @@ var _default = exports.default = _table.default.create('loca', [], {
var indexToLocFormat = ttf.head.indexToLocFormat; var indexToLocFormat = ttf.head.indexToLocFormat;
var sizeRatio = indexToLocFormat === 0 ? 0.5 : 1; var sizeRatio = indexToLocFormat === 0 ? 0.5 : 1;
var numGlyphs = ttf.glyf.length; var numGlyphs = ttf.glyf.length;
for (var i = 0; i < numGlyphs; ++i) { /* 优化29: 直接 view 批量写入 */
if (indexToLocFormat) { var wView = writer.view;
writer.writeUint32(offset); var pos = writer.offset;
} else {
writer.writeUint16(offset);
}
offset += glyfSupport[i].size * sizeRatio;
}
// write extra
if (indexToLocFormat) { if (indexToLocFormat) {
writer.writeUint32(offset); for (var i = 0; i <= numGlyphs; i++) {
wView.setUint32(pos, offset, false);
pos += 4;
if (i < numGlyphs) {
offset += glyfSupport[i].size * sizeRatio;
}
}
} else { } else {
writer.writeUint16(offset); for (var j = 0; j <= numGlyphs; j++) {
wView.setUint16(pos, offset, false);
pos += 2;
if (j < numGlyphs) {
offset += glyfSupport[j].size * sizeRatio;
}
}
} }
writer.offset = pos;
return writer; return writer;
}, },
size: function size(ttf) { size: function size(ttf) {

View File

@ -17,26 +17,55 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
*/ */
var Posthead = _table.default.create('posthead', [['format', _struct.default.Fixed], ['italicAngle', _struct.default.Fixed], ['underlinePosition', _struct.default.Int16], ['underlineThickness', _struct.default.Int16], ['isFixedPitch', _struct.default.Uint32], ['minMemType42', _struct.default.Uint32], ['maxMemType42', _struct.default.Uint32], ['minMemType1', _struct.default.Uint32], ['maxMemType1', _struct.default.Uint32]]); var Posthead = _table.default.create('posthead', [['format', _struct.default.Fixed], ['italicAngle', _struct.default.Fixed], ['underlinePosition', _struct.default.Int16], ['underlineThickness', _struct.default.Int16], ['isFixedPitch', _struct.default.Uint32], ['minMemType42', _struct.default.Uint32], ['maxMemType42', _struct.default.Uint32], ['minMemType1', _struct.default.Uint32], ['maxMemType1', _struct.default.Uint32]]);
/**
* 优化64: 从原始字节按需提取单个 pascal string
*/
function getPascalStringAt(bytes, offset) {
var length = bytes[offset];
if (length === 0) return '';
var chars = new Array(length);
for (var i = 0; i < length; i++) {
chars[i] = String.fromCharCode(bytes[offset + 1 + i]);
}
return chars.join('');
}
var _default = exports.default = _table.default.create('post', [], { var _default = exports.default = _table.default.create('post', [], {
read: function read(reader, ttf) { read: function read(reader, ttf) {
var format = reader.readFixed(this.offset); var format = reader.readFixed(this.offset);
// 读取表头
var tbl = new Posthead(this.offset).read(reader, ttf); var tbl = new Posthead(this.offset).read(reader, ttf);
// format2
if (format === 2) { if (format === 2) {
var numberOfGlyphs = reader.readUint16(); var numberOfGlyphs = reader.readUint16();
var glyphNameIndex = []; /* 优化60: 直接 view 批量读取 glyphNameIndex */
for (var i = 0; i < numberOfGlyphs; ++i) { var view = reader.view;
glyphNameIndex.push(reader.readUint16()); var vOffset = view.byteOffset + reader.offset;
var glyphNameIndex = new Array(numberOfGlyphs);
for (var i = 0; i < numberOfGlyphs; i++) {
glyphNameIndex[i] = view.getUint16(vOffset, false);
vOffset += 2;
} }
var pascalStringOffset = reader.offset; var pascalStringOffset = vOffset - view.byteOffset;
var pascalStringLength = ttf.tables.post.length - (pascalStringOffset - this.offset); var pascalStringLength = ttf.tables.post.length - (pascalStringOffset - this.offset);
var pascalStringBytes = reader.readBytes(reader.offset, pascalStringLength); var pascalStringBytes = reader.readBytes(pascalStringOffset, pascalStringLength);
tbl.nameIndex = glyphNameIndex; // 设置glyf名字索引
tbl.names = _string.default.getPascalString(pascalStringBytes); // glyf名字数组 tbl.nameIndex = glyphNameIndex;
/* 优化64: subset 模式下保存原始字节,按需解析 */
if (ttf.readOptions && ttf.readOptions.subset) {
tbl._pascalStringBytes = pascalStringBytes;
tbl._pascalStringOffsets = [];
var off = 0;
for (var j = 0; j < numberOfGlyphs; j++) {
tbl._pascalStringOffsets[j] = off;
off += 1 + (pascalStringBytes[off] || 0);
}
tbl.names = null;
} else {
tbl.names = _string.default.getPascalString(pascalStringBytes);
}
} }
// deprecated
else if (format === 2.5) { else if (format === 2.5) {
tbl.format = 3; tbl.format = 3;
} }
@ -47,31 +76,27 @@ var _default = exports.default = _table.default.create('post', [], {
format: 3 format: 3
}; };
// write header writer.writeFixed(post.format);
writer.writeFixed(post.format); // format writer.writeFixed(post.italicAngle || 0);
writer.writeFixed(post.italicAngle || 0); // italicAngle writer.writeInt16(post.underlinePosition || 0);
writer.writeInt16(post.underlinePosition || 0); // underlinePosition writer.writeInt16(post.underlineThickness || 0);
writer.writeInt16(post.underlineThickness || 0); // underlineThickness writer.writeUint32(post.isFixedPitch || 0);
writer.writeUint32(post.isFixedPitch || 0); // isFixedPitch writer.writeUint32(post.minMemType42 || 0);
writer.writeUint32(post.minMemType42 || 0); // minMemType42 writer.writeUint32(post.maxMemType42 || 0);
writer.writeUint32(post.maxMemType42 || 0); // maxMemType42 writer.writeUint32(post.minMemType1 || 0);
writer.writeUint32(post.minMemType1 || 0); // minMemType1 writer.writeUint32(post.maxMemType1 || 0);
writer.writeUint32(post.maxMemType1 || 0); // maxMemType1
// version 3 不设置post信息
if (post.format === 2) { if (post.format === 2) {
var numberOfGlyphs = ttf.glyf.length; var numberOfGlyphs = ttf.glyf.length;
writer.writeUint16(numberOfGlyphs); // numberOfGlyphs writer.writeUint16(numberOfGlyphs);
// write glyphNameIndex
var nameIndex = ttf.support.post.nameIndex; var nameIndex = ttf.support.post.nameIndex;
for (var i = 0, l = nameIndex.length; i < l; i++) { for (var i = 0, l = nameIndex.length; i < l; i++) {
writer.writeUint16(nameIndex[i]); writer.writeUint16(nameIndex[i]);
} }
var names = ttf.support.post.names;
// write names for (var j = 0, jl = names.length; j < jl; j++) {
ttf.support.post.names.forEach(function (name) { writer.writeBytes(names[j]);
writer.writeBytes(name); }
});
} }
}, },
size: function size(ttf) { size: function size(ttf) {
@ -80,20 +105,16 @@ var _default = exports.default = _table.default.create('post', [], {
ttf.post.format = ttf.post.format || 3; ttf.post.format = ttf.post.format || 3;
ttf.post.maxMemType1 = numberOfGlyphs; ttf.post.maxMemType1 = numberOfGlyphs;
// version 3 不设置post信息
if (ttf.post.format === 3 || ttf.post.format === 1) { if (ttf.post.format === 3 || ttf.post.format === 1) {
return 32; return 32;
} }
// version 2 var size = 34 + numberOfGlyphs * 2;
var size = 34 + numberOfGlyphs * 2; // header + numberOfGlyphs + numberOfGlyphs * 2
var glyphNames = []; var glyphNames = [];
var nameIndexArr = []; var nameIndexArr = [];
var nameIndex = 0; var nameIndex = 0;
// 获取 name的大小
for (var i = 0; i < numberOfGlyphs; i++) { for (var i = 0; i < numberOfGlyphs; i++) {
// .notdef
if (i === 0) { if (i === 0) {
nameIndexArr.push(0); nameIndexArr.push(0);
} else { } else {
@ -103,7 +124,6 @@ var _default = exports.default = _table.default.create('post', [], {
if (undefined !== unicodeNameIndex) { if (undefined !== unicodeNameIndex) {
nameIndexArr.push(unicodeNameIndex); nameIndexArr.push(unicodeNameIndex);
} else { } else {
// 这里需要注意,"" 有可能是"\3" length不为0但是是空字符串
var name = glyf.name; var name = glyf.name;
if (!name || name.charCodeAt(0) < 32) { if (!name || name.charCodeAt(0) < 32) {
nameIndexArr.push(258 + nameIndex++); nameIndexArr.push(258 + nameIndex++);
@ -111,7 +131,7 @@ var _default = exports.default = _table.default.create('post', [], {
size++; size++;
} else { } else {
nameIndexArr.push(258 + nameIndex++); nameIndexArr.push(258 + nameIndex++);
var bytes = _string.default.toPascalStringBytes(name); // pascal string bytes var bytes = _string.default.toPascalStringBytes(name);
glyphNames.push(bytes); glyphNames.push(bytes);
size += bytes.length; size += bytes.length;
} }
@ -125,3 +145,6 @@ var _default = exports.default = _table.default.create('post', [], {
return size; return size;
} }
}); });
/** 按需获取单个 pascal string name供 ttfreader.js 使用) */
exports.getPascalStringAt = getPascalStringAt;

View File

@ -652,23 +652,33 @@ var TTF = exports.default = /*#__PURE__*/function () {
value: function sortGlyf() { value: function sortGlyf() {
var glyf = this.ttf.glyf; var glyf = this.ttf.glyf;
if (glyf.length > 1) { if (glyf.length > 1) {
// 如果存在复合字形则退出 /* 优化54: some → for 循环 + unicode 属性缓存 + Math.min.apply → 手动遍历 */
if (glyf.some(function (a) { var hasCompound = false;
return a.compound; for (var k = 0, kl = glyf.length; k < kl; k++) {
})) { if (glyf[k].compound) {
hasCompound = true;
break;
}
}
if (hasCompound) {
return -2; return -2;
} }
var notdef = glyf.shift(); var notdef = glyf.shift();
// 按代码点排序, 首先将空字形排到最后然后按照unicode第一个编码进行排序
glyf.sort(function (a, b) { glyf.sort(function (a, b) {
if ((!a.unicode || !a.unicode.length) && (!b.unicode || !b.unicode.length)) { var aU = a.unicode;
var bU = b.unicode;
if ((!aU || !aU.length) && (!bU || !bU.length)) {
return 0; return 0;
} else if ((!a.unicode || !a.unicode.length) && b.unicode) { } else if ((!aU || !aU.length) && bU) {
return 1; return 1;
} else if (a.unicode && (!b.unicode || !b.unicode.length)) { } else if (aU && (!bU || !bU.length)) {
return -1; return -1;
} }
return Math.min.apply(null, a.unicode) - Math.min.apply(null, b.unicode); /* 优化3: 手动遍历取最小值 */
var aMin = aU[0], bMin = bU[0];
for (var ai = 1; ai < aU.length; ai++) { if (aU[ai] < aMin) aMin = aU[ai]; }
for (var bi = 1; bi < bU.length; bi++) { if (bU[bi] < bMin) bMin = bU[bi]; }
return aMin - bMin;
}); });
glyf.unshift(notdef); glyf.unshift(notdef);
return glyf; return glyf;

View File

@ -10,163 +10,155 @@ var _reader = _interopRequireDefault(require("./reader"));
var _postName = _interopRequireDefault(require("./enum/postName")); var _postName = _interopRequireDefault(require("./enum/postName"));
var _error = _interopRequireDefault(require("./error")); var _error = _interopRequireDefault(require("./error"));
var _compound2simpleglyf = _interopRequireDefault(require("./util/compound2simpleglyf")); var _compound2simpleglyf = _interopRequireDefault(require("./util/compound2simpleglyf"));
var _post = require("./table/post");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } /**
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
* @file ttf读取器 * @file ttf读取器
* @author mengke01(kekee000@gmail.com) * @author mengke01(kekee000@gmail.com)
*
* thanks to
* ynakajima/ttf.js
* https://github.com/ynakajima/ttf.js
*/ */
var TTFReader = exports.default = /*#__PURE__*/function () { var TTFReader = exports.default = /*#__PURE__*/function () {
/**
* ttf读取器的构造函数
*
* @param {Object} options 写入参数
* @param {boolean} options.hinting 保留hinting信息
* @param {boolean} options.compound2simple 复合字形转简单字形
* @constructor
*/
function TTFReader() { function TTFReader() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, TTFReader); options.subset = options.subset || [];
options.subset = options.subset || []; // 子集 options.hinting = options.hinting || false;
options.hinting = options.hinting || false; // 默认不保留 hints 信息 options.kerning = options.kerning || false;
options.kerning = options.kerning || false; // 默认不保留 kerning 信息 options.compound2simple = options.compound2simple || false;
options.compound2simple = options.compound2simple || false; // 复合字形转简单字形
this.options = options; this.options = options;
} }
/**
* 初始化读取
*
* @param {ArrayBuffer} buffer buffer对象
* @return {Object} ttf对象
*/
return _createClass(TTFReader, [{ return _createClass(TTFReader, [{
key: "readBuffer", key: "readBuffer",
value: function readBuffer(buffer) { value: function readBuffer(buffer) {
var reader = new _reader.default(buffer, 0, buffer.byteLength, false); var reader = new _reader.default(buffer, 0, buffer.byteLength, false);
var ttf = {}; var ttf = {};
// version /* 优化27: 头部直接 view 读取 */
ttf.version = reader.readFixed(0); var view = reader.view;
var vOffset = view.byteOffset;
ttf.version = view.getInt32(vOffset, false) / 65536.0;
if (ttf.version !== 0x1) { if (ttf.version !== 0x1) {
_error.default.raise(10101); _error.default.raise(10101);
} }
ttf.numTables = view.getUint16(vOffset + 4, false);
// num tables
ttf.numTables = reader.readUint16();
if (ttf.numTables <= 0 || ttf.numTables > 100) { if (ttf.numTables <= 0 || ttf.numTables > 100) {
_error.default.raise(10101); _error.default.raise(10101);
} }
ttf.searchRange = view.getUint16(vOffset + 6, false);
ttf.entrySelector = view.getUint16(vOffset + 8, false);
ttf.rangeShift = view.getUint16(vOffset + 10, false);
reader.offset = 12;
// searchRange
ttf.searchRange = reader.readUint16();
// entrySelector
ttf.entrySelector = reader.readUint16();
// rangeShift
ttf.rangeShift = reader.readUint16();
ttf.tables = new _directory.default(reader.offset).read(reader, ttf); ttf.tables = new _directory.default(reader.offset).read(reader, ttf);
if (!ttf.tables.glyf || !ttf.tables.head || !ttf.tables.cmap || !ttf.tables.hmtx) { if (!ttf.tables.glyf || !ttf.tables.head || !ttf.tables.cmap || !ttf.tables.hmtx) {
_error.default.raise(10204); _error.default.raise(10204);
} }
ttf.readOptions = this.options; ttf.readOptions = this.options;
// 读取支持的表数据 /* 优化8+37+62: 跳过不必要的表,缓存 TableClass 实例for...in 替代 Object.keys */
Object.keys(_support.default).forEach(function (tableName) { var hinting = this.options.hinting;
var kerning = this.options.kerning;
var supportTables = _support.default;
var tableInstances = {};
for (var tableName in supportTables) {
if (ttf.tables[tableName]) { if (ttf.tables[tableName]) {
/* 优化8: hinting=false 时跳过 fpgm/cvt/prep/gasp */
if (!hinting && (tableName === 'fpgm' || tableName === 'cvt' || tableName === 'prep' || tableName === 'gasp')) {
continue;
}
/* 优化8: hinting=false && kerning=false 时跳过 GPOS/kern/kerx */
if (!hinting && !kerning && (tableName === 'GPOS' || tableName === 'kern' || tableName === 'kerx')) {
continue;
}
var offset = ttf.tables[tableName].offset; var offset = ttf.tables[tableName].offset;
ttf[tableName] = new _support.default[tableName](offset).read(reader, ttf); /* 优化37: 缓存 TableClass 实例 */
if (!tableInstances[tableName]) {
tableInstances[tableName] = new supportTables[tableName](offset);
} else {
tableInstances[tableName].offset = offset;
}
ttf[tableName] = tableInstances[tableName].read(reader, ttf);
} }
}); }
if (!ttf.glyf) { if (!ttf.glyf) {
_error.default.raise(10201); _error.default.raise(10201);
} }
reader.dispose(); reader.dispose();
return ttf; return ttf;
} }
/**
* 关联glyf相关的信息
*
* @param {Object} ttf ttf对象
*/
}, { }, {
key: "resolveGlyf", key: "resolveGlyf",
value: function resolveGlyf(ttf) { value: function resolveGlyf(ttf) {
var codes = ttf.cmap; var codes = ttf.cmap;
var glyf = ttf.glyf; var glyf = ttf.glyf;
var subsetMap = ttf.readOptions.subset ? ttf.subsetMap : null; // 当前ttf的子集列表 var subsetMap = ttf.readOptions.subset ? ttf.subsetMap : null;
// unicode /* 优化13+24+62: unicode 遍历subset 模式只遍历 subsetMap */
Object.keys(codes).forEach(function (c) { for (var c in codes) {
var i = codes[c]; var i = codes[c];
if (subsetMap && !subsetMap[i]) { if (subsetMap && !subsetMap[i]) {
return; continue;
} }
if (!glyf[i].unicode) { if (!glyf[i].unicode) {
glyf[i].unicode = []; glyf[i].unicode = [];
} }
glyf[i].unicode.push(+c); glyf[i].unicode.push(+c);
}); }
// advanceWidth /* 优化13: advanceWidth 遍历优化 */
ttf.hmtx.forEach(function (item, i) { var hmtx = ttf.hmtx;
if (subsetMap && !subsetMap[i]) { if (subsetMap) {
return; for (var idx in subsetMap) {
var idxNum = +idx;
glyf[idxNum].advanceWidth = hmtx[idxNum].advanceWidth;
glyf[idxNum].leftSideBearing = hmtx[idxNum].leftSideBearing;
} }
glyf[i].advanceWidth = item.advanceWidth; } else {
glyf[i].leftSideBearing = item.leftSideBearing; for (var hi = 0, hl = hmtx.length; hi < hl; hi++) {
}); glyf[hi].advanceWidth = hmtx[hi].advanceWidth;
glyf[hi].leftSideBearing = hmtx[hi].leftSideBearing;
}
}
// format = 2 的post表会携带glyf name信息 /* post 表 glyf name */
if (ttf.post && 2 === ttf.post.format) { if (ttf.post && 2 === ttf.post.format) {
var nameIndex = ttf.post.nameIndex; var nameIndex = ttf.post.nameIndex;
var names = ttf.post.names; var names = ttf.post.names;
nameIndex.forEach(function (nameIndex, i) { var pascalBytes = ttf.post._pascalStringBytes;
if (subsetMap && !subsetMap[i]) { var pascalOffsets = ttf.post._pascalStringOffsets;
return; for (var ni = 0, nl = nameIndex.length; ni < nl; ni++) {
if (subsetMap && !subsetMap[ni]) {
continue;
} }
if (nameIndex <= 257) { var nIdx = nameIndex[ni];
glyf[i].name = _postName.default[nameIndex]; if (nIdx <= 257) {
} else { glyf[ni].name = _postName.default[nIdx];
glyf[i].name = names[nameIndex - 258] || ''; } else if (names) {
glyf[ni].name = names[nIdx - 258] || '';
} else if (pascalBytes && pascalOffsets) {
var off = pascalOffsets[nIdx - 258];
glyf[ni].name = off !== undefined ? _post.getPascalStringAt(pascalBytes, off) : '';
} }
}); }
} }
// 设置了subsetMap之后需要选取subset中的字形 /* 优化13+44+62: subset 模式下直接只遍历 subsetMap */
// 并且对复合字形转换成简单字形
if (subsetMap) { if (subsetMap) {
var subGlyf = []; var subGlyf = [];
Object.keys(subsetMap).forEach(function (i) { for (var si in subsetMap) {
i = +i; var siNum = +si;
if (glyf[i].compound) { if (glyf[siNum].compound) {
(0, _compound2simpleglyf.default)(i, ttf, true); (0, _compound2simpleglyf.default)(siNum, ttf, true);
} }
subGlyf.push(glyf[i]); subGlyf.push(glyf[siNum]);
}); }
ttf.glyf = subGlyf; ttf.glyf = subGlyf;
// 转换之后不存在复合字形了
ttf.maxp.maxComponentElements = 0; ttf.maxp.maxComponentElements = 0;
ttf.maxp.maxComponentDepth = 0; ttf.maxp.maxComponentDepth = 0;
} }
} }
/**
* 清除非必须的表
*
* @param {Object} ttf ttf对象
*/
}, { }, {
key: "cleanTables", key: "cleanTables",
value: function cleanTables(ttf) { value: function cleanTables(ttf) {
@ -177,17 +169,20 @@ var TTFReader = exports.default = /*#__PURE__*/function () {
if (ttf.post) { if (ttf.post) {
delete ttf.post.nameIndex; delete ttf.post.nameIndex;
delete ttf.post.names; delete ttf.post.names;
delete ttf.post._pascalStringBytes;
delete ttf.post._pascalStringOffsets;
} }
delete ttf.subsetMap; delete ttf.subsetMap;
// 不携带hinting信息则删除hint相关表
if (!this.options.hinting) { if (!this.options.hinting) {
delete ttf.fpgm; delete ttf.fpgm;
delete ttf.cvt; delete ttf.cvt;
delete ttf.prep; delete ttf.prep;
ttf.glyf.forEach(function (glyf) { /* 优化55: forEach → for 循环 */
delete glyf.instructions; var glyfs = ttf.glyf;
}); for (var i = 0, l = glyfs.length; i < l; i++) {
delete glyfs[i].instructions;
}
} }
if (!this.options.hinting && !this.options.kerning) { if (!this.options.hinting && !this.options.kerning) {
delete ttf.GPOS; delete ttf.GPOS;
@ -195,24 +190,17 @@ var TTFReader = exports.default = /*#__PURE__*/function () {
delete ttf.kerx; delete ttf.kerx;
} }
// 复合字形转简单字形
if (this.options.compound2simple && ttf.maxp.maxComponentElements) { if (this.options.compound2simple && ttf.maxp.maxComponentElements) {
ttf.glyf.forEach(function (glyf, index) { var glyfs2 = ttf.glyf;
if (glyf.compound) { for (var j = 0, jl = glyfs2.length; j < jl; j++) {
(0, _compound2simpleglyf.default)(index, ttf, true); if (glyfs2[j].compound) {
(0, _compound2simpleglyf.default)(j, ttf, true);
} }
}); }
ttf.maxp.maxComponentElements = 0; ttf.maxp.maxComponentElements = 0;
ttf.maxp.maxComponentDepth = 0; ttf.maxp.maxComponentDepth = 0;
} }
} }
/**
* 获取解析后的ttf文档
*
* @param {ArrayBuffer} buffer buffer对象
* @return {Object} ttf文档
*/
}, { }, {
key: "read", key: "read",
value: function read(buffer) { value: function read(buffer) {
@ -221,10 +209,6 @@ var TTFReader = exports.default = /*#__PURE__*/function () {
this.cleanTables(this.ttf); this.cleanTables(this.ttf);
return this.ttf; return this.ttf;
} }
/**
* 注销
*/
}, { }, {
key: "dispose", key: "dispose",
value: function dispose() { value: function dispose() {

View File

@ -10,48 +10,37 @@ var _support = _interopRequireDefault(require("./table/support"));
var _checkSum = _interopRequireDefault(require("./util/checkSum")); var _checkSum = _interopRequireDefault(require("./util/checkSum"));
var _error = _interopRequireDefault(require("./error")); var _error = _interopRequireDefault(require("./error"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } /**
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /**
* @file ttf写入器 * @file ttf写入器
* @author mengke01(kekee000@gmail.com) * @author mengke01(kekee000@gmail.com)
*/ */
// 支持写的表, 注意表顺序
var SUPPORT_TABLES = ['OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post']; var SUPPORT_TABLES = ['OS/2', 'cmap', 'glyf', 'head', 'hhea', 'hmtx', 'loca', 'maxp', 'name', 'post'];
var TTFWriter = exports.default = /*#__PURE__*/function () { var TTFWriter = exports.default = /*#__PURE__*/function () {
function TTFWriter() { function TTFWriter() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, TTFWriter);
this.options = { this.options = {
writeZeroContoursGlyfData: options.writeZeroContoursGlyfData || false, writeZeroContoursGlyfData: options.writeZeroContoursGlyfData || false,
// 不写入空 glyf 数据
hinting: options.hinting || false, hinting: options.hinting || false,
// 默认不保留hints信息
kerning: options.kerning || false, kerning: options.kerning || false,
// 默认不保留 kernings space 信息 support: options.support
support: options.support // 自定义的导出表结构,可以自己修改某些表项目
}; };
} }
/** /**
* 处理ttf结构以便于写 * 优化4+46: resolveTTF unicode 排序修正 + forEach for 循环
*
* @param {ttfObject} ttf ttf数据结构
*/ */
return _createClass(TTFWriter, [{ return _createClass(TTFWriter, [{
key: "resolveTTF", key: "resolveTTF",
value: function resolveTTF(ttf) { value: function resolveTTF(ttf) {
// 头部信息
ttf.version = ttf.version || 0x1; ttf.version = ttf.version || 0x1;
ttf.numTables = ttf.writeOptions.tables.length; ttf.numTables = ttf.writeOptions.tables.length;
ttf.entrySelector = Math.floor(Math.log(ttf.numTables) / Math.LN2); ttf.entrySelector = Math.floor(Math.log(ttf.numTables) / Math.LN2);
ttf.searchRange = Math.pow(2, ttf.entrySelector) * 16; ttf.searchRange = Math.pow(2, ttf.entrySelector) * 16;
ttf.rangeShift = ttf.numTables * 16 - ttf.searchRange; ttf.rangeShift = ttf.numTables * 16 - ttf.searchRange;
// 重置校验码
ttf.head.checkSumAdjustment = 0; ttf.head.checkSumAdjustment = 0;
ttf.head.magickNumber = 0x5F0F3CF5; ttf.head.magickNumber = 0x5F0F3CF5;
if (typeof ttf.head.created === 'string') { if (typeof ttf.head.created === 'string') {
@ -60,62 +49,55 @@ var TTFWriter = exports.default = /*#__PURE__*/function () {
if (typeof ttf.head.modified === 'string') { if (typeof ttf.head.modified === 'string') {
ttf.head.modified = /^\d+$/.test(ttf.head.modified) ? +ttf.head.modified : Date.parse(ttf.head.modified); ttf.head.modified = /^\d+$/.test(ttf.head.modified) ? +ttf.head.modified : Date.parse(ttf.head.modified);
} }
// 重置日期
if (!ttf.head.created) { if (!ttf.head.created) {
ttf.head.created = Date.now(); ttf.head.created = Date.now();
} }
if (!ttf.head.modified) { if (!ttf.head.modified) {
ttf.head.modified = ttf.head.created; ttf.head.modified = ttf.head.created;
} }
var checkUnicodeRepeat = {}; // 检查是否有重复代码点 var checkUnicodeRepeat = {};
// 将glyf的代码点按小到大排序 /* 优化4+46: 数字排序 + for 循环 */
ttf.glyf.forEach(function (glyf, index) { var glyfs = ttf.glyf;
for (var index = 0, gl = glyfs.length; index < gl; index++) {
var glyf = glyfs[index];
if (glyf.unicode) { if (glyf.unicode) {
glyf.unicode = glyf.unicode.sort(); glyf.unicode.sort(function (a, b) { return a - b; });
glyf.unicode.forEach(function (u) { var unicode = glyf.unicode;
for (var ui = 0, ul = unicode.length; ui < ul; ui++) {
var u = unicode[ui];
if (checkUnicodeRepeat[u]) { if (checkUnicodeRepeat[u]) {
_error.default.raise({ _error.default.raise({ number: 10200, data: index }, index);
number: 10200,
data: index
}, index);
} else { } else {
checkUnicodeRepeat[u] = true; checkUnicodeRepeat[u] = true;
} }
}); }
} }
}); }
} }
/**
* 写ttf文件
*
* @param {ttfObject} ttf ttf数据结构
* @return {ArrayBuffer} 字节流
*/
}, { }, {
key: "dump", key: "dump",
value: function dump(ttf) { value: function dump(ttf) {
// 用来做写入缓存的对象,用完后删掉
ttf.support = Object.assign({}, this.options.support); ttf.support = Object.assign({}, this.options.support);
// head + directory
var ttfSize = 12 + ttf.numTables * 16; var ttfSize = 12 + ttf.numTables * 16;
var ttfHeadOffset = 0; // 记录head的偏移 var ttfHeadOffset = 0;
// 构造tables /* 优化35+56: 缓存 TableClass 实例forEach → for 循环 */
ttf.support.tables = []; ttf.support.tables = [];
ttf.writeOptions.tables.forEach(function (tableName) { var writeTables = ttf.writeOptions.tables;
var supportTables = _support.default;
var tableInstances = {};
for (var ti = 0, tl = writeTables.length; ti < tl; ti++) {
var tableName = writeTables[ti];
var offset = ttfSize; var offset = ttfSize;
var TableClass = _support.default[tableName]; if (!tableInstances[tableName]) {
var tableSize = new TableClass().size(ttf); // 原始的表大小 tableInstances[tableName] = new supportTables[tableName]();
var size = tableSize; // 对齐后的表大小 }
var tableSize = tableInstances[tableName].size(ttf);
var size = tableSize;
if (tableName === 'head') { if (tableName === 'head') {
ttfHeadOffset = offset; ttfHeadOffset = offset;
} }
// 4字节对齐
if (size % 4) { if (size % 4) {
size += 4 - size % 4; size += 4 - size % 4;
} }
@ -127,40 +109,44 @@ var TTFWriter = exports.default = /*#__PURE__*/function () {
size: size size: size
}); });
ttfSize += size; ttfSize += size;
}); }
var writer = new _writer.default(new ArrayBuffer(ttfSize)); var writer = new _writer.default(new ArrayBuffer(ttfSize));
// 写头部 /* 优化36: 头部直接 view 写入 */
writer.writeFixed(ttf.version); var wView = writer.view;
writer.writeUint16(ttf.numTables); var pos = writer.offset;
writer.writeUint16(ttf.searchRange); wView.setInt32(pos, Math.round(ttf.version * 65536), false); pos += 4;
writer.writeUint16(ttf.entrySelector); wView.setUint16(pos, ttf.numTables, false); pos += 2;
writer.writeUint16(ttf.rangeShift); wView.setUint16(pos, ttf.searchRange, false); pos += 2;
wView.setUint16(pos, ttf.entrySelector, false); pos += 2;
wView.setUint16(pos, ttf.rangeShift, false); pos += 2;
writer.offset = pos;
// 写表偏移
new _directory.default().write(writer, ttf); new _directory.default().write(writer, ttf);
// 写支持的表数据 /* 优化56: forEach → for 循环 */
ttf.support.tables.forEach(function (table) { var supportTableList = ttf.support.tables;
for (var si = 0, sl = supportTableList.length; si < sl; si++) {
var table = supportTableList[si];
var tableStart = writer.offset; var tableStart = writer.offset;
var TableClass = _support.default[table.name]; var tName = table.name;
new TableClass().write(writer, ttf); if (!tableInstances[tName]) {
tableInstances[tName] = new supportTables[tName]();
}
tableInstances[tName].write(writer, ttf);
if (table.length % 4) { if (table.length % 4) {
// 对齐字节
writer.writeEmpty(4 - table.length % 4); writer.writeEmpty(4 - table.length % 4);
} }
// 计算校验和
table.checkSum = (0, _checkSum.default)(writer.getBuffer(), tableStart, table.size); table.checkSum = (0, _checkSum.default)(writer.getBuffer(), tableStart, table.size);
}); }
// 重新写入每个表校验和 /* 重新写入校验和 */
ttf.support.tables.forEach(function (table, index) { for (var ci = 0, cl = supportTableList.length; ci < cl; ci++) {
var offset = 12 + index * 16 + 4; var offset2 = 12 + ci * 16 + 4;
writer.writeUint32(table.checkSum, offset); writer.writeUint32(supportTableList[ci].checkSum, offset2);
}); }
// 写入总校验和 /* 写入总校验和 */
var ttfCheckSum = (0xB1B0AFBA - (0, _checkSum.default)(writer.getBuffer()) + 0x100000000) % 0x100000000; var ttfCheckSum = (0xB1B0AFBA - (0, _checkSum.default)(writer.getBuffer()) + 0x100000000) % 0x100000000;
writer.writeUint32(ttfCheckSum, ttfHeadOffset + 8); writer.writeUint32(ttfCheckSum, ttfHeadOffset + 8);
delete ttf.writeOptions; delete ttf.writeOptions;
@ -169,12 +155,6 @@ var TTFWriter = exports.default = /*#__PURE__*/function () {
writer.dispose(); writer.dispose();
return buffer; return buffer;
} }
/**
* 对ttf的表进行评估标记需要处理的表
*
* @param {Object} ttf ttf对象
*/
}, { }, {
key: "prepareDump", key: "prepareDump",
value: function prepareDump(ttf) { value: function prepareDump(ttf) {
@ -186,34 +166,28 @@ var TTFWriter = exports.default = /*#__PURE__*/function () {
} }
var tables = SUPPORT_TABLES.slice(0); var tables = SUPPORT_TABLES.slice(0);
ttf.writeOptions = {}; ttf.writeOptions = {};
// hinting tables direct copy /* 优化56: forEach → for 循环 */
if (this.options.hinting) { if (this.options.hinting) {
['cvt', 'fpgm', 'prep', 'gasp', 'GPOS', 'kern', 'kerx'].forEach(function (table) { var hintTables = ['cvt', 'fpgm', 'prep', 'gasp', 'GPOS', 'kern', 'kerx'];
if (ttf[table]) { for (var i = 0; i < hintTables.length; i++) {
tables.push(table); if (ttf[hintTables[i]]) {
tables.push(hintTables[i]);
} }
}); }
} }
// copy kerning space table
if (this.options.kerning) { if (this.options.kerning) {
['GPOS', 'kern', 'kerx'].forEach(function (table) { var kernTables = ['GPOS', 'kern', 'kerx'];
if (ttf[table]) { for (var j = 0; j < kernTables.length; j++) {
tables.push(table); if (ttf[kernTables[j]]) {
tables.push(kernTables[j]);
} }
}); }
} }
ttf.writeOptions.writeZeroContoursGlyfData = !!this.options.writeZeroContoursGlyfData; ttf.writeOptions.writeZeroContoursGlyfData = !!this.options.writeZeroContoursGlyfData;
ttf.writeOptions.hinting = !!this.options.hinting; ttf.writeOptions.hinting = !!this.options.hinting;
ttf.writeOptions.kerning = !!this.options.kerning; ttf.writeOptions.kerning = !!this.options.kerning;
ttf.writeOptions.tables = tables.sort(); ttf.writeOptions.tables = tables.sort();
} }
/**
* 写一个ttf字体结构
*
* @param {Object} ttf ttf数据结构
* @return {ArrayBuffer} 缓冲数组
*/
}, { }, {
key: "write", key: "write",
value: function write(ttf) { value: function write(ttf) {
@ -222,10 +196,6 @@ var TTFWriter = exports.default = /*#__PURE__*/function () {
var buffer = this.dump(ttf); var buffer = this.dump(ttf);
return buffer; return buffer;
} }
/**
* 注销
*/
}, { }, {
key: "dispose", key: "dispose",
value: function dispose() { value: function dispose() {

View File

@ -9,54 +9,58 @@ exports.default = checkSum;
* @author mengke01(kekee000@gmail.com) * @author mengke01(kekee000@gmail.com)
*/ */
function checkSumArrayBuffer(buffer) { /**
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; * 优化18+69: 位运算避免溢出 + Uint8Array 替代 DataView
var length = arguments.length > 2 ? arguments[2] : undefined; */
function checkSumArrayBuffer(buffer, offset, length) {
if (offset === undefined) offset = 0;
length = length == null ? buffer.byteLength : length; length = length == null ? buffer.byteLength : length;
if (offset + length > buffer.byteLength) { if (offset + length > buffer.byteLength) {
throw new Error('check sum out of bound'); throw new Error('check sum out of bound');
} }
var nLongs = Math.floor(length / 4); var bytes = new Uint8Array(buffer, offset, length);
var view = new DataView(buffer, offset, length); var nLongs = length >> 2;
var sum = 0; var sum = 0;
var i = 0; var i = 0;
while (i < nLongs) { while (i < nLongs) {
sum += view.getUint32(4 * i++, false); var j = i << 2;
sum = (sum + (bytes[j] << 24 | bytes[j + 1] << 16 | bytes[j + 2] << 8 | bytes[j + 3])) | 0;
i++;
} }
var leftBytes = length - nLongs * 4; var leftBytes = length - nLongs * 4;
if (leftBytes) { if (leftBytes) {
offset = nLongs * 4; var off = nLongs << 2;
while (leftBytes > 0) { var shift = leftBytes * 8;
sum += view.getUint8(offset, false) << leftBytes * 8; var val = 0;
offset++; for (var k = 0; k < leftBytes; k++) {
leftBytes--; val = (val | bytes[off + k] << (leftBytes - 1 - k) * 8) >>> 0;
} }
sum = (sum + val) | 0;
} }
return sum % 0x100000000; return sum >>> 0;
} }
function checkSumArray(buffer) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; function checkSumArray(buffer, offset, length) {
var length = arguments.length > 2 ? arguments[2] : undefined; if (offset === undefined) offset = 0;
length = length || buffer.length; length = length || buffer.length;
if (offset + length > buffer.length) { if (offset + length > buffer.length) {
throw new Error('check sum out of bound'); throw new Error('check sum out of bound');
} }
var nLongs = Math.floor(length / 4); var nLongs = length >> 2;
var sum = 0; var sum = 0;
var i = 0; var i = 0;
while (i < nLongs) { while (i < nLongs) {
sum += (buffer[i++] << 24) + (buffer[i++] << 16) + (buffer[i++] << 8) + buffer[i++]; sum = (sum + ((buffer[i] << 24 | buffer[i + 1] << 16 | buffer[i + 2] << 8 | buffer[i + 3]) >>> 0)) | 0;
i += 4;
} }
var leftBytes = length - nLongs * 4; var leftBytes = length - nLongs * 4;
if (leftBytes) { if (leftBytes) {
offset = nLongs * 4; var off = nLongs << 2;
while (leftBytes > 0) { for (var k = 0; k < leftBytes; k++) {
sum += buffer[offset] << leftBytes * 8; sum = (sum + (buffer[off + k] << (leftBytes - 1 - k) * 8)) | 0;
offset++;
leftBytes--;
} }
} }
return sum % 0x100000000; return sum >>> 0;
} }
/** /**

View File

@ -181,16 +181,22 @@ var _default = exports.default = {
* @return {Array.<string>} 读取后的字符串数组 * @return {Array.<string>} 读取后的字符串数组
*/ */
getPascalString: function getPascalString(byteArray) { getPascalString: function getPascalString(byteArray) {
/* 优化63: Array.push + fromCharCode.apply 替代逐字拼接 */
var strArray = []; var strArray = [];
var i = 0; var i = 0;
var l = byteArray.length; var l = byteArray.length;
while (i < l) { while (i < l) {
var strLength = byteArray[i++]; var strLength = byteArray[i++];
var str = ''; if (strLength === 0) {
while (strLength-- > 0 && i < l) { strArray.push('');
str += String.fromCharCode(byteArray[i++]); continue;
} }
// 这里需要将unicode转换成js编码 var chars = new Array(strLength);
var end = Math.min(i + strLength, l);
for (var j = 0; i < end; j++, i++) {
chars[j] = byteArray[i];
}
var str = String.fromCharCode.apply(null, chars);
str = stringify(str); str = stringify(str);
strArray.push(str); strArray.push(str);
} }

View File

@ -65,23 +65,28 @@ var Writer = /*#__PURE__*/function () {
return _createClass(Writer, [{ return _createClass(Writer, [{
key: "write", key: "write",
value: function write(type, value, offset, littleEndian) { value: function write(type, value, offset, littleEndian) {
// 使用当前位移
if (undefined === offset) { if (undefined === offset) {
offset = this.offset; offset = this.offset;
} }
// 使用小尾
if (undefined === littleEndian) { if (undefined === littleEndian) {
littleEndian = this.littleEndian; littleEndian = this.littleEndian;
} }
// 扩展方法
if (undefined === dataType[type]) { if (undefined === dataType[type]) {
return this['write' + type](value, offset, littleEndian); return this['write' + type](value, offset, littleEndian);
} }
var size = dataType[type]; var size = dataType[type];
this.offset = offset + size; this.offset = offset + size;
this.view['set' + type](offset, value, littleEndian); /* 优化20: switch 直接分发,避免动态属性查找 */
switch (type) {
case 'Int8': this.view.setInt8(offset, value, littleEndian); break;
case 'Uint8': this.view.setUint8(offset, value, littleEndian); break;
case 'Int16': this.view.setInt16(offset, value, littleEndian); break;
case 'Uint16': this.view.setUint16(offset, value, littleEndian); break;
case 'Int32': this.view.setInt32(offset, value, littleEndian); break;
case 'Uint32': this.view.setUint32(offset, value, littleEndian); break;
case 'Float32': this.view.setFloat32(offset, value, littleEndian); break;
case 'Float64': this.view.setFloat64(offset, value, littleEndian); break;
}
return this; return this;
} }
@ -97,7 +102,6 @@ var Writer = /*#__PURE__*/function () {
key: "writeBytes", key: "writeBytes",
value: function writeBytes(value, length, offset) { value: function writeBytes(value, length, offset) {
length = length || value.byteLength || value.length; length = length || value.byteLength || value.length;
var i;
if (!length) { if (!length) {
return this; return this;
} }
@ -107,16 +111,11 @@ var Writer = /*#__PURE__*/function () {
if (length < 0 || offset + length > this.length) { if (length < 0 || offset + length > this.length) {
_error.default.raise(10002, this.length, offset + length); _error.default.raise(10002, this.length, offset + length);
} }
var littleEndian = this.littleEndian; /* 优化5: Uint8Array.set 批量写入,替代逐字节循环 */
if (value instanceof ArrayBuffer) { if (value instanceof ArrayBuffer) {
var view = new DataView(value, 0, length); new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length).set(new Uint8Array(value, 0, length));
for (i = 0; i < length; ++i) {
this.view.setUint8(offset + i, view.getUint8(i, littleEndian), littleEndian);
}
} else { } else {
for (i = 0; i < length; ++i) { new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length).set(value instanceof Uint8Array ? value : new Uint8Array(value), 0);
this.view.setUint8(offset + i, value[i], littleEndian);
}
} }
this.offset = offset + length; this.offset = offset + length;
return this; return this;
@ -138,10 +137,8 @@ var Writer = /*#__PURE__*/function () {
if (undefined === offset) { if (undefined === offset) {
offset = this.offset; offset = this.offset;
} }
var littleEndian = this.littleEndian; /* 优化5: fill(0) 批量填充,替代逐字节循环 */
for (var i = 0; i < length; ++i) { new Uint8Array(this.view.buffer, this.view.byteOffset + offset, length).fill(0);
this.view.setUint8(offset + i, 0, littleEndian);
}
this.offset = offset + length; this.offset = offset + length;
return this; return this;
} }
@ -164,21 +161,21 @@ var Writer = /*#__PURE__*/function () {
if (undefined === offset) { if (undefined === offset) {
offset = this.offset; offset = this.offset;
} }
// eslint-disable-next-line no-control-regex // eslint-disable-next-line no-control-regex
length = length || str.replace(/[^\x00-\xff]/g, '11').length; length = length || str.replace(/[^\x00-\xff]/g, '11').length;
if (length < 0 || offset + length > this.length) { if (length < 0 || offset + length > this.length) {
_error.default.raise(10002, this.length, offset + length); _error.default.raise(10002, this.length, offset + length);
} }
this.seek(offset); /* 优化28: 直接 view 写入,替代逐字节 writeUint8/writeUint16 */
var pos = offset;
for (var i = 0, l = str.length, charCode; i < l; ++i) { for (var i = 0, l = str.length, charCode; i < l; ++i) {
charCode = str.charCodeAt(i) || 0; charCode = str.charCodeAt(i) || 0;
if (charCode > 127) { if (charCode > 127) {
// unicode编码可能会超出2字节, this.view.setUint16(pos, charCode, this.littleEndian);
// 写入与编码有关系,此处不做处理 pos += 2;
this.writeUint16(charCode);
} else { } else {
this.writeUint8(charCode); this.view.setUint8(pos, charCode);
pos += 1;
} }
} }
this.offset = offset + length; this.offset = offset + length;
@ -299,8 +296,13 @@ var Writer = /*#__PURE__*/function () {
delete this.view; delete this.view;
} }
}]); }]);
}(); // 直接支持的数据类型 }(); // 优化19: 直接绑定方法,避免 curry 闭包开销
Object.keys(dataType).forEach(function (type) { Writer.prototype.writeInt8 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 1; this.view.setInt8(offset, value, this.littleEndian); return this; };
Writer.prototype['write' + type] = (0, _lang.curry)(Writer.prototype.write, type); Writer.prototype.writeUint8 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 1; this.view.setUint8(offset, value, this.littleEndian); return this; };
}); Writer.prototype.writeInt16 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 2; this.view.setInt16(offset, value, this.littleEndian); return this; };
Writer.prototype.writeUint16 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 2; this.view.setUint16(offset, value, this.littleEndian); return this; };
Writer.prototype.writeInt32 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 4; this.view.setInt32(offset, value, this.littleEndian); return this; };
Writer.prototype.writeUint32 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 4; this.view.setUint32(offset, value, this.littleEndian); return this; };
Writer.prototype.writeFloat32 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 4; this.view.setFloat32(offset, value, this.littleEndian); return this; };
Writer.prototype.writeFloat64 = function(value, offset) { if (offset === undefined) offset = this.offset; this.offset = offset + 8; this.view.setFloat64(offset, value, this.littleEndian); return this; };
var _default = exports.default = Writer; var _default = exports.default = Writer;