From a24d86e1cf812b9cef276b1863388fdb86d3d506 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 10 Apr 2026 15:50:32 +0200 Subject: [PATCH] Re-factor the `FontRendererFactory` class, and related code, to use `DataView`s when reading data --- src/core/font_renderer.js | 121 ++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 65 deletions(-) diff --git a/src/core/font_renderer.js b/src/core/font_renderer.js index 29d0f0818..e2bd0f911 100644 --- a/src/core/font_renderer.js +++ b/src/core/font_renderer.js @@ -25,22 +25,14 @@ import { Util, warn, } from "../shared/util.js"; -import { - isNumberArray, - readInt16, - readInt8, - readUint16, - readUint32, -} from "./core_utils.js"; import { CFFParser } from "./cff_parser.js"; import { getGlyphsUnicode } from "./glyphlist.js"; +import { isNumberArray } from "./core_utils.js"; import { StandardEncoding } from "./encodings.js"; import { Stream } from "./stream.js"; -// TODO: use DataView and its methods. - -function getFloat214(data, offset) { - return readInt16(data, offset) / 16384; +function getFloat214(view, offset) { + return view.getInt16(offset) / 16384; } function getSubroutineBias(subrs) { @@ -55,49 +47,50 @@ function getSubroutineBias(subrs) { } function parseCmap(data, start, end) { + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); const offset = - readUint16(data, start + 2) === 1 - ? readUint32(data, start + 8) - : readUint32(data, start + 16); - const format = readUint16(data, start + offset); + view.getUint16(start + 2) === 1 + ? view.getUint32(start + 8) + : view.getUint32(start + 16); + const format = view.getUint16(start + offset); let ranges, p, i; if (format === 4) { - readUint16(data, start + offset + 2); // length - const segCount = readUint16(data, start + offset + 6) >> 1; + // Skip length, at `start + offset + 2`. + const segCount = view.getUint16(start + offset + 6) >> 1; p = start + offset + 14; ranges = []; for (i = 0; i < segCount; i++, p += 2) { - ranges[i] = { end: readUint16(data, p) }; + ranges[i] = { end: view.getUint16(p) }; } 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) { - ranges[i].idDelta = readUint16(data, p); + ranges[i].idDelta = view.getUint16(p); } for (i = 0; i < segCount; i++, p += 2) { - let idOffset = readUint16(data, p); + let idOffset = view.getUint16(p); if (idOffset === 0) { continue; } ranges[i].ids = []; 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; } } return ranges; } else if (format === 12) { - const groups = readUint32(data, start + offset + 12); + const groups = view.getUint32(start + offset + 12); p = start + offset + 16; ranges = []; for (i = 0; i < groups; i++) { - start = readUint32(data, p); + start = view.getUint32(p); ranges.push({ start, - end: readUint32(data, p + 4), - idDelta: readUint32(data, p + 8) - start, + end: view.getUint32(p + 4), + idDelta: view.getUint32(p + 8) - start, }); p += 12; } @@ -125,18 +118,19 @@ function parseCff(data, start, end, seacAnalysisEnabled) { } function parseGlyfTable(glyf, loca, isGlyphLocationsLong) { + const view = new DataView(loca.buffer, loca.byteOffset, loca.byteLength); let itemSize, itemDecode; if (isGlyphLocationsLong) { itemSize = 4; - itemDecode = readUint32; + itemDecode = (dv, offset) => dv.getUint32(offset); } else { itemSize = 2; - itemDecode = (data, offset) => 2 * readUint16(data, offset); + itemDecode = (dv, offset) => 2 * dv.getUint16(offset); } const glyphs = []; - let startOffset = itemDecode(loca, 0); + let startOffset = itemDecode(view, 0); 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)); startOffset = endOffset; } @@ -173,8 +167,8 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) { warn("compileGlyf: skipping recursive composite glyph reference."); return; } - visitedGlyphs.add(code); + function moveTo(x, y) { if (firstPoint) { // 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]); } + const view = new DataView(code.buffer, code.byteOffset, code.byteLength); let i = 0; - const numberOfContours = readInt16(code, i); + const numberOfContours = view.getInt16(i); let flags; let firstPoint = null; let x = 0, @@ -200,22 +195,22 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) { if (numberOfContours < 0) { // composite glyph do { - flags = readUint16(code, i); - const glyphIndex = readUint16(code, i + 2); + flags = view.getUint16(i); + const glyphIndex = view.getUint16(i + 2); i += 4; let arg1, arg2; if (flags & 0x01) { if (flags & 0x02) { - arg1 = readInt16(code, i); - arg2 = readInt16(code, i + 2); + arg1 = view.getInt16(i); + arg2 = view.getInt16(i + 2); } else { - arg1 = readUint16(code, i); - arg2 = readUint16(code, i + 2); + arg1 = view.getUint16(i); + arg2 = view.getUint16(i + 2); } i += 4; } else if (flags & 0x02) { - arg1 = readInt8(code, i++); - arg2 = readInt8(code, i++); + arg1 = view.getInt8(i++); + arg2 = view.getInt8(i++); } else { arg1 = code[i++]; arg2 = code[i++]; @@ -232,17 +227,17 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) { scale01 = 0, scale10 = 0; if (flags & 0x08) { - scaleX = scaleY = getFloat214(code, i); + scaleX = scaleY = getFloat214(view, i); i += 2; } else if (flags & 0x40) { - scaleX = getFloat214(code, i); - scaleY = getFloat214(code, i + 2); + scaleX = getFloat214(view, i); + scaleY = getFloat214(view, i + 2); i += 4; } else if (flags & 0x80) { - scaleX = getFloat214(code, i); - scale01 = getFloat214(code, i + 2); - scale10 = getFloat214(code, i + 4); - scaleY = getFloat214(code, i + 6); + scaleX = getFloat214(view, i); + scale01 = getFloat214(view, i + 2); + scale10 = getFloat214(view, i + 4); + scaleY = getFloat214(view, i + 6); i += 8; } const subglyph = font.glyphs[glyphIndex]; @@ -265,10 +260,10 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) { const endPtsOfContours = []; let j, jj; for (j = 0; j < numberOfContours; j++) { - endPtsOfContours.push(readUint16(code, i)); + endPtsOfContours.push(view.getUint16(i)); i += 2; } - const instructionLength = readUint16(code, i); + const instructionLength = view.getUint16(i); i += 2 + instructionLength; // skipping the instructions const numberOfPoints = endPtsOfContours.at(-1) + 1; const points = []; @@ -285,7 +280,7 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) { for (j = 0; j < numberOfPoints; j++) { switch (points[j].flags & 0x12) { case 0x00: - x += readInt16(code, i); + x += view.getInt16(i); i += 2; break; case 0x02: @@ -300,7 +295,7 @@ function compileGlyf(code, cmds, font, visitedGlyphs = new Set()) { for (j = 0; j < numberOfPoints; j++) { switch (points[j].flags & 0x24) { case 0x00: - y += readInt16(code, i); + y += view.getInt16(i); i += 2; break; case 0x04: @@ -384,6 +379,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) { let firstPoint = null; function parse(code) { + const view = new DataView(code.buffer, code.byteOffset, code.byteLength); let i = 0; while (i < code.length) { let stackClean = false; @@ -660,7 +656,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) { } break; case 28: - stack.push(readInt16(code, i)); + stack.push(view.getInt16(i)); i += 2; break; case 29: // callgsubr @@ -725,13 +721,7 @@ function compileCharString(charStringCode, cmds, font, glyphId) { } else if (v < 255) { stack.push(-(v - 251) * 256 - code[i++] - 108); } else { - stack.push( - ((code[i] << 24) | - (code[i + 1] << 16) | - (code[i + 2] << 8) | - code[i + 3]) / - 65536 - ); + stack.push(view.getInt32(i) / 65536); i += 4; } break; @@ -915,13 +905,14 @@ class Type2Compiled extends CompiledFont { class FontRendererFactory { 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; - const numTables = readUint16(data, 4); + const numTables = view.getUint16(4); for (let i = 0, p = 12; i < numTables; i++, p += 16) { const tag = bytesToString(data.subarray(p, p + 4)); - const offset = readUint32(data, p + 8); - const length = readUint32(data, p + 12); + const offset = view.getUint32(p + 8); + const length = view.getUint32(p + 12); switch (tag) { case "cmap": cmap = parseCmap(data, offset, offset + length); @@ -933,8 +924,8 @@ class FontRendererFactory { loca = data.subarray(offset, offset + length); break; case "head": - unitsPerEm = readUint16(data, offset + 18); - indexToLocFormat = readUint16(data, offset + 50); + unitsPerEm = view.getUint16(offset + 18); + indexToLocFormat = view.getUint16(offset + 50); break; case "CFF ": cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);