astro-ghostcms/.pnpm-store/v3/files/84/caf0c7c857196c05756cf5bcd77...

146 lines
5.1 KiB
Plaintext

// The `cmap` table stores the mappings from characters to glyphs.
// https://www.microsoft.com/typography/OTSPEC/cmap.htm
import check from '../check';
import parse from '../parse';
function parseCmapTableFormat12(cmap, p) {
//Skip reserved.
p.parseUShort();
// Length in bytes of the sub-tables.
cmap.length = p.parseULong();
cmap.language = p.parseULong();
let groupCount;
cmap.groupCount = groupCount = p.parseULong();
cmap.glyphIndexMap = {};
for (let i = 0; i < groupCount; i += 1) {
const startCharCode = p.parseULong();
const endCharCode = p.parseULong();
let startGlyphId = p.parseULong();
for (let c = startCharCode; c <= endCharCode; c += 1) {
cmap.glyphIndexMap[c] = startGlyphId;
startGlyphId++;
}
}
}
function parseCmapTableFormat4(cmap, p, data, start, offset) {
// Length in bytes of the sub-tables.
cmap.length = p.parseUShort();
cmap.language = p.parseUShort();
// segCount is stored x 2.
let segCount;
cmap.segCount = segCount = p.parseUShort() >> 1;
// Skip searchRange, entrySelector, rangeShift.
p.skip('uShort', 3);
// The "unrolled" mapping from character codes to glyph indices.
cmap.glyphIndexMap = {};
const endCountParser = new parse.Parser(data, start + offset + 14);
const startCountParser = new parse.Parser(
data,
start + offset + 16 + segCount * 2
);
const idDeltaParser = new parse.Parser(
data,
start + offset + 16 + segCount * 4
);
const idRangeOffsetParser = new parse.Parser(
data,
start + offset + 16 + segCount * 6
);
let glyphIndexOffset = start + offset + 16 + segCount * 8;
for (let i = 0; i < segCount - 1; i += 1) {
let glyphIndex;
const endCount = endCountParser.parseUShort();
const startCount = startCountParser.parseUShort();
const idDelta = idDeltaParser.parseShort();
const idRangeOffset = idRangeOffsetParser.parseUShort();
for (let c = startCount; c <= endCount; c += 1) {
if (idRangeOffset !== 0) {
// The idRangeOffset is relative to the current position in the idRangeOffset array.
// Take the current offset in the idRangeOffset array.
glyphIndexOffset =
idRangeOffsetParser.offset +
idRangeOffsetParser.relativeOffset -
2;
// Add the value of the idRangeOffset, which will move us into the glyphIndex array.
glyphIndexOffset += idRangeOffset;
// Then add the character index of the current segment, multiplied by 2 for USHORTs.
glyphIndexOffset += (c - startCount) * 2;
glyphIndex = parse.getUShort(data, glyphIndexOffset);
if (glyphIndex !== 0) {
glyphIndex = (glyphIndex + idDelta) & 0xffff;
}
} else {
glyphIndex = (c + idDelta) & 0xffff;
}
cmap.glyphIndexMap[c] = glyphIndex;
}
}
}
// Parse the `cmap` table. This table stores the mappings from characters to glyphs.
// There are many available formats, but we only support the Windows format 4 and 12.
// This function returns a `CmapEncoding` object or null if no supported format could be found.
function parseCmapTable(data, start) {
const cmap = {};
cmap.version = parse.getUShort(data, start);
check.argument(cmap.version === 0, 'cmap table version should be 0.');
// The cmap table can contain many sub-tables, each with their own format.
// We're only interested in a "platform 0" (Unicode format) and "platform 3" (Windows format) table.
cmap.numTables = parse.getUShort(data, start + 2);
let offset = -1;
for (let i = cmap.numTables - 1; i >= 0; i -= 1) {
const platformId = parse.getUShort(data, start + 4 + i * 8);
const encodingId = parse.getUShort(data, start + 4 + i * 8 + 2);
if (
(platformId === 3 &&
(encodingId === 0 || encodingId === 1 || encodingId === 10)) ||
(platformId === 0 &&
(encodingId === 0 ||
encodingId === 1 ||
encodingId === 2 ||
encodingId === 3 ||
encodingId === 4))
) {
offset = parse.getULong(data, start + 4 + i * 8 + 4);
break;
}
}
if (offset === -1) {
// There is no cmap table in the font that we support.
throw new Error('No valid cmap sub-tables found.');
}
const p = new parse.Parser(data, start + offset);
cmap.format = p.parseUShort();
if (cmap.format === 12) {
parseCmapTableFormat12(cmap, p);
} else if (cmap.format === 4) {
parseCmapTableFormat4(cmap, p, data, start, offset);
} else {
throw new Error(
'Only format 4 and 12 cmap tables are supported (found format ' +
cmap.format +
').'
);
}
return cmap;
}
export default { parse: parseCmapTable };