fonteditor-core
Version:
fonts (ttf, woff, woff2, eot, svg, otf) parse, write, transform, glyph adjust.
173 lines (139 loc) • 4.47 kB
JavaScript
/**
* @file 写cmap表
* @author mengke01(kekee000@gmail.com)
*/
/**
* 创建`子表0`
*
* @param {Writer} writer 写对象
* @param {Array} unicodes unicodes列表
* @return {Writer}
*/
function writeSubTable0(writer, unicodes) {
writer.writeUint16(0); // format
writer.writeUint16(262); // length
writer.writeUint16(0); // language
// Array of unicodes 0..255
let i = -1;
let unicode;
while ((unicode = unicodes.shift())) {
while (++i < unicode[0]) {
writer.writeUint8(0);
}
writer.writeUint8(unicode[1]);
i = unicode[0];
}
while (++i < 256) {
writer.writeUint8(0);
}
return writer;
}
/**
* 创建`子表4`
*
* @param {Writer} writer 写对象
* @param {Array} segments 分块编码列表
* @return {Writer}
*/
function writeSubTable4(writer, segments) {
writer.writeUint16(4); // format
writer.writeUint16(24 + segments.length * 8); // length
writer.writeUint16(0); // language
const segCount = segments.length + 1;
const maxExponent = Math.floor(Math.log(segCount) / Math.LN2);
const searchRange = 2 * Math.pow(2, maxExponent);
writer.writeUint16(segCount * 2); // segCountX2
writer.writeUint16(searchRange); // searchRange
writer.writeUint16(maxExponent); // entrySelector
writer.writeUint16(2 * segCount - searchRange); // rangeShift
// end list
segments.forEach((segment) => {
writer.writeUint16(segment.end);
});
writer.writeUint16(0xFFFF); // end code
writer.writeUint16(0); // reservedPad
// start list
segments.forEach((segment) => {
writer.writeUint16(segment.start);
});
writer.writeUint16(0xFFFF); // start code
// id delta
segments.forEach((segment) => {
writer.writeUint16(segment.delta);
});
writer.writeUint16(1);
// Array of range offsets, it doesn't matter when deltas present
for (let i = 0, l = segments.length; i < l; i++) {
writer.writeUint16(0);
}
writer.writeUint16(0); // rangeOffsetArray should be finished with 0
return writer;
}
/**
* 创建`子表12`
*
* @param {Writer} writer 写对象
* @param {Array} segments 分块编码列表
* @return {Writer}
*/
function writeSubTable12(writer, segments) {
writer.writeUint16(12); // format
writer.writeUint16(0); // reserved
writer.writeUint32(16 + segments.length * 12); // length
writer.writeUint32(0); // language
writer.writeUint32(segments.length); // nGroups
segments.forEach((segment) => {
writer.writeUint32(segment.start);
writer.writeUint32(segment.end);
writer.writeUint32(segment.startId);
});
return writer;
}
/**
* 写subtableheader
*
* @param {Writer} writer Writer对象
* @param {number} platform 平台
* @param {number} encoding 编码
* @param {number} offset 偏移
* @return {Writer}
*/
function writeSubTableHeader(writer, platform, encoding, offset) {
writer.writeUint16(platform); // platform
writer.writeUint16(encoding); // encoding
writer.writeUint32(offset); // offset
return writer;
}
/**
* 写cmap表数据
*
* @param {Object} writer 写入器
* @param {Object} ttf ttf对象
* @return {Object} 写入器
*/
export default function write(writer, ttf) {
const hasGLyphsOver2Bytes = ttf.support.cmap.hasGLyphsOver2Bytes;
// write table header.
writer.writeUint16(0); // version
writer.writeUint16(hasGLyphsOver2Bytes ? 4 : 3); // count
// header size
const subTableOffset = 4 + (hasGLyphsOver2Bytes ? 32 : 24);
const format4Size = ttf.support.cmap.format4Size;
const format0Size = ttf.support.cmap.format0Size;
// subtable 4, unicode
writeSubTableHeader(writer, 0, 3, subTableOffset);
// subtable 0, mac standard
writeSubTableHeader(writer, 1, 0, subTableOffset + format4Size);
// subtable 4, windows standard
writeSubTableHeader(writer, 3, 1, subTableOffset);
if (hasGLyphsOver2Bytes) {
writeSubTableHeader(writer, 3, 10, subTableOffset + format4Size + format0Size);
}
// write tables, order of table seem to be magic, it is taken from TTX tool
writeSubTable4(writer, ttf.support.cmap.format4Segments);
writeSubTable0(writer, ttf.support.cmap.format0Segments);
if (hasGLyphsOver2Bytes) {
writeSubTable12(writer, ttf.support.cmap.format12Segments);
}
return writer;
}