Use TypedArrays in the createCmapTable function

This commit is contained in:
Jonas Jenwald 2026-04-14 16:34:55 +02:00
parent f9ecebe63c
commit cb935c35d3

View File

@ -20,7 +20,6 @@ import {
FormatError, FormatError,
info, info,
shadow, shadow,
string32,
stringToBytes, stringToBytes,
warn, warn,
} from "../shared/util.js"; } from "../shared/util.js";
@ -689,12 +688,13 @@ function getRanges(glyphs, toUnicodeExtraMap, numGlyphs) {
function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) { function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) {
const ranges = getRanges(glyphs, toUnicodeExtraMap, numGlyphs); const ranges = getRanges(glyphs, toUnicodeExtraMap, numGlyphs);
const numTables = ranges.at(-1)[1] > 0xffff ? 2 : 1; const numTables = ranges.at(-1)[1] > 0xffff ? 2 : 1;
let cmap =
"\x00\x00" + // version const cmap = new TrueTypeTableBuilder({ exactLength: 12 });
string16(numTables) + // numTables cmap.skip(2); // version, skip redundant "\x00\x00"
"\x00\x03" + // platformID cmap.setInt16(numTables); // numTables
"\x00\x01" + // encodingID cmap.setArray([0x00, 0x03]); // platformID
string32(4 + numTables * 8); // start of the table record cmap.setArray([0x00, 0x01]); // encodingID
cmap.setInt32(4 + numTables * 8); // start of the table record
let i, ii, j, jj; let i, ii, j, jj;
for (i = ranges.length - 1; i >= 0; --i) { for (i = ranges.length - 1; i >= 0; --i) {
@ -712,11 +712,11 @@ function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) {
const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2); const searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2);
// Fill up the 4 parallel arrays describing the segments. // Fill up the 4 parallel arrays describing the segments.
let startCount = ""; const startCount = new TrueTypeTableBuilder({}),
let endCount = ""; endCount = new TrueTypeTableBuilder({}),
let idDeltas = ""; idDeltas = new TrueTypeTableBuilder({}),
let idRangeOffsets = ""; idRangeOffsets = new TrueTypeTableBuilder({}),
let glyphsIds = ""; glyphsIds = new TrueTypeTableBuilder({});
let bias = 0; let bias = 0;
let range, start, end, codes; let range, start, end, codes;
@ -724,8 +724,8 @@ function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) {
range = ranges[i]; range = ranges[i];
start = range[0]; start = range[0];
end = range[1]; end = range[1];
startCount += string16(start); startCount.setInt16(start);
endCount += string16(end); endCount.setInt16(end);
codes = range[2]; codes = range[2];
let contiguous = true; let contiguous = true;
for (j = 1, jj = codes.length; j < jj; ++j) { for (j = 1, jj = codes.length; j < jj; ++j) {
@ -738,48 +738,58 @@ function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) {
const offset = (segCount - i) * 2 + bias * 2; const offset = (segCount - i) * 2 + bias * 2;
bias += end - start + 1; bias += end - start + 1;
idDeltas += string16(0); idDeltas.skip(2); // Skip redundant "\x00\x00"
idRangeOffsets += string16(offset); idRangeOffsets.setInt16(offset);
for (j = 0, jj = codes.length; j < jj; ++j) { for (j = 0, jj = codes.length; j < jj; ++j) {
glyphsIds += string16(codes[j]); glyphsIds.setInt16(codes[j]);
} }
} else { } else {
const startCode = codes[0]; const startCode = codes[0];
idDeltas += string16((startCode - start) & 0xffff); idDeltas.setInt16((startCode - start) & 0xffff);
idRangeOffsets += string16(0); idRangeOffsets.skip(2); // Skip redundant "\x00\x00"
} }
} }
if (trailingRangesCount > 0) { if (trailingRangesCount > 0) {
endCount += "\xFF\xFF"; endCount.setArray([0xff, 0xff]);
startCount += "\xFF\xFF"; startCount.setArray([0xff, 0xff]);
idDeltas += "\x00\x01"; idDeltas.setArray([0x00, 0x01]);
idRangeOffsets += "\x00\x00"; idRangeOffsets.skip(2); // Skip redundant "\x00\x00"
} }
const format314 = const format314 = new TrueTypeTableBuilder({
"\x00\x00" + // language exactLength:
string16(2 * segCount) + 12 +
string16(searchParams.range) + startCount.length +
string16(searchParams.entry) + endCount.length +
string16(searchParams.rangeShift) + idDeltas.length +
endCount + idRangeOffsets.length +
"\x00\x00" + glyphsIds.length,
startCount + });
idDeltas + format314.skip(2); // language, skip redundant "\x00\x00"
idRangeOffsets + format314.setInt16(2 * segCount);
glyphsIds; format314.setInt16(searchParams.range);
format314.setInt16(searchParams.entry);
format314.setInt16(searchParams.rangeShift);
format314.setArray(endCount.data);
format314.skip(2); // Skip redundant "\x00\x00"
format314.setArray(startCount.data);
format314.setArray(idDeltas.data);
format314.setArray(idRangeOffsets.data);
format314.setArray(glyphsIds.data);
let format31012 = ""; let cmap31012 = null,
let header31012 = ""; format31012 = null,
header31012 = null;
if (numTables > 1) { if (numTables > 1) {
cmap += cmap31012 = new TrueTypeTableBuilder({ exactLength: 8 });
"\x00\x03" + // platformID cmap31012.setArray([0x00, 0x03]); // platformID
"\x00\x0A" + // encodingID cmap31012.setArray([0x00, 0x0a]); // encodingID
string32(4 + numTables * 8 + 4 + format314.length); // start of the table record cmap31012.setInt32(4 + numTables * 8 + 4 + format314.length); // start of the table record
format31012 = "";
format31012 = new TrueTypeTableBuilder({});
for (i = 0, ii = ranges.length; i < ii; i++) { for (i = 0, ii = ranges.length; i < ii; i++) {
range = ranges[i]; range = ranges[i];
start = range[0]; start = range[0];
@ -788,35 +798,43 @@ function createCmapTable(glyphs, toUnicodeExtraMap, numGlyphs) {
for (j = 1, jj = codes.length; j < jj; ++j) { for (j = 1, jj = codes.length; j < jj; ++j) {
if (codes[j] !== codes[j - 1] + 1) { if (codes[j] !== codes[j - 1] + 1) {
end = range[0] + j - 1; end = range[0] + j - 1;
format31012 += format31012.setInt32(start); // startCharCode
string32(start) + // startCharCode format31012.setInt32(end); // endCharCode
string32(end) + // endCharCode format31012.setInt32(code); // startGlyphID
string32(code); // startGlyphID
start = end + 1; start = end + 1;
code = codes[j]; code = codes[j];
} }
} }
format31012 += format31012.setInt32(start); // startCharCode
string32(start) + // startCharCode format31012.setInt32(range[1]); // endCharCode
string32(range[1]) + // endCharCode format31012.setInt32(code); // startGlyphID
string32(code); // startGlyphID
}
header31012 =
"\x00\x0C" + // format
"\x00\x00" + // reserved
string32(format31012.length + 16) + // length
"\x00\x00\x00\x00" + // language
string32(format31012.length / 12); // nGroups
} }
return stringToBytes( header31012 = new TrueTypeTableBuilder({ exactLength: 16 });
cmap + header31012.setArray([0x00, 0x0c]); // format
"\x00\x04" + // format header31012.skip(2); // reserved, skip redundant "\x00\x00"
string16(format314.length + 4) + // length header31012.setInt32(format31012.length + 16); // length
format314 + header31012.skip(4); // language, skip redundant "\x00\x00\x00\x00"
header31012 + header31012.setInt32(format31012.length / 12); // nGroups
format31012 }
);
const table = new TrueTypeTableBuilder({
exactLength:
4 +
cmap.length +
(cmap31012?.length ?? 0) +
format314.length +
(header31012?.length ?? 0) +
(format31012?.length ?? 0),
});
table.setArray(cmap.data);
table.setArray(cmap31012?.data ?? []);
table.setArray([0x00, 0x04]); // format
table.setInt16(format314.length + 4); // length
table.setArray(format314.data);
table.setArray(header31012?.data ?? []);
table.setArray(format31012?.data ?? []);
return table.data;
} }
function validateOS2Table(os2, file) { function validateOS2Table(os2, file) {