fonteditor-core
Version:
fonts (ttf, woff, woff2, eot, svg, otf) parse, write, transform, glyph adjust.
254 lines (214 loc) • 8.05 kB
JavaScript
/**
* @file 解析cmap表
* @author mengke01(kekee000@gmail.com)
*/
import readWindowsAllCodes from '../../util/readWindowsAllCodes';
/**
* 读取cmap子表
*
* @param {Reader} reader Reader对象
* @param {Object} ttf ttf对象
* @param {Object} subTable 子表对象
* @param {number} cmapOffset 子表的偏移
*/
function readSubTable(reader, ttf, subTable, cmapOffset) {
let i;
let l;
let glyphIdArray;
const startOffset = cmapOffset + subTable.offset;
let glyphCount;
subTable.format = reader.readUint16(startOffset);
// 0~256 紧凑排列
if (subTable.format === 0) {
const format0 = subTable;
// 跳过format字段
format0.length = reader.readUint16();
format0.language = reader.readUint16();
glyphIdArray = [];
for (i = 0, l = format0.length - 6; i < l; i++) {
glyphIdArray.push(reader.readUint8());
}
format0.glyphIdArray = glyphIdArray;
}
else if (subTable.format === 2) {
const format2 = subTable;
// 跳过format字段
format2.length = reader.readUint16();
format2.language = reader.readUint16();
const subHeadKeys = [];
let maxSubHeadKey = 0;// 最大索引
let maxPos = -1; // 最大位置
for (let i = 0, l = 256; i < l; i++) {
subHeadKeys[i] = reader.readUint16() / 8;
if (subHeadKeys[i] > maxSubHeadKey) {
maxSubHeadKey = subHeadKeys[i];
maxPos = i;
}
}
const subHeads = [];
for (i = 0; i <= maxSubHeadKey; i++) {
subHeads[i] = {
firstCode: reader.readUint16(),
entryCount: reader.readUint16(),
idDelta: reader.readUint16(),
idRangeOffset: (reader.readUint16() - (maxSubHeadKey - i) * 8 - 2) / 2
};
}
glyphCount = (startOffset + format2.length - reader.offset) / 2;
const glyphs = [];
for (i = 0; i < glyphCount; i++) {
glyphs[i] = reader.readUint16();
}
format2.subHeadKeys = subHeadKeys;
format2.maxPos = maxPos;
format2.subHeads = subHeads;
format2.glyphs = glyphs;
}
// 双字节编码,非紧凑排列
else if (subTable.format === 4) {
const format4 = subTable;
// 跳过format字段
format4.length = reader.readUint16();
format4.language = reader.readUint16();
format4.segCountX2 = reader.readUint16();
format4.searchRange = reader.readUint16();
format4.entrySelector = reader.readUint16();
format4.rangeShift = reader.readUint16();
const segCount = format4.segCountX2 / 2;
// end code
const endCode = [];
for (i = 0; i < segCount; ++i) {
endCode.push(reader.readUint16());
}
format4.endCode = endCode;
format4.reservedPad = reader.readUint16();
// start code
const startCode = [];
for (i = 0; i < segCount; ++i) {
startCode.push(reader.readUint16());
}
format4.startCode = startCode;
// idDelta
const idDelta = [];
for (i = 0; i < segCount; ++i) {
idDelta.push(reader.readUint16());
}
format4.idDelta = idDelta;
format4.idRangeOffsetOffset = reader.offset;
// idRangeOffset
const idRangeOffset = [];
for (i = 0; i < segCount; ++i) {
idRangeOffset.push(reader.readUint16());
}
format4.idRangeOffset = idRangeOffset;
// 总长度 - glyphIdArray起始偏移/2
glyphCount = (format4.length - (reader.offset - startOffset)) / 2;
// 记录array offset
format4.glyphIdArrayOffset = reader.offset;
// glyphIdArray
glyphIdArray = [];
for (i = 0; i < glyphCount; ++i) {
glyphIdArray.push(reader.readUint16());
}
format4.glyphIdArray = glyphIdArray;
}
else if (subTable.format === 6) {
const format6 = subTable;
format6.length = reader.readUint16();
format6.language = reader.readUint16();
format6.firstCode = reader.readUint16();
format6.entryCount = reader.readUint16();
// 记录array offset
format6.glyphIdArrayOffset = reader.offset;
const glyphIndexArray = [];
const entryCount = format6.entryCount;
// 读取字符分组
for (i = 0; i < entryCount; ++i) {
glyphIndexArray.push(reader.readUint16());
}
format6.glyphIdArray = glyphIndexArray;
}
// defines segments for sparse representation in 4-byte character space
else if (subTable.format === 12) {
const format12 = subTable;
format12.reserved = reader.readUint16();
format12.length = reader.readUint32();
format12.language = reader.readUint32();
format12.nGroups = reader.readUint32();
const groups = [];
const nGroups = format12.nGroups;
// 读取字符分组
for (i = 0; i < nGroups; ++i) {
const group = {};
group.start = reader.readUint32();
group.end = reader.readUint32();
group.startId = reader.readUint32();
groups.push(group);
}
format12.groups = groups;
}
// format 14
else if (subTable.format === 14) {
const format14 = subTable;
format14.length = reader.readUint32();
const numVarSelectorRecords = reader.readUint32();
const groups = [];
let offset = reader.offset;
for (let i = 0; i < numVarSelectorRecords; i++) {
const varSelector = reader.readUint24(offset);
const defaultUVSOffset = reader.readUint32(offset + 3);
const nonDefaultUVSOffset = reader.readUint32(offset + 7);
offset += 11;
if (defaultUVSOffset) {
const numUnicodeValueRanges = reader.readUint32(startOffset + defaultUVSOffset);
for (let j = 0; j < numUnicodeValueRanges; j++) {
const startUnicode = reader.readUint24();
const additionalCount = reader.readUint8();
groups.push({
start: startUnicode,
end: startUnicode + additionalCount,
varSelector
});
}
}
if (nonDefaultUVSOffset) {
const numUVSMappings = reader.readUint32(startOffset + nonDefaultUVSOffset);
for (let j = 0; j < numUVSMappings; j++) {
const unicode = reader.readUint24();
const glyphId = reader.readUint16();
groups.push({
unicode,
glyphId,
varSelector
});
}
}
}
format14.groups = groups;
}
else {
console.warn('not support cmap format:' + subTable.format);
}
}
export default function parse(reader, ttf) {
const tcmap = {};
// eslint-disable-next-line no-invalid-this
const cmapOffset = this.offset;
reader.seek(cmapOffset);
tcmap.version = reader.readUint16(); // 编码方式
const numberSubtables = tcmap.numberSubtables = reader.readUint16(); // 表个数
const subTables = tcmap.tables = []; // 名字表
let offset = reader.offset;
// 使用offset读取,以便于查找
for (let i = 0, l = numberSubtables; i < l; i++) {
const subTable = {};
subTable.platformID = reader.readUint16(offset);
subTable.encodingID = reader.readUint16(offset + 2);
subTable.offset = reader.readUint32(offset + 4);
readSubTable(reader, ttf, subTable, cmapOffset);
subTables.push(subTable);
offset += 8;
}
const cmap = readWindowsAllCodes(subTables, ttf);
return cmap;
}