Re-factor the FontRendererFactory class, and related code, to use DataViews when reading data

This commit is contained in:
Jonas Jenwald 2026-04-10 15:50:32 +02:00
parent 88abcefea0
commit a24d86e1cf

View File

@ -25,22 +25,14 @@ import {
Util, Util,
warn, warn,
} from "../shared/util.js"; } from "../shared/util.js";
import {
isNumberArray,
readInt16,
readInt8,
readUint16,
readUint32,
} from "./core_utils.js";
import { CFFParser } from "./cff_parser.js"; import { CFFParser } from "./cff_parser.js";
import { getGlyphsUnicode } from "./glyphlist.js"; import { getGlyphsUnicode } from "./glyphlist.js";
import { isNumberArray } from "./core_utils.js";
import { StandardEncoding } from "./encodings.js"; import { StandardEncoding } from "./encodings.js";
import { Stream } from "./stream.js"; import { Stream } from "./stream.js";
// TODO: use DataView and its methods. function getFloat214(view, offset) {
return view.getInt16(offset) / 16384;
function getFloat214(data, offset) {
return readInt16(data, offset) / 16384;
} }
function getSubroutineBias(subrs) { function getSubroutineBias(subrs) {
@ -55,49 +47,50 @@ function getSubroutineBias(subrs) {
} }
function parseCmap(data, start, end) { function parseCmap(data, start, end) {
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
const offset = const offset =
readUint16(data, start + 2) === 1 view.getUint16(start + 2) === 1
? readUint32(data, start + 8) ? view.getUint32(start + 8)
: readUint32(data, start + 16); : view.getUint32(start + 16);
const format = readUint16(data, start + offset); const format = view.getUint16(start + offset);
let ranges, p, i; let ranges, p, i;
if (format === 4) { if (format === 4) {
readUint16(data, start + offset + 2); // length // Skip length, at `start + offset + 2`.
const segCount = readUint16(data, start + offset + 6) >> 1; const segCount = view.getUint16(start + offset + 6) >> 1;
p = start + offset + 14; p = start + offset + 14;
ranges = []; ranges = [];
for (i = 0; i < segCount; i++, p += 2) { for (i = 0; i < segCount; i++, p += 2) {
ranges[i] = { end: readUint16(data, p) }; ranges[i] = { end: view.getUint16(p) };
} }
p += 2; p += 2;
for (i = 0; i < segCount; i++, p += 2) { for (i = 0; i < segCount; i++, p += 2) {
ranges[i].start = readUint16(data, p); ranges[i].start = view.getUint16(p);
} }
for (i = 0; i < segCount; i++, p += 2) { for (i = 0; i < segCount; i++, p += 2) {
ranges[i].idDelta = readUint16(data, p); ranges[i].idDelta = view.getUint16(p);
} }
for (i = 0; i < segCount; i++, p += 2) { for (i = 0; i < segCount; i++, p += 2) {
let idOffset = readUint16(data, p); let idOffset = view.getUint16(p);
if (idOffset === 0) { if (idOffset === 0) {
continue; continue;
} }
ranges[i].ids = []; ranges[i].ids = [];
for (let j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) { for (let j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
ranges[i].ids[j] = readUint16(data, p + idOffset); ranges[i].ids[j] = view.getUint16(p + idOffset);
idOffset += 2; idOffset += 2;
} }
} }
return ranges; return ranges;
} else if (format === 12) { } else if (format === 12) {
const groups = readUint32(data, start + offset + 12); const groups = view.getUint32(start + offset + 12);
p = start + offset + 16; p = start + offset + 16;
ranges = []; ranges = [];
for (i = 0; i < groups; i++) { for (i = 0; i < groups; i++) {
start = readUint32(data, p); start = view.getUint32(p);
ranges.push({ ranges.push({
start, start,
end: readUint32(data, p + 4), end: view.getUint32(p + 4),
idDelta: readUint32(data, p + 8) - start, idDelta: view.getUint32(p + 8) - start,
}); });
p += 12; p += 12;
} }
@ -125,18 +118,19 @@ function parseCff(data, start, end, seacAnalysisEnabled) {
} }
function parseGlyfTable(glyf, loca, isGlyphLocationsLong) { function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
const view = new DataView(loca.buffer, loca.byteOffset, loca.byteLength);
let itemSize, itemDecode; let itemSize, itemDecode;
if (isGlyphLocationsLong) { if (isGlyphLocationsLong) {
itemSize = 4; itemSize = 4;
itemDecode = readUint32; itemDecode = (dv, offset) => dv.getUint32(offset);
} else { } else {
itemSize = 2; itemSize = 2;
itemDecode = (data, offset) => 2 * readUint16(data, offset); itemDecode = (dv, offset) => 2 * dv.getUint16(offset);
} }
const glyphs = []; const glyphs = [];
let startOffset = itemDecode(loca, 0); let startOffset = itemDecode(view, 0);
for (let j = itemSize; j < loca.length; j += itemSize) { for (let j = itemSize; j < loca.length; j += itemSize) {
const endOffset = itemDecode(loca, j); const endOffset = itemDecode(view, j);
glyphs.push(glyf.subarray(startOffset, endOffset)); glyphs.push(glyf.subarray(startOffset, endOffset));
startOffset = endOffset; startOffset = endOffset;
} }
@ -173,8 +167,8 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
warn("compileGlyf: skipping recursive composite glyph reference."); warn("compileGlyf: skipping recursive composite glyph reference.");
return; return;
} }
visitedGlyphs.add(code); visitedGlyphs.add(code);
function moveTo(x, y) { function moveTo(x, y) {
if (firstPoint) { if (firstPoint) {
// Close the current subpath in adding a straight line to the first point. // Close the current subpath in adding a straight line to the first point.
@ -190,8 +184,9 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
cmds.add(DrawOPS.quadraticCurveTo, [xa, ya, x, y]); cmds.add(DrawOPS.quadraticCurveTo, [xa, ya, x, y]);
} }
const view = new DataView(code.buffer, code.byteOffset, code.byteLength);
let i = 0; let i = 0;
const numberOfContours = readInt16(code, i); const numberOfContours = view.getInt16(i);
let flags; let flags;
let firstPoint = null; let firstPoint = null;
let x = 0, let x = 0,
@ -200,22 +195,22 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
if (numberOfContours < 0) { if (numberOfContours < 0) {
// composite glyph // composite glyph
do { do {
flags = readUint16(code, i); flags = view.getUint16(i);
const glyphIndex = readUint16(code, i + 2); const glyphIndex = view.getUint16(i + 2);
i += 4; i += 4;
let arg1, arg2; let arg1, arg2;
if (flags & 0x01) { if (flags & 0x01) {
if (flags & 0x02) { if (flags & 0x02) {
arg1 = readInt16(code, i); arg1 = view.getInt16(i);
arg2 = readInt16(code, i + 2); arg2 = view.getInt16(i + 2);
} else { } else {
arg1 = readUint16(code, i); arg1 = view.getUint16(i);
arg2 = readUint16(code, i + 2); arg2 = view.getUint16(i + 2);
} }
i += 4; i += 4;
} else if (flags & 0x02) { } else if (flags & 0x02) {
arg1 = readInt8(code, i++); arg1 = view.getInt8(i++);
arg2 = readInt8(code, i++); arg2 = view.getInt8(i++);
} else { } else {
arg1 = code[i++]; arg1 = code[i++];
arg2 = code[i++]; arg2 = code[i++];
@ -232,17 +227,17 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
scale01 = 0, scale01 = 0,
scale10 = 0; scale10 = 0;
if (flags & 0x08) { if (flags & 0x08) {
scaleX = scaleY = getFloat214(code, i); scaleX = scaleY = getFloat214(view, i);
i += 2; i += 2;
} else if (flags & 0x40) { } else if (flags & 0x40) {
scaleX = getFloat214(code, i); scaleX = getFloat214(view, i);
scaleY = getFloat214(code, i + 2); scaleY = getFloat214(view, i + 2);
i += 4; i += 4;
} else if (flags & 0x80) { } else if (flags & 0x80) {
scaleX = getFloat214(code, i); scaleX = getFloat214(view, i);
scale01 = getFloat214(code, i + 2); scale01 = getFloat214(view, i + 2);
scale10 = getFloat214(code, i + 4); scale10 = getFloat214(view, i + 4);
scaleY = getFloat214(code, i + 6); scaleY = getFloat214(view, i + 6);
i += 8; i += 8;
} }
const subglyph = font.glyphs[glyphIndex]; const subglyph = font.glyphs[glyphIndex];
@ -265,10 +260,10 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
const endPtsOfContours = []; const endPtsOfContours = [];
let j, jj; let j, jj;
for (j = 0; j < numberOfContours; j++) { for (j = 0; j < numberOfContours; j++) {
endPtsOfContours.push(readUint16(code, i)); endPtsOfContours.push(view.getUint16(i));
i += 2; i += 2;
} }
const instructionLength = readUint16(code, i); const instructionLength = view.getUint16(i);
i += 2 + instructionLength; // skipping the instructions i += 2 + instructionLength; // skipping the instructions
const numberOfPoints = endPtsOfContours.at(-1) + 1; const numberOfPoints = endPtsOfContours.at(-1) + 1;
const points = []; const points = [];
@ -285,7 +280,7 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
for (j = 0; j < numberOfPoints; j++) { for (j = 0; j < numberOfPoints; j++) {
switch (points[j].flags & 0x12) { switch (points[j].flags & 0x12) {
case 0x00: case 0x00:
x += readInt16(code, i); x += view.getInt16(i);
i += 2; i += 2;
break; break;
case 0x02: case 0x02:
@ -300,7 +295,7 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) {
for (j = 0; j < numberOfPoints; j++) { for (j = 0; j < numberOfPoints; j++) {
switch (points[j].flags & 0x24) { switch (points[j].flags & 0x24) {
case 0x00: case 0x00:
y += readInt16(code, i); y += view.getInt16(i);
i += 2; i += 2;
break; break;
case 0x04: case 0x04:
@ -384,6 +379,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
let firstPoint = null; let firstPoint = null;
function parse(code) { function parse(code) {
const view = new DataView(code.buffer, code.byteOffset, code.byteLength);
let i = 0; let i = 0;
while (i < code.length) { while (i < code.length) {
let stackClean = false; let stackClean = false;
@ -660,7 +656,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
} }
break; break;
case 28: case 28:
stack.push(readInt16(code, i)); stack.push(view.getInt16(i));
i += 2; i += 2;
break; break;
case 29: // callgsubr case 29: // callgsubr
@ -725,13 +721,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) {
} else if (v < 255) { } else if (v < 255) {
stack.push(-(v - 251) * 256 - code[i++] - 108); stack.push(-(v - 251) * 256 - code[i++] - 108);
} else { } else {
stack.push( stack.push(view.getInt32(i) / 65536);
((code[i] << 24) |
(code[i + 1] << 16) |
(code[i + 2] << 8) |
code[i + 3]) /
65536
);
i += 4; i += 4;
} }
break; break;
@ -915,13 +905,14 @@ class Type2Compiled extends CompiledFont {
class FontRendererFactory { class FontRendererFactory {
static create(font, seacAnalysisEnabled) { static create(font, seacAnalysisEnabled) {
const data = new Uint8Array(font.data); const data = new Uint8Array(font.data),
view = new DataView(data.buffer);
let cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm; let cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
const numTables = readUint16(data, 4); const numTables = view.getUint16(4);
for (let i = 0, p = 12; i < numTables; i++, p += 16) { for (let i = 0, p = 12; i < numTables; i++, p += 16) {
const tag = bytesToString(data.subarray(p, p + 4)); const tag = bytesToString(data.subarray(p, p + 4));
const offset = readUint32(data, p + 8); const offset = view.getUint32(p + 8);
const length = readUint32(data, p + 12); const length = view.getUint32(p + 12);
switch (tag) { switch (tag) {
case "cmap": case "cmap":
cmap = parseCmap(data, offset, offset + length); cmap = parseCmap(data, offset, offset + length);
@ -933,8 +924,8 @@ class FontRendererFactory {
loca = data.subarray(offset, offset + length); loca = data.subarray(offset, offset + length);
break; break;
case "head": case "head":
unitsPerEm = readUint16(data, offset + 18); unitsPerEm = view.getUint16(offset + 18);
indexToLocFormat = readUint16(data, offset + 50); indexToLocFormat = view.getUint16(offset + 50);
break; break;
case "CFF ": case "CFF ":
cff = parseCff(data, offset, offset + length, seacAnalysisEnabled); cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);