Merge pull request #21103 from Snuffleupagus/TrueTypeTableBuilder-2

Use TypedArrays in the `createCmapTable` function
This commit is contained in:
Jonas Jenwald 2026-04-16 11:44:14 +02:00 committed by GitHub
commit 445cb9abf9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 299 additions and 241 deletions

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";
@ -313,43 +312,110 @@ function string16(value) {
return String.fromCharCode((value >> 8) & 0xff, value & 0xff); return String.fromCharCode((value >> 8) & 0xff, value & 0xff);
} }
function setArray(data, pos, arr) { class TrueTypeTableBuilder {
data.set(arr, pos); #buf;
return pos + arr.length;
}
function setInt16(view, pos, val) { #bufLength = 1024;
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
typeof val === "number" && Math.abs(val) < 2 ** 16,
`setInt16: Unexpected input "${val}".`
);
}
view.setInt16(pos, val);
return pos + 2;
}
function setSafeInt16(view, pos, val) { #hasExactLength = false;
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
typeof val === "number" && !Number.isNaN(val),
`safeString16: Unexpected input "${val}".`
);
}
// clamp value to the 16-bit int range
view.setInt16(pos, MathClamp(val, -0x8000, 0x7fff));
return pos + 2;
}
function setInt32(view, pos, val) { #pos = 0;
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert( #view;
typeof val === "number" && Math.abs(val) < 2 ** 32,
`setInt32: Unexpected input "${val}".` constructor({ exactLength, minLength }) {
); this.#hasExactLength = !!exactLength;
this.#initBuf(exactLength || minLength);
}
#initBuf(minLength) {
if (this.#hasExactLength) {
this.#bufLength = minLength;
} else {
// Compute the first power of two that is as big as the `minLength`.
while (this.#bufLength < minLength) {
this.#bufLength *= 2;
}
}
const newBuf = new Uint8Array(this.#bufLength);
if (this.#buf) {
newBuf.set(this.#buf, 0);
}
this.#buf = newBuf;
this.#view = new DataView(newBuf.buffer);
}
get data() {
return this.#buf.subarray(0, this.#pos);
}
get length() {
return this.#pos;
}
skip(n) {
this.#pos += n;
}
setArray(arr) {
const newPos = this.#pos + arr.length;
if (!this.#hasExactLength && newPos > this.#bufLength) {
this.#initBuf(newPos);
}
this.#buf.set(arr, this.#pos);
this.#pos = newPos;
}
setInt16(val) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
typeof val === "number" && Math.abs(val) < 2 ** 16,
`setInt16: Unexpected input "${val}".`
);
}
const newPos = this.#pos + 2;
if (!this.#hasExactLength && newPos > this.#bufLength) {
this.#initBuf(newPos);
}
this.#view.setInt16(this.#pos, val);
this.#pos = newPos;
}
setSafeInt16(val) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
typeof val === "number" && !Number.isNaN(val),
`safeString16: Unexpected input "${val}".`
);
}
const newPos = this.#pos + 2;
if (!this.#hasExactLength && newPos > this.#bufLength) {
this.#initBuf(newPos);
}
// clamp value to the 16-bit int range
this.#view.setInt16(this.#pos, MathClamp(val, -0x8000, 0x7fff));
this.#pos = newPos;
}
setInt32(val) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
typeof val === "number" && Math.abs(val) < 2 ** 32,
`setInt32: Unexpected input "${val}".`
);
}
const newPos = this.#pos + 4;
if (!this.#hasExactLength && newPos > this.#bufLength) {
this.#initBuf(newPos);
}
this.#view.setInt32(this.#pos, val);
this.#pos = newPos;
} }
view.setInt32(pos, val);
return pos + 4;
} }
function isTrueTypeFile(file) { function isTrueTypeFile(file) {
@ -622,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) {
@ -645,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;
@ -657,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) {
@ -671,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];
@ -721,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 header31012 = new TrueTypeTableBuilder({ exactLength: 16 });
"\x00\x00" + // reserved header31012.setArray([0x00, 0x0c]); // format
string32(format31012.length + 16) + // length header31012.skip(2); // reserved, skip redundant "\x00\x00"
"\x00\x00\x00\x00" + // language header31012.setInt32(format31012.length + 16); // length
string32(format31012.length / 12); // nGroups header31012.skip(4); // language, skip redundant "\x00\x00\x00\x00"
header31012.setInt32(format31012.length / 12); // nGroups
} }
return stringToBytes( const table = new TrueTypeTableBuilder({
cmap + exactLength:
"\x00\x04" + // format 4 +
string16(format314.length + 4) + // length cmap.length +
format314 + (cmap31012?.length ?? 0) +
header31012 + format314.length +
format31012 (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) {
@ -856,27 +941,24 @@ function createOS2Table(properties, charstrings, override) {
const winAscent = override.yMax || typoAscent; const winAscent = override.yMax || typoAscent;
const winDescent = -override.yMin || -typoDescent; const winDescent = -override.yMin || -typoDescent;
const data = new Uint8Array(96), const os2 = new TrueTypeTableBuilder({ exactLength: 96 });
view = new DataView(data.buffer); os2.setArray([0x00, 0x03]); // version
let pos = 0; os2.setArray([0x02, 0x24]); // xAvgCharWidth
os2.setArray([0x01, 0xf4]); // usWeightClass
pos = setArray(data, pos, [0x00, 0x03]); // version os2.setArray([0x00, 0x05]); // usWidthClass
pos = setArray(data, pos, [0x02, 0x24]); // xAvgCharWidth os2.skip(2); // fstype (0 to improve browser compatibility), skip redundant "\x00\x00"
pos = setArray(data, pos, [0x01, 0xf4]); // usWeightClass os2.setArray([0x02, 0x8a]); // ySubscriptXSize
pos = setArray(data, pos, [0x00, 0x05]); // usWidthClass os2.setArray([0x02, 0xbb]); // ySubscriptYSize
pos += 2; // fstype (0 to improve browser compatibility), skip redundant "\x00\x00" os2.skip(2); // ySubscriptXOffset, skip redundant "\x00\x00"
pos = setArray(data, pos, [0x02, 0x8a]); // ySubscriptXSize os2.setArray([0x00, 0x8c]); // ySubscriptYOffset
pos = setArray(data, pos, [0x02, 0xbb]); // ySubscriptYSize os2.setArray([0x02, 0x8a]); // ySuperScriptXSize
pos += 2; // ySubscriptXOffset, skip redundant "\x00\x00" os2.setArray([0x02, 0xbb]); // ySuperScriptYSize
pos = setArray(data, pos, [0x00, 0x8c]); // ySubscriptYOffset os2.skip(2); // ySuperScriptXOffset, skip redundant "\x00\x00"
pos = setArray(data, pos, [0x02, 0x8a]); // ySuperScriptXSize os2.setArray([0x01, 0xdf]); // ySuperScriptYOffset
pos = setArray(data, pos, [0x02, 0xbb]); // ySuperScriptYSize os2.setArray([0x00, 0x31]); // yStrikeOutSize
pos += 2; // ySuperScriptXOffset, skip redundant "\x00\x00" os2.setArray([0x01, 0x02]); // yStrikeOutPosition
pos = setArray(data, pos, [0x01, 0xdf]); // ySuperScriptYOffset os2.skip(2); // sFamilyClass, skip redundant "\x00\x00"
pos = setArray(data, pos, [0x00, 0x31]); // yStrikeOutSize os2.setArray([
pos = setArray(data, pos, [0x01, 0x02]); // yStrikeOutPosition
pos += 2; // sFamilyClass, skip redundant "\x00\x00"
pos = setArray(data, pos, [
0x00, 0x00,
0x00, 0x00,
0x06, 0x06,
@ -888,46 +970,47 @@ function createOS2Table(properties, charstrings, override) {
0x00, 0x00,
0x00, 0x00,
]); // Panose ]); // Panose
pos = setInt32(view, pos, ulUnicodeRange1); // ulUnicodeRange1 (Bits 0-31) os2.setInt32(ulUnicodeRange1); // ulUnicodeRange1 (Bits 0-31)
pos = setInt32(view, pos, ulUnicodeRange2); // ulUnicodeRange2 (Bits 32-63) os2.setInt32(ulUnicodeRange2); // ulUnicodeRange2 (Bits 32-63)
pos = setInt32(view, pos, ulUnicodeRange3); // ulUnicodeRange3 (Bits 64-95) os2.setInt32(ulUnicodeRange3); // ulUnicodeRange3 (Bits 64-95)
pos = setInt32(view, pos, ulUnicodeRange4); // ulUnicodeRange4 (Bits 96-127) os2.setInt32(ulUnicodeRange4); // ulUnicodeRange4 (Bits 96-127)
pos = setArray(data, pos, [0x2a, 0x32, 0x31, 0x2a]); // achVendID os2.setArray([0x2a, 0x32, 0x31, 0x2a]); // achVendID
pos = setInt16(view, pos, properties.italicAngle ? 1 : 0); // fsSelection os2.setInt16(properties.italicAngle ? 1 : 0); // fsSelection
pos = setInt16(view, pos, firstCharIndex || properties.firstChar); // usFirstCharIndex os2.setInt16(firstCharIndex || properties.firstChar); // usFirstCharIndex
pos = setInt16(view, pos, lastCharIndex || properties.lastChar); // usLastCharIndex os2.setInt16(lastCharIndex || properties.lastChar); // usLastCharIndex
pos = setInt16(view, pos, typoAscent); // sTypoAscender os2.setInt16(typoAscent); // sTypoAscender
pos = setInt16(view, pos, typoDescent); // sTypoDescender os2.setInt16(typoDescent); // sTypoDescender
pos = setArray(data, pos, [0x00, 0x64]); // sTypoLineGap (7%-10% of the unitsPerEM value) os2.setArray([0x00, 0x64]); // sTypoLineGap (7%-10% of the unitsPerEM value)
pos = setInt16(view, pos, winAscent); // usWinAscent os2.setInt16(winAscent); // usWinAscent
pos = setInt16(view, pos, winDescent); // usWinDescent os2.setInt16(winDescent); // usWinDescent
pos += 4; // ulCodePageRange1 (Bits 0-31), skip redundant "\x00\x00\x00\x00" os2.skip(
pos += 4; // ulCodePageRange2 (Bits 32-63), skip redundant "\x00\x00\x00\x00" 4 + // ulCodePageRange1 (Bits 0-31), skip redundant "\x00\x00\x00\x00"
pos = setInt16(view, pos, properties.xHeight); // sxHeight 4 // ulCodePageRange2 (Bits 32-63), skip redundant "\x00\x00\x00\x00"
pos = setInt16(view, pos, properties.capHeight); // sCapHeight );
pos += 2; // usDefaultChar, skip redundant "\x00\x00" os2.setInt16(properties.xHeight); // sxHeight
pos = setInt16(view, pos, firstCharIndex || properties.firstChar); // usBreakChar os2.setInt16(properties.capHeight); // sCapHeight
setArray(data, pos, [0x00, 0x03]); // usMaxContext os2.skip(2); // usDefaultChar, skip redundant "\x00\x00"
os2.setInt16(firstCharIndex || properties.firstChar); // usBreakChar
return data; os2.setArray([0x00, 0x03]); // usMaxContext
return os2.data;
} }
function createPostTable(properties) { function createPostTable(properties) {
const data = new Uint8Array(32), const post = new TrueTypeTableBuilder({ exactLength: 32 });
view = new DataView(data.buffer); post.setArray([0x00, 0x03, 0x00, 0x00]); // Version number
let pos = 0; post.setInt32(Math.floor(properties.italicAngle * 2 ** 16)); // italicAngle
post.skip(
pos = setArray(data, pos, [0x00, 0x03, 0x00, 0x00]); // Version number 2 + // underlinePosition, skip redundant "\x00\x00"
pos = setInt32(view, pos, Math.floor(properties.italicAngle * 2 ** 16)); // italicAngle 2 // underlineThickness, skip redundant "\x00\x00"
pos += 2; // underlinePosition, skip redundant "\x00\x00" );
pos += 2; // underlineThickness, skip redundant "\x00\x00" post.setInt32(properties.fixedPitch ? 1 : 0); // isFixedPitch
setInt32(view, pos, properties.fixedPitch ? 1 : 0); // isFixedPitch post.skip(
// minMemType42, skip redundant "\x00\x00\x00\x00" 4 + // minMemType42, skip redundant "\x00\x00\x00\x00"
// maxMemType42, skip redundant "\x00\x00\x00\x00" 4 + // maxMemType42, skip redundant "\x00\x00\x00\x00"
// minMemType1, skip redundant "\x00\x00\x00\x00" 4 + // minMemType1, skip redundant "\x00\x00\x00\x00"
// maxMemType1, skip redundant "\x00\x00\x00\x00" 4 // maxMemType1, skip redundant "\x00\x00\x00\x00"
);
return data; return post.data;
} }
function createPostscriptName(name) { function createPostscriptName(name) {
@ -3299,29 +3382,28 @@ class Font {
"head", "head",
(function fontTableHead() { (function fontTableHead() {
const dateArr = [0x00, 0x00, 0x00, 0x00, 0x9e, 0x0b, 0x7e, 0x27]; const dateArr = [0x00, 0x00, 0x00, 0x00, 0x9e, 0x0b, 0x7e, 0x27];
const data = new Uint8Array(54),
view = new DataView(data.buffer);
let pos = 0;
pos = setArray(data, pos, [0x00, 0x01, 0x00, 0x00]); // Version number const head = new TrueTypeTableBuilder({ exactLength: 54 });
pos = setArray(data, pos, [0x00, 0x00, 0x10, 0x00]); // fontRevision head.setArray([0x00, 0x01, 0x00, 0x00]); // Version number
pos += 4; // checksumAdjustement, skip redundant "\x00\x00\x00\x00" head.setArray([0x00, 0x00, 0x10, 0x00]); // fontRevision
pos = setArray(data, pos, [0x5f, 0x0f, 0x3c, 0xf5]); // magicNumber head.skip(4); // checksumAdjustement, skip redundant "\x00\x00\x00\x00"
pos += 2; // Flags, skip redundant "\x00\x00" head.setArray([0x5f, 0x0f, 0x3c, 0xf5]); // magicNumber
pos = setSafeInt16(view, pos, unitsPerEm); // unitsPerEM head.skip(2); // Flags, skip redundant "\x00\x00"
pos = setArray(data, pos, dateArr); // creation date head.setSafeInt16(unitsPerEm); // unitsPerEM
pos = setArray(data, pos, dateArr); // modifification date head.setArray(dateArr); // creation date
pos += 2; // xMin, skip redundant "\x00\x00" head.setArray(dateArr); // modifification date
pos = setSafeInt16(view, pos, properties.descent); // yMin head.skip(2); // xMin, skip redundant "\x00\x00"
pos = setArray(data, pos, [0x0f, 0xff]); // xMax head.setSafeInt16(properties.descent); // yMin
pos = setSafeInt16(view, pos, properties.ascent); // yMax head.setArray([0x0f, 0xff]); // xMax
pos = setInt16(view, pos, properties.italicAngle ? 2 : 0); // macStyle head.setSafeInt16(properties.ascent); // yMax
setArray(data, pos, [0x00, 0x11]); // lowestRecPPEM head.setInt16(properties.italicAngle ? 2 : 0); // macStyle
// fontDirectionHint, skip redundant "\x00\x00" head.setArray([0x00, 0x11]); // lowestRecPPEM
// indexToLocFormat, skip redundant "\x00\x00" head.skip(
// glyphDataFormat, skip redundant "\x00\x00" 2 + // fontDirectionHint, skip redundant "\x00\x00"
2 + // indexToLocFormat, skip redundant "\x00\x00"
return data; 2 // glyphDataFormat, skip redundant "\x00\x00"
);
return head.data;
})() })()
); );
@ -3329,33 +3411,31 @@ class Font {
builder.addTable( builder.addTable(
"hhea", "hhea",
(function fontTableHhea() { (function fontTableHhea() {
const data = new Uint8Array(36), const hhea = new TrueTypeTableBuilder({ exactLength: 36 });
view = new DataView(data.buffer); hhea.setArray([0x00, 0x01, 0x00, 0x00]); // Version number
let pos = 0; hhea.setSafeInt16(properties.ascent); // Typographic Ascent
hhea.setSafeInt16(properties.descent); // Typographic Descent
pos = setArray(data, pos, [0x00, 0x01, 0x00, 0x00]); // Version number hhea.skip(2); // Line Gap, skip redundant "\x00\x00"
pos = setSafeInt16(view, pos, properties.ascent); // Typographic Ascent hhea.setArray([0xff, 0xff]); // advanceWidthMax
pos = setSafeInt16(view, pos, properties.descent); // Typographic Descent hhea.skip(
pos += 2; // Line Gap, skip redundant "\x00\x00" 2 + // minLeftSidebearing, skip redundant "\x00\x00"
pos = setArray(data, pos, [0xff, 0xff]); // advanceWidthMax 2 + // minRightSidebearing, skip redundant "\x00\x00"
pos += 2; // minLeftSidebearing, skip redundant "\x00\x00" 2 // xMaxExtent, skip redundant "\x00\x00"
pos += 2; // minRightSidebearing, skip redundant "\x00\x00" );
pos += 2; // xMaxExtent, skip redundant "\x00\x00" hhea.setSafeInt16(properties.capHeight); // caretSlopeRise
pos = setSafeInt16(view, pos, properties.capHeight); // caretSlopeRise hhea.setSafeInt16(
pos = setSafeInt16(
view,
pos,
Math.tan(properties.italicAngle) * properties.xHeight Math.tan(properties.italicAngle) * properties.xHeight
); // caretSlopeRun ); // caretSlopeRun
pos += 2; // caretOffset, skip redundant "\x00\x00" hhea.skip(
pos += 2; // -reserved-, skip redundant "\x00\x00" 2 + // caretOffset, skip redundant "\x00\x00"
pos += 2; // -reserved-, skip redundant "\x00\x00" 2 + // -reserved-, skip redundant "\x00\x00"
pos += 2; // -reserved-, skip redundant "\x00\x00" 2 + // -reserved-, skip redundant "\x00\x00"
pos += 2; // -reserved-, skip redundant "\x00\x00" 2 + // -reserved-, skip redundant "\x00\x00"
pos += 2; // metricDataFormat, skip redundant "\x00\x00" 2 + // -reserved-, skip redundant "\x00\x00"
setInt16(view, pos, numGlyphs); // Number of HMetrics 2 // metricDataFormat, skip redundant "\x00\x00"
);
return data; hhea.setInt16(numGlyphs); // Number of HMetrics
return hhea.data;
})() })()
); );
@ -3366,10 +3446,9 @@ class Font {
const charstrings = font.charstrings; const charstrings = font.charstrings;
const cffWidths = font.cff?.widths ?? null; const cffWidths = font.cff?.widths ?? null;
const data = new Uint8Array(numGlyphs * 4), const hmtx = new TrueTypeTableBuilder({ exactLength: numGlyphs * 4 });
view = new DataView(data.buffer);
// Fake .notdef (width=0 and lsb=0) first, skip redundant assignment. // Fake .notdef (width=0 and lsb=0) first, skip redundant assignment.
let pos = 4; hmtx.skip(4);
for (let i = 1, ii = numGlyphs; i < ii; i++) { for (let i = 1, ii = numGlyphs; i < ii; i++) {
let width = 0; let width = 0;
@ -3378,10 +3457,10 @@ class Font {
} else if (cffWidths) { } else if (cffWidths) {
width = Math.ceil(cffWidths[i] || 0); width = Math.ceil(cffWidths[i] || 0);
} }
pos = setInt16(view, pos, width); hmtx.setInt16(width);
pos += 2; // Use lsb=0, skip redundant assignment. hmtx.skip(2); // Use lsb=0, skip redundant assignment.
} }
return data; return hmtx.data;
})() })()
); );
@ -3389,13 +3468,10 @@ class Font {
builder.addTable( builder.addTable(
"maxp", "maxp",
(function fontTableMaxp() { (function fontTableMaxp() {
const data = new Uint8Array(6), const maxp = new TrueTypeTableBuilder({ exactLength: 6 });
view = new DataView(data.buffer); maxp.setArray([0x00, 0x00, 0x50, 0x00]); // Version number
maxp.setInt16(numGlyphs); // Num of glyphs
setArray(data, 0, [0x00, 0x00, 0x50, 0x00]); // Version number return maxp.data;
setInt16(view, 4, numGlyphs); // Num of glyphs
return data;
})() })()
); );

View File

@ -18,7 +18,6 @@ import {
FeatureTest, FeatureTest,
isNodeJS, isNodeJS,
shadow, shadow,
string32,
unreachable, unreachable,
warn, warn,
} from "../shared/util.js"; } from "../shared/util.js";
@ -270,6 +269,14 @@ class FontLoader {
(data.charCodeAt(offset + 3) & 0xff) (data.charCodeAt(offset + 3) & 0xff)
); );
} }
function string32(value) {
return String.fromCharCode(
(value >> 24) & 0xff,
(value >> 16) & 0xff,
(value >> 8) & 0xff,
value & 0xff
);
}
function spliceString(s, offset, remove, insert) { function spliceString(s, offset, remove, insert) {
const chunk1 = s.substring(0, offset); const chunk1 = s.substring(0, offset);
const chunk2 = s.substring(offset + remove); const chunk2 = s.substring(offset + remove);

View File

@ -596,21 +596,6 @@ function stringToBytes(str) {
return bytes; return bytes;
} }
function string32(value) {
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
typeof value === "number" && Math.abs(value) < 2 ** 32,
`string32: Unexpected input "${value}".`
);
}
return String.fromCharCode(
(value >> 24) & 0xff,
(value >> 16) & 0xff,
(value >> 8) & 0xff,
value & 0xff
);
}
function objectSize(obj) { function objectSize(obj) {
return Object.keys(obj).length; return Object.keys(obj).length;
} }
@ -1334,7 +1319,6 @@ export {
ResponseException, ResponseException,
setVerbosityLevel, setVerbosityLevel,
shadow, shadow,
string32,
stringToBytes, stringToBytes,
stringToPDFString, stringToPDFString,
stringToUTF8String, stringToUTF8String,

View File

@ -19,7 +19,6 @@ import {
createValidAbsoluteUrl, createValidAbsoluteUrl,
getModificationDate, getModificationDate,
getUuid, getUuid,
string32,
stringToBytes, stringToBytes,
stringToPDFString, stringToPDFString,
} from "../../src/shared/util.js"; } from "../../src/shared/util.js";
@ -72,14 +71,6 @@ describe("util", function () {
}); });
}); });
describe("string32", function () {
it("converts unsigned 32-bit integers to strings", function () {
expect(string32(0x74727565)).toEqual("true");
expect(string32(0x74797031)).toEqual("typ1");
expect(string32(0x4f54544f)).toEqual("OTTO");
});
});
describe("stringToBytes", function () { describe("stringToBytes", function () {
it("handles non-string arguments", function () { it("handles non-string arguments", function () {
expect(function () { expect(function () {