diff --git a/src/core/annotation.js b/src/core/annotation.js index f2f642b5008d5..a0da4ae462ff3 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -5290,8 +5290,8 @@ class FileAttachmentAnnotation extends MarkupAnnotation { constructor(params) { super(params); - const { dict, xref } = params; - const file = new FileSpec(dict.get("FS"), xref); + const { dict } = params; + const file = new FileSpec(dict.get("FS")); this.data.annotationType = AnnotationType.FILEATTACHMENT; this.data.hasOwnCanvas = this.data.noRotate; diff --git a/src/core/catalog.js b/src/core/catalog.js index 3f73e6d918389..9ab10f281a0f5 100644 --- a/src/core/catalog.js +++ b/src/core/catalog.js @@ -1057,7 +1057,7 @@ class Catalog { if (obj instanceof Dict && obj.has("EmbeddedFiles")) { const nameTree = new NameTree(obj.getRaw("EmbeddedFiles"), this.xref); for (const [key, value] of nameTree.getAll()) { - const fs = new FileSpec(value, this.xref); + const fs = new FileSpec(value); attachments ??= Object.create(null); attachments[stringToPDFString(key, /* keepEscapeSequence = */ true)] = fs.serializable; @@ -1623,23 +1623,21 @@ class Catalog { case "GoToR": const urlDict = action.get("F"); if (urlDict instanceof Dict) { - const fs = new FileSpec( - urlDict, - /* xref = */ null, - /* skipContent = */ true - ); - const { rawFilename } = fs.serializable; - url = rawFilename; + const fs = new FileSpec(urlDict, /* skipContent = */ true); + ({ rawFilename: url } = fs.serializable); } else if (typeof urlDict === "string") { url = urlDict; + } else { + break; } // NOTE: the destination is relative to the *remote* document. const remoteDest = fetchRemoteDest(action); - if (remoteDest && typeof url === "string") { + if (remoteDest) { // NOTE: We don't use the `updateUrlHash` function here, since - // the `createValidAbsoluteUrl` function (see below) already - // handles parsing and validation of the final URL. + // the `createValidAbsoluteUrl` function (see below) already handles + // parsing/validation of the final URL and manual splitting also + // ensures that the `unsafeUrl` property will be available/correct. url = /* baseUrl = */ url.split("#", 1)[0] + "#" + remoteDest; } // The 'NewWindow' property, equal to `LinkTarget.BLANK`. diff --git a/src/core/document.js b/src/core/document.js index a33d20385c09c..80e49ca9f0aed 100644 --- a/src/core/document.js +++ b/src/core/document.js @@ -27,7 +27,6 @@ import { stringToBytes, stringToPDFString, stringToUTF8String, - toHexUtil, unreachable, Util, warn, @@ -1605,8 +1604,8 @@ class PDFDocument { } return shadow(this, "fingerprints", [ - toHexUtil(hashOriginal), - hashModified ? toHexUtil(hashModified) : null, + hashOriginal.toHex(), + hashModified?.toHex() ?? null, ]); } diff --git a/src/core/file_spec.js b/src/core/file_spec.js index d331af04e93ce..c52ff54d33a45 100644 --- a/src/core/file_spec.js +++ b/src/core/file_spec.js @@ -13,26 +13,18 @@ * limitations under the License. */ -import { shadow, stringToPDFString, warn } from "../shared/util.js"; +import { stringToPDFString, warn } from "../shared/util.js"; import { BaseStream } from "./base_stream.js"; import { Dict } from "./primitives.js"; function pickPlatformItem(dict) { - if (!(dict instanceof Dict)) { - return null; - } - // Look for the filename in this order: - // UF, F, Unix, Mac, DOS - if (dict.has("UF")) { - return dict.get("UF"); - } else if (dict.has("F")) { - return dict.get("F"); - } else if (dict.has("Unix")) { - return dict.get("Unix"); - } else if (dict.has("Mac")) { - return dict.get("Mac"); - } else if (dict.has("DOS")) { - return dict.get("DOS"); + if (dict instanceof Dict) { + // Look for the filename in this order: UF, F, Unix, Mac, DOS + for (const key of ["UF", "F", "Unix", "Mac", "DOS"]) { + if (dict.has(key)) { + return dict.get(key); + } + } } return null; } @@ -51,11 +43,10 @@ function stripPath(str) { class FileSpec { #contentAvailable = false; - constructor(root, xref, skipContent = false) { + constructor(root, skipContent = false) { if (!(root instanceof Dict)) { return; } - this.xref = xref; this.root = root; if (root.has("FS")) { this.fs = root.get("FS"); @@ -73,56 +64,44 @@ class FileSpec { } get filename() { - let filename = ""; - const item = pickPlatformItem(this.root); if (item && typeof item === "string") { - filename = stringToPDFString(item, /* keepEscapeSequence = */ true) + return stringToPDFString(item, /* keepEscapeSequence = */ true) .replaceAll("\\\\", "\\") .replaceAll("\\/", "/") .replaceAll("\\", "/"); } - return shadow(this, "filename", filename || "unnamed"); + return ""; } get content() { if (!this.#contentAvailable) { return null; } - this._contentRef ||= pickPlatformItem(this.root?.get("EF")); + const ef = pickPlatformItem(this.root?.get("EF")); - let content = null; - if (this._contentRef) { - const fileObj = this.xref.fetchIfRef(this._contentRef); - if (fileObj instanceof BaseStream) { - content = fileObj.getBytes(); - } else { - warn( - "Embedded file specification points to non-existing/invalid content" - ); - } - } else { - warn("Embedded file specification does not have any content"); + if (ef instanceof BaseStream) { + return ef.getBytes(); } - return content; + warn("Embedded file specification points to non-existing/invalid content"); + return null; } get description() { - let description = ""; - const desc = this.root?.get("Desc"); if (desc && typeof desc === "string") { - description = stringToPDFString(desc); + return stringToPDFString(desc); } - return shadow(this, "description", description); + return ""; } get serializable() { + const { filename, content, description } = this; return { - rawFilename: this.filename, - filename: stripPath(this.filename), - content: this.content, - description: this.description, + rawFilename: filename, + filename: stripPath(filename) || "unnamed", + content, + description, }; } } diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index 61322dcd9cda7..4c643d51722a5 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -90,7 +90,6 @@ import { XFAObject, XFAObjectArray, } from "./xfa_object.js"; -import { fromBase64Util, Util, warn } from "../../shared/util.js"; import { getBBox, getColor, @@ -103,6 +102,7 @@ import { getStringOption, HTMLResult, } from "./utils.js"; +import { Util, warn } from "../../shared/util.js"; import { getMetrics } from "./fonts.js"; import { recoverJsURL } from "../core_utils.js"; import { searchNode } from "./som.js"; @@ -3420,7 +3420,7 @@ class Image extends StringObject { } if (!buffer && this.transferEncoding === "base64") { - buffer = fromBase64Util(this[$content]); + buffer = Uint8Array.fromBase64(this[$content]); } if (!buffer) { diff --git a/src/display/editor/drawers/signaturedraw.js b/src/display/editor/drawers/signaturedraw.js index f90a1428edac2..d3e217d6cda7a 100644 --- a/src/display/editor/drawers/signaturedraw.js +++ b/src/display/editor/drawers/signaturedraw.js @@ -13,10 +13,10 @@ * limitations under the License. */ -import { fromBase64Util, toBase64Util, warn } from "../../../shared/util.js"; import { ContourDrawOutline } from "./contour.js"; import { InkDrawOutline } from "./inkdraw.js"; import { Outline } from "./outline.js"; +import { warn } from "../../../shared/util.js"; const BASE_HEADER_LENGTH = 8; const POINTS_PROPERTIES_NUMBER = 3; @@ -749,12 +749,12 @@ class SignatureExtractor { const buf = await new Response(cs.readable).arrayBuffer(); const bytes = new Uint8Array(buf); - return toBase64Util(bytes); + return bytes.toBase64(); } static async decompressSignature(signatureData) { try { - const bytes = fromBase64Util(signatureData); + const bytes = Uint8Array.fromBase64(signatureData); const { readable, writable } = new DecompressionStream("deflate-raw"); const writer = writable.getWriter(); await writer.ready; diff --git a/src/display/font_loader.js b/src/display/font_loader.js index b74349ac0f6fc..b7b27e6ddbfd8 100644 --- a/src/display/font_loader.js +++ b/src/display/font_loader.js @@ -19,7 +19,6 @@ import { isNodeJS, shadow, string32, - toBase64Util, unreachable, warn, } from "../shared/util.js"; @@ -408,7 +407,7 @@ class FontFaceObject { return null; } // Add the @font-face rule to the document. - const url = `url(data:${this.mimetype};base64,${toBase64Util(this.data)});`; + const url = `url(data:${this.mimetype};base64,${this.data.toBase64()});`; 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 b50819976f854..df9138133e43d 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -1235,44 +1235,6 @@ function MathClamp(v, min, max) { return Math.min(Math.max(v, min), max); } -// TODO: Remove this once `Uint8Array.prototype.toHex` is generally available. -function toHexUtil(arr) { - if (Uint8Array.prototype.toHex) { - return arr.toHex(); - } - return Array.from(arr, num => hexNumbers[num]).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)); -} - -// TODO: Remove this once https://bugzilla.mozilla.org/show_bug.cgi?id=1928493 -// is fixed. -if ( - (typeof PDFJSDev === "undefined" || PDFJSDev.test("SKIP_BABEL")) && - typeof Promise.try !== "function" -) { - Promise.try = function (fn, ...args) { - return new Promise(resolve => { - resolve(fn(...args)); - }); - }; -} - // TODO: Remove this once the `javascript.options.experimental.math_sumprecise` // preference is removed from Firefox. if (typeof Math.sumPrecise !== "function") { @@ -1338,7 +1300,6 @@ export { FeatureTest, FONT_IDENTITY_MATRIX, FormatError, - fromBase64Util, getModificationDate, getUuid, getVerbosityLevel, @@ -1368,8 +1329,6 @@ export { stringToPDFString, stringToUTF8String, TextRenderingMode, - toBase64Util, - toHexUtil, UnknownErrorException, unreachable, updateUrlHash, diff --git a/test/unit/display_utils_spec.js b/test/unit/display_utils_spec.js index 45a0d16149c68..87da7c0ca063d 100644 --- a/test/unit/display_utils_spec.js +++ b/test/unit/display_utils_spec.js @@ -22,7 +22,7 @@ import { PDFDateString, renderRichText, } from "../../src/display/display_utils.js"; -import { isNodeJS, toBase64Util } from "../../src/shared/util.js"; +import { isNodeJS } from "../../src/shared/util.js"; describe("display_utils", function () { describe("getFilenameFromUrl", function () { @@ -183,9 +183,7 @@ describe("display_utils", function () { it('gets fallback filename from query string appended to "data:" URL', function () { const typedArray = new Uint8Array([1, 2, 3, 4, 5]); - const dataUrl = `data:application/pdf;base64,${toBase64Util(typedArray)}`; - // Sanity check to ensure that a "data:" URL was returned. - expect(dataUrl.startsWith("data:")).toEqual(true); + const dataUrl = `data:application/pdf;base64,${typedArray.toBase64()}`; expect(getPdfFilenameFromUrl(dataUrl + "?file1.pdf")).toEqual( "document.pdf"