diff --git a/src/core/document.js b/src/core/document.js index 2dab34d062000..434cbac2a6325 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -26,6 +26,7 @@ import { stringToBytes, stringToPDFString, stringToUTF8String, + toHexUtil, unreachable, Util, warn, @@ -862,10 +863,6 @@ const STARTXREF_SIGNATURE = new Uint8Array([ ]); const ENDOBJ_SIGNATURE = new Uint8Array([0x65, 0x6e, 0x64, 0x6f, 0x62, 0x6a]); -const FINGERPRINT_FIRST_BYTES = 1024; -const EMPTY_FINGERPRINT = - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - function find(stream, signature, limit = 1024, backwards = false) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { assert(limit > 0, 'The "limit" must be a positive integer.'); @@ -1548,30 +1545,24 @@ class PDFDocument { } get fingerprints() { + const FINGERPRINT_FIRST_BYTES = 1024; + const EMPTY_FINGERPRINT = "\x00".repeat(16); + function validate(data) { return ( typeof data === "string" && - data.length > 0 && + data.length === 16 && data !== EMPTY_FINGERPRINT ); } - function hexString(hash) { - const buf = []; - for (const num of hash) { - const hex = num.toString(16); - buf.push(hex.padStart(2, "0")); - } - return buf.join(""); - } - - const idArray = this.xref.trailer.get("ID"); + const id = this.xref.trailer.get("ID"); let hashOriginal, hashModified; - if (Array.isArray(idArray) && validate(idArray[0])) { - hashOriginal = stringToBytes(idArray[0]); + if (Array.isArray(id) && validate(id[0])) { + hashOriginal = stringToBytes(id[0]); - if (idArray[1] !== idArray[0] && validate(idArray[1])) { - hashModified = stringToBytes(idArray[1]); + if (id[1] !== id[0] && validate(id[1])) { + hashModified = stringToBytes(id[1]); } } else { hashOriginal = calculateMD5( @@ -1582,8 +1573,8 @@ class PDFDocument { } return shadow(this, "fingerprints", [ - hexString(hashOriginal), - hashModified ? hexString(hashModified) : null, + toHexUtil(hashOriginal), + hashModified ? toHexUtil(hashModified) : null, ]); } diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 8ec158ff888fe..96d18930c1c53 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -90,6 +90,7 @@ import { XFAObject, XFAObjectArray, } from "./xfa_object.js"; +import { fromBase64Util, Util, warn } from "../../shared/util.js"; import { getBBox, getColor, @@ -102,7 +103,6 @@ import { getStringOption, HTMLResult, } from "./utils.js"; -import { stringToBytes, Util, warn } from "../../shared/util.js"; import { getMetrics } from "./fonts.js"; import { recoverJsURL } from "../core_utils.js"; import { searchNode } from "./som.js"; @@ -3427,7 +3427,7 @@ class Image extends StringObject { } if (!buffer && this.transferEncoding === "base64") { - buffer = stringToBytes(atob(this[$content])); + buffer = fromBase64Util(this[$content]); } if (!buffer) { diff --git a/src/display/font_loader.js b/src/display/font_loader.js index 1014c536b67e0..1ec7160c8500a 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -15,11 +15,11 @@ import { assert, - bytesToString, FontRenderOps, isNodeJS, shadow, string32, + toBase64Util, unreachable, warn, } from "../shared/util.js"; @@ -399,9 +399,8 @@ class FontFaceObject { if (!this.data || this.disableFontFace) { return null; } - const data = bytesToString(this.data); // Add the @font-face rule to the document. - const url = `url(data:${this.mimetype};base64,${btoa(data)});`; + const url = `url(data:${this.mimetype};base64,${toBase64Util(this.data)});`; let rule; if (!this.cssFontInfo) { rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`; diff --git a/src/shared/util.js b/src/shared/util.js index eccd578974b92..700f9f87104bb 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -1097,6 +1097,35 @@ const FontRenderOps = { TRANSLATE: 8, }; +// TODO: Remove this once `Uint8Array.prototype.toHex` is generally available. +function toHexUtil(arr) { + if (Uint8Array.prototype.toHex) { + return arr.toHex(); + } + const buf = []; + for (const num of arr) { + buf.push(num.toString(16).padStart(2, "0")); + } + return buf.join(""); +} + +// TODO: Remove this once `Uint8Array.prototype.toBase64` is generally +// available. +function toBase64Util(arr) { + if (Uint8Array.prototype.toBase64) { + return arr.toBase64(); + } + return btoa(bytesToString(arr)); +} + +// TODO: Remove this once `Uint8Array.fromBase64` is generally available. +function fromBase64Util(str) { + if (Uint8Array.fromBase64) { + return Uint8Array.fromBase64(str); + } + return stringToBytes(atob(str)); +} + export { AbortException, AnnotationActionEventType, @@ -1120,6 +1149,7 @@ export { FONT_IDENTITY_MATRIX, FontRenderOps, FormatError, + fromBase64Util, getModificationDate, getUuid, getVerbosityLevel, @@ -1149,6 +1179,8 @@ export { stringToPDFString, stringToUTF8String, TextRenderingMode, + toBase64Util, + toHexUtil, UnexpectedResponseException, UnknownErrorException, unreachable,