commit c4e294aaf9be3773f7b435174b035cc0715c8325
parent 6cebd143bf4d5cb9d727f52e6f1dbaccb3d90a28
Author: Calixte Denizet <calixte.denizet@gmail.com>
Date: Thu, 23 Oct 2025 10:35:05 +0000
Bug 1995924 - Update PDF.js to new version f6317ddbbb847b9d6e70aa843f651a93e470257b r=pdfjs-reviewers,fluent-reviewers,flod,marco
Differential Revision: https://phabricator.services.mozilla.com/D269707
Diffstat:
8 files changed, 5199 insertions(+), 4715 deletions(-)
diff --git a/toolkit/components/pdfjs/content/build/pdf.mjs b/toolkit/components/pdfjs/content/build/pdf.mjs
@@ -21,8 +21,8 @@
*/
/**
- * pdfjsVersion = 5.4.329
- * pdfjsBuild = 3eca60735
+ * pdfjsVersion = 5.4.370
+ * pdfjsBuild = f6317ddbb
*/
/******/ // The require scope
/******/ var __webpack_require__ = {};
@@ -331,7 +331,8 @@ const DrawOPS = {
moveTo: 0,
lineTo: 1,
curveTo: 2,
- closePath: 3
+ quadraticCurveTo: 3,
+ closePath: 4
};
const PasswordResponses = {
NEED_PASSWORD: 1,
@@ -520,6 +521,9 @@ class util_FeatureTest {
static get isImageDecoderSupported() {
return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined");
}
+ static get isFloat16ArraySupported() {
+ return shadow(this, "isFloat16ArraySupported", typeof Float16Array !== "undefined");
+ }
static get platform() {
const {
platform,
@@ -1812,6 +1816,35 @@ function renderRichText({
fragment.firstChild.classList.add("richText", className);
container.append(fragment);
}
+function makePathFromDrawOPS(data) {
+ const path = new Path2D();
+ if (!data) {
+ return path;
+ }
+ for (let i = 0, ii = data.length; i < ii;) {
+ switch (data[i++]) {
+ case DrawOPS.moveTo:
+ path.moveTo(data[i++], data[i++]);
+ break;
+ case DrawOPS.lineTo:
+ path.lineTo(data[i++], data[i++]);
+ break;
+ case DrawOPS.curveTo:
+ path.bezierCurveTo(data[i++], data[i++], data[i++], data[i++], data[i++], data[i++]);
+ break;
+ case DrawOPS.quadraticCurveTo:
+ path.quadraticCurveTo(data[i++], data[i++], data[i++], data[i++]);
+ break;
+ case DrawOPS.closePath:
+ path.closePath();
+ break;
+ default:
+ warn(`Unrecognized drawing path operator: ${data[i - 1]}`);
+ break;
+ }
+ }
+ return path;
+}
;// ./src/display/editor/toolbar.js
@@ -1978,23 +2011,34 @@ class EditorToolbar {
async addButton(name, tool) {
switch (name) {
case "colorPicker":
- this.addColorPicker(tool);
+ if (tool) {
+ this.addColorPicker(tool);
+ }
break;
case "altText":
- await this.addAltText(tool);
+ if (tool) {
+ await this.addAltText(tool);
+ }
break;
case "editSignature":
- await this.addEditSignatureButton(tool);
+ if (tool) {
+ await this.addEditSignatureButton(tool);
+ }
break;
case "delete":
this.addDeleteButton();
break;
case "comment":
- this.addComment(tool);
+ if (tool) {
+ this.addComment(tool);
+ }
break;
}
}
async addButtonBefore(name, tool, beforeSelector) {
+ if (!tool && name === "comment") {
+ return;
+ }
const beforeElement = this.#buttons.querySelector(beforeSelector);
if (!beforeElement) {
return;
@@ -3195,11 +3239,11 @@ class AnnotationEditorUIManager {
}
addEditListeners() {
this.#addKeyboardManager();
- this.#addCopyPasteListeners();
+ this.setEditingState(true);
}
removeEditListeners() {
this.#removeKeyboardManager();
- this.#removeCopyPasteListeners();
+ this.setEditingState(false);
}
dragOver(event) {
for (const {
@@ -4482,7 +4526,7 @@ class Comment {
comment.setAttribute("data-l10n-id", "pdfjs-show-comment-button");
} else {
comment.ariaControlsElements = [this.#editor._uiManager.getCommentDialogElement()];
- comment.setAttribute("data-l10n-id", "pdfjs-editor-edit-comment-button");
+ comment.setAttribute("data-l10n-id", "pdfjs-editor-add-comment-button");
}
const signal = this.#editor._uiManager._signal;
if (!(signal instanceof AbortSignal) || signal.aborted) {
@@ -4895,6 +4939,7 @@ class AnnotationEditor {
this.annotationElementId = parameters.annotationElementId || null;
this.creationDate = parameters.creationDate || new Date();
this.modificationDate = parameters.modificationDate || null;
+ this.canAddComment = true;
const {
rotation,
rawDims: {
@@ -5574,7 +5619,7 @@ class AnnotationEditor {
this.#comment?.focusButton();
}
addCommentButton() {
- return this.#comment ||= new Comment(this);
+ return this.canAddComment ? this.#comment ||= new Comment(this) : null;
}
addStandaloneCommentButton() {
if (!this._uiManager.hasCommentManager()) {
@@ -6150,7 +6195,7 @@ class AnnotationEditor {
return this.div;
}
setCommentButtonStates(options) {
- this.#comment.setCommentButtonStates(options);
+ this.#comment?.setCommentButtonStates(options);
}
keydown(event) {
if (!this.isResizable || event.target !== this.div || event.key !== "Enter") {
@@ -6814,6 +6859,7 @@ class PrintAnnotationStorage extends AnnotationStorage {
;// ./src/display/font_loader.js
+
class FontLoader {
#systemFonts = new Set();
constructor({
@@ -6990,7 +7036,7 @@ class FontFaceObject {
} catch (ex) {
warn(`getPathGenerator - ignoring character: "${ex}".`);
}
- const path = new Path2D(cmds || "");
+ const path = makePathFromDrawOPS(cmds);
if (!this.fontExtraProperties) {
objs.delete(objId);
}
@@ -7070,4646 +7116,4830 @@ class FontFaceObject {
}
}
-;// ./src/display/api_utils.js
+;// ./src/shared/obj-bin-transform.js
-function getUrlProp(val) {
- return null;
-}
-function getDataProp(val) {
- if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
- return val;
+class CssFontInfo {
+ #buffer;
+ #view;
+ #decoder;
+ static strings = ["fontFamily", "fontWeight", "italicAngle"];
+ static write(info) {
+ const encoder = new TextEncoder();
+ const encodedStrings = {};
+ let stringsLength = 0;
+ for (const prop of CssFontInfo.strings) {
+ const encoded = encoder.encode(info[prop]);
+ encodedStrings[prop] = encoded;
+ stringsLength += 4 + encoded.length;
+ }
+ const buffer = new ArrayBuffer(stringsLength);
+ const data = new Uint8Array(buffer);
+ const view = new DataView(buffer);
+ let offset = 0;
+ for (const prop of CssFontInfo.strings) {
+ const encoded = encodedStrings[prop];
+ const length = encoded.length;
+ view.setUint32(offset, length);
+ data.set(encoded, offset + 4);
+ offset += 4 + length;
+ }
+ assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow");
+ return buffer;
}
- if (typeof val === "string") {
- return stringToBytes(val);
+ constructor(buffer) {
+ this.#buffer = buffer;
+ this.#view = new DataView(this.#buffer);
+ this.#decoder = new TextDecoder();
}
- if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) {
- return new Uint8Array(val);
+ #readString(index) {
+ assert(index < CssFontInfo.strings.length, "Invalid string index");
+ let offset = 0;
+ for (let i = 0; i < index; i++) {
+ offset += this.#view.getUint32(offset) + 4;
+ }
+ const length = this.#view.getUint32(offset);
+ return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
}
- throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property.");
-}
-function getFactoryUrlProp(val) {
- if (typeof val !== "string") {
- return null;
+ get fontFamily() {
+ return this.#readString(0);
}
- if (val.endsWith("/")) {
- return val;
+ get fontWeight() {
+ return this.#readString(1);
}
- throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
-}
-const isRefProxy = v => typeof v === "object" && Number.isInteger(v?.num) && v.num >= 0 && Number.isInteger(v?.gen) && v.gen >= 0;
-const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
-const isValidExplicitDest = _isValidExplicitDest.bind(null, isRefProxy, isNameProxy);
-class LoopbackPort {
- #listeners = new Map();
- #deferred = Promise.resolve();
- postMessage(obj, transfer) {
- const event = {
- data: structuredClone(obj, transfer ? {
- transfer
- } : null)
- };
- this.#deferred.then(() => {
- for (const [listener] of this.#listeners) {
- listener.call(this, event);
- }
- });
+ get italicAngle() {
+ return this.#readString(2);
}
- addEventListener(name, listener, options = null) {
- let rmAbort = null;
- if (options?.signal instanceof AbortSignal) {
- const {
- signal
- } = options;
- if (signal.aborted) {
- warn("LoopbackPort - cannot use an `aborted` signal.");
- return;
- }
- const onAbort = () => this.removeEventListener(name, listener);
- rmAbort = () => signal.removeEventListener("abort", onAbort);
- signal.addEventListener("abort", onAbort);
+}
+class SystemFontInfo {
+ #buffer;
+ #view;
+ #decoder;
+ static strings = ["css", "loadedName", "baseFontName", "src"];
+ static write(info) {
+ const encoder = new TextEncoder();
+ const encodedStrings = {};
+ let stringsLength = 0;
+ for (const prop of SystemFontInfo.strings) {
+ const encoded = encoder.encode(info[prop]);
+ encodedStrings[prop] = encoded;
+ stringsLength += 4 + encoded.length;
}
- this.#listeners.set(listener, rmAbort);
+ stringsLength += 4;
+ let encodedStyleStyle,
+ encodedStyleWeight,
+ lengthEstimate = 1 + stringsLength;
+ if (info.style) {
+ encodedStyleStyle = encoder.encode(info.style.style);
+ encodedStyleWeight = encoder.encode(info.style.weight);
+ lengthEstimate += 4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length;
+ }
+ const buffer = new ArrayBuffer(lengthEstimate);
+ const data = new Uint8Array(buffer);
+ const view = new DataView(buffer);
+ let offset = 0;
+ view.setUint8(offset++, info.guessFallback ? 1 : 0);
+ view.setUint32(offset, 0);
+ offset += 4;
+ stringsLength = 0;
+ for (const prop of SystemFontInfo.strings) {
+ const encoded = encodedStrings[prop];
+ const length = encoded.length;
+ stringsLength += 4 + length;
+ view.setUint32(offset, length);
+ data.set(encoded, offset + 4);
+ offset += 4 + length;
+ }
+ view.setUint32(offset - stringsLength - 4, stringsLength);
+ if (info.style) {
+ view.setUint32(offset, encodedStyleStyle.length);
+ data.set(encodedStyleStyle, offset + 4);
+ offset += 4 + encodedStyleStyle.length;
+ view.setUint32(offset, encodedStyleWeight.length);
+ data.set(encodedStyleWeight, offset + 4);
+ offset += 4 + encodedStyleWeight.length;
+ }
+ assert(offset <= buffer.byteLength, "SubstitionInfo.write: Buffer overflow");
+ return buffer.transferToFixedLength(offset);
}
- removeEventListener(name, listener) {
- const rmAbort = this.#listeners.get(listener);
- rmAbort?.();
- this.#listeners.delete(listener);
+ constructor(buffer) {
+ this.#buffer = buffer;
+ this.#view = new DataView(this.#buffer);
+ this.#decoder = new TextDecoder();
}
- terminate() {
- for (const [, rmAbort] of this.#listeners) {
- rmAbort?.();
+ get guessFallback() {
+ return this.#view.getUint8(0) !== 0;
+ }
+ #readString(index) {
+ assert(index < SystemFontInfo.strings.length, "Invalid string index");
+ let offset = 5;
+ for (let i = 0; i < index; i++) {
+ offset += this.#view.getUint32(offset) + 4;
}
- this.#listeners.clear();
+ const length = this.#view.getUint32(offset);
+ return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
}
-}
-
-;// ./src/shared/message_handler.js
-
-const CallbackKind = {
- DATA: 1,
- ERROR: 2
-};
-const StreamKind = {
- CANCEL: 1,
- CANCEL_COMPLETE: 2,
- CLOSE: 3,
- ENQUEUE: 4,
- ERROR: 5,
- PULL: 6,
- PULL_COMPLETE: 7,
- START_COMPLETE: 8
-};
-function onFn() {}
-function wrapReason(ex) {
- if (ex instanceof AbortException || ex instanceof InvalidPDFException || ex instanceof PasswordException || ex instanceof ResponseException || ex instanceof UnknownErrorException) {
- return ex;
+ get css() {
+ return this.#readString(0);
}
- if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) {
- unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');
+ get loadedName() {
+ return this.#readString(1);
}
- switch (ex.name) {
- case "AbortException":
- return new AbortException(ex.message);
- case "InvalidPDFException":
- return new InvalidPDFException(ex.message);
- case "PasswordException":
- return new PasswordException(ex.message, ex.code);
- case "ResponseException":
- return new ResponseException(ex.message, ex.status, ex.missing);
- case "UnknownErrorException":
- return new UnknownErrorException(ex.message, ex.details);
+ get baseFontName() {
+ return this.#readString(2);
}
- return new UnknownErrorException(ex.message, ex.toString());
-}
-class MessageHandler {
- #messageAC = new AbortController();
- constructor(sourceName, targetName, comObj) {
- this.sourceName = sourceName;
- this.targetName = targetName;
- this.comObj = comObj;
- this.callbackId = 1;
- this.streamId = 1;
- this.streamSinks = Object.create(null);
- this.streamControllers = Object.create(null);
- this.callbackCapabilities = Object.create(null);
- this.actionHandler = Object.create(null);
- comObj.addEventListener("message", this.#onMessage.bind(this), {
- signal: this.#messageAC.signal
- });
+ get src() {
+ return this.#readString(3);
}
- #onMessage({
- data
- }) {
- if (data.targetName !== this.sourceName) {
- return;
- }
- if (data.stream) {
- this.#processStreamMessage(data);
- return;
+ get style() {
+ let offset = 1;
+ offset += 4 + this.#view.getUint32(offset);
+ const styleLength = this.#view.getUint32(offset);
+ const style = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, styleLength));
+ offset += 4 + styleLength;
+ const weightLength = this.#view.getUint32(offset);
+ const weight = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, weightLength));
+ return {
+ style,
+ weight
+ };
+ }
+}
+class FontInfo {
+ static bools = ["black", "bold", "disableFontFace", "fontExtraProperties", "isInvalidPDFjsFont", "isType3Font", "italic", "missingFile", "remeasure", "vertical"];
+ static numbers = ["ascent", "defaultWidth", "descent"];
+ static strings = ["fallbackName", "loadedName", "mimetype", "name"];
+ static #OFFSET_NUMBERS = Math.ceil(this.bools.length * 2 / 8);
+ static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8;
+ static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4;
+ static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6;
+ static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3;
+ #buffer;
+ #decoder;
+ #view;
+ constructor({
+ data,
+ extra
+ }) {
+ this.#buffer = data;
+ this.#decoder = new TextDecoder();
+ this.#view = new DataView(this.#buffer);
+ if (extra) {
+ Object.assign(this, extra);
}
- if (data.callback) {
- const callbackId = data.callbackId;
- const capability = this.callbackCapabilities[callbackId];
- if (!capability) {
- throw new Error(`Cannot resolve callback ${callbackId}`);
- }
- delete this.callbackCapabilities[callbackId];
- if (data.callback === CallbackKind.DATA) {
- capability.resolve(data.data);
- } else if (data.callback === CallbackKind.ERROR) {
- capability.reject(wrapReason(data.reason));
- } else {
- throw new Error("Unexpected callback case");
- }
- return;
+ }
+ #readBoolean(index) {
+ assert(index < FontInfo.bools.length, "Invalid boolean index");
+ const byteOffset = Math.floor(index / 4);
+ const bitOffset = index * 2 % 8;
+ const value = this.#view.getUint8(byteOffset) >> bitOffset & 0x03;
+ return value === 0x00 ? undefined : value === 0x02;
+ }
+ get black() {
+ return this.#readBoolean(0);
+ }
+ get bold() {
+ return this.#readBoolean(1);
+ }
+ get disableFontFace() {
+ return this.#readBoolean(2);
+ }
+ get fontExtraProperties() {
+ return this.#readBoolean(3);
+ }
+ get isInvalidPDFjsFont() {
+ return this.#readBoolean(4);
+ }
+ get isType3Font() {
+ return this.#readBoolean(5);
+ }
+ get italic() {
+ return this.#readBoolean(6);
+ }
+ get missingFile() {
+ return this.#readBoolean(7);
+ }
+ get remeasure() {
+ return this.#readBoolean(8);
+ }
+ get vertical() {
+ return this.#readBoolean(9);
+ }
+ #readNumber(index) {
+ assert(index < FontInfo.numbers.length, "Invalid number index");
+ return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8);
+ }
+ get ascent() {
+ return this.#readNumber(0);
+ }
+ get defaultWidth() {
+ return this.#readNumber(1);
+ }
+ get descent() {
+ return this.#readNumber(2);
+ }
+ get bbox() {
+ let offset = FontInfo.#OFFSET_BBOX;
+ const numCoords = this.#view.getUint8(offset);
+ if (numCoords === 0) {
+ return undefined;
}
- const action = this.actionHandler[data.action];
- if (!action) {
- throw new Error(`Unknown action from worker: ${data.action}`);
+ offset += 1;
+ const bbox = [];
+ for (let i = 0; i < 4; i++) {
+ bbox.push(this.#view.getInt16(offset, true));
+ offset += 2;
}
- if (data.callbackId) {
- const sourceName = this.sourceName,
- targetName = data.sourceName,
- comObj = this.comObj;
- Promise.try(action, data.data).then(function (result) {
- comObj.postMessage({
- sourceName,
- targetName,
- callback: CallbackKind.DATA,
- callbackId: data.callbackId,
- data: result
- });
- }, function (reason) {
- comObj.postMessage({
- sourceName,
- targetName,
- callback: CallbackKind.ERROR,
- callbackId: data.callbackId,
- reason: wrapReason(reason)
- });
- });
- return;
+ return bbox;
+ }
+ get fontMatrix() {
+ let offset = FontInfo.#OFFSET_FONT_MATRIX;
+ const numPoints = this.#view.getUint8(offset);
+ if (numPoints === 0) {
+ return undefined;
}
- if (data.streamId) {
- this.#createStreamSink(data);
- return;
+ offset += 1;
+ const fontMatrix = [];
+ for (let i = 0; i < 6; i++) {
+ fontMatrix.push(this.#view.getFloat64(offset, true));
+ offset += 8;
}
- action(data.data);
+ return fontMatrix;
}
- on(actionName, handler) {
- const ah = this.actionHandler;
- if (ah[actionName]) {
- throw new Error(`There is already an actionName called "${actionName}"`);
+ get defaultVMetrics() {
+ let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS;
+ const numMetrics = this.#view.getUint8(offset);
+ if (numMetrics === 0) {
+ return undefined;
}
- ah[actionName] = handler;
+ offset += 1;
+ const defaultVMetrics = [];
+ for (let i = 0; i < 3; i++) {
+ defaultVMetrics.push(this.#view.getInt16(offset, true));
+ offset += 2;
+ }
+ return defaultVMetrics;
}
- send(actionName, data, transfers) {
- this.comObj.postMessage({
- sourceName: this.sourceName,
- targetName: this.targetName,
- action: actionName,
- data
- }, transfers);
+ #readString(index) {
+ assert(index < FontInfo.strings.length, "Invalid string index");
+ let offset = FontInfo.#OFFSET_STRINGS + 4;
+ for (let i = 0; i < index; i++) {
+ offset += this.#view.getUint32(offset) + 4;
+ }
+ const length = this.#view.getUint32(offset);
+ const stringData = new Uint8Array(length);
+ stringData.set(new Uint8Array(this.#buffer, offset + 4, length));
+ return this.#decoder.decode(stringData);
}
- sendWithPromise(actionName, data, transfers) {
- const callbackId = this.callbackId++;
- const capability = Promise.withResolvers();
- this.callbackCapabilities[callbackId] = capability;
- try {
- this.comObj.postMessage({
- sourceName: this.sourceName,
- targetName: this.targetName,
- action: actionName,
- callbackId,
- data
- }, transfers);
- } catch (ex) {
- capability.reject(ex);
+ get fallbackName() {
+ return this.#readString(0);
+ }
+ get loadedName() {
+ return this.#readString(1);
+ }
+ get mimetype() {
+ return this.#readString(2);
+ }
+ get name() {
+ return this.#readString(3);
+ }
+ get data() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + systemFontInfoLength;
+ const cssFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + cssFontInfoLength;
+ const length = this.#view.getUint32(offset);
+ if (length === 0) {
+ return undefined;
}
- return capability.promise;
+ return new Uint8Array(this.#buffer, offset + 4, length);
}
- sendWithStream(actionName, data, queueingStrategy, transfers) {
- const streamId = this.streamId++,
- sourceName = this.sourceName,
- targetName = this.targetName,
- comObj = this.comObj;
- return new ReadableStream({
- start: controller => {
- const startCapability = Promise.withResolvers();
- this.streamControllers[streamId] = {
- controller,
- startCall: startCapability,
- pullCall: null,
- cancelCall: null,
- isClosed: false
- };
- comObj.postMessage({
- sourceName,
- targetName,
- action: actionName,
- streamId,
- data,
- desiredSize: controller.desiredSize
- }, transfers);
- return startCapability.promise;
- },
- pull: controller => {
- const pullCapability = Promise.withResolvers();
- this.streamControllers[streamId].pullCall = pullCapability;
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.PULL,
- streamId,
- desiredSize: controller.desiredSize
- });
- return pullCapability.promise;
- },
- cancel: reason => {
- assert(reason instanceof Error, "cancel must have a valid reason");
- const cancelCapability = Promise.withResolvers();
- this.streamControllers[streamId].cancelCall = cancelCapability;
- this.streamControllers[streamId].isClosed = true;
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.CANCEL,
- streamId,
- reason: wrapReason(reason)
- });
- return cancelCapability.promise;
- }
- }, queueingStrategy);
- }
- #createStreamSink(data) {
- const streamId = data.streamId,
- sourceName = this.sourceName,
- targetName = data.sourceName,
- comObj = this.comObj;
- const self = this,
- action = this.actionHandler[data.action];
- const streamSink = {
- enqueue(chunk, size = 1, transfers) {
- if (this.isCancelled) {
- return;
- }
- const lastDesiredSize = this.desiredSize;
- this.desiredSize -= size;
- if (lastDesiredSize > 0 && this.desiredSize <= 0) {
- this.sinkCapability = Promise.withResolvers();
- this.ready = this.sinkCapability.promise;
- }
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.ENQUEUE,
- streamId,
- chunk
- }, transfers);
- },
- close() {
- if (this.isCancelled) {
- return;
- }
- this.isCancelled = true;
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.CLOSE,
- streamId
- });
- delete self.streamSinks[streamId];
- },
- error(reason) {
- assert(reason instanceof Error, "error must have a valid reason");
- if (this.isCancelled) {
- return;
- }
- this.isCancelled = true;
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.ERROR,
- streamId,
- reason: wrapReason(reason)
- });
- },
- sinkCapability: Promise.withResolvers(),
- onPull: null,
- onCancel: null,
- isCancelled: false,
- desiredSize: data.desiredSize,
- ready: null
- };
- streamSink.sinkCapability.resolve();
- streamSink.ready = streamSink.sinkCapability.promise;
- this.streamSinks[streamId] = streamSink;
- Promise.try(action, data.data, streamSink).then(function () {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.START_COMPLETE,
- streamId,
- success: true
- });
- }, function (reason) {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.START_COMPLETE,
- streamId,
- reason: wrapReason(reason)
- });
- });
- }
- #processStreamMessage(data) {
- const streamId = data.streamId,
- sourceName = this.sourceName,
- targetName = data.sourceName,
- comObj = this.comObj;
- const streamController = this.streamControllers[streamId],
- streamSink = this.streamSinks[streamId];
- switch (data.stream) {
- case StreamKind.START_COMPLETE:
- if (data.success) {
- streamController.startCall.resolve();
- } else {
- streamController.startCall.reject(wrapReason(data.reason));
- }
- break;
- case StreamKind.PULL_COMPLETE:
- if (data.success) {
- streamController.pullCall.resolve();
- } else {
- streamController.pullCall.reject(wrapReason(data.reason));
- }
- break;
- case StreamKind.PULL:
- if (!streamSink) {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.PULL_COMPLETE,
- streamId,
- success: true
- });
- break;
- }
- if (streamSink.desiredSize <= 0 && data.desiredSize > 0) {
- streamSink.sinkCapability.resolve();
- }
- streamSink.desiredSize = data.desiredSize;
- Promise.try(streamSink.onPull || onFn).then(function () {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.PULL_COMPLETE,
- streamId,
- success: true
- });
- }, function (reason) {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.PULL_COMPLETE,
- streamId,
- reason: wrapReason(reason)
- });
- });
- break;
- case StreamKind.ENQUEUE:
- assert(streamController, "enqueue should have stream controller");
- if (streamController.isClosed) {
- break;
- }
- streamController.controller.enqueue(data.chunk);
- break;
- case StreamKind.CLOSE:
- assert(streamController, "close should have stream controller");
- if (streamController.isClosed) {
- break;
- }
- streamController.isClosed = true;
- streamController.controller.close();
- this.#deleteStreamController(streamController, streamId);
- break;
- case StreamKind.ERROR:
- assert(streamController, "error should have stream controller");
- streamController.controller.error(wrapReason(data.reason));
- this.#deleteStreamController(streamController, streamId);
- break;
- case StreamKind.CANCEL_COMPLETE:
- if (data.success) {
- streamController.cancelCall.resolve();
- } else {
- streamController.cancelCall.reject(wrapReason(data.reason));
- }
- this.#deleteStreamController(streamController, streamId);
- break;
- case StreamKind.CANCEL:
- if (!streamSink) {
- break;
- }
- const dataReason = wrapReason(data.reason);
- Promise.try(streamSink.onCancel || onFn, dataReason).then(function () {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.CANCEL_COMPLETE,
- streamId,
- success: true
- });
- }, function (reason) {
- comObj.postMessage({
- sourceName,
- targetName,
- stream: StreamKind.CANCEL_COMPLETE,
- streamId,
- reason: wrapReason(reason)
- });
- });
- streamSink.sinkCapability.reject(dataReason);
- streamSink.isCancelled = true;
- delete this.streamSinks[streamId];
- break;
- default:
- throw new Error("Unexpected stream case");
- }
- }
- async #deleteStreamController(streamController, streamId) {
- await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]);
- delete this.streamControllers[streamId];
- }
- destroy() {
- this.#messageAC?.abort();
- this.#messageAC = null;
- }
-}
-
-;// ./src/display/canvas_dependency_tracker.js
-
-const FORCED_DEPENDENCY_LABEL = "__forcedDependency";
-const {
- floor,
- ceil
-} = Math;
-function expandBBox(array, index, minX, minY, maxX, maxY) {
- array[index * 4 + 0] = Math.min(array[index * 4 + 0], minX);
- array[index * 4 + 1] = Math.min(array[index * 4 + 1], minY);
- array[index * 4 + 2] = Math.max(array[index * 4 + 2], maxX);
- array[index * 4 + 3] = Math.max(array[index * 4 + 3], maxY);
-}
-const EMPTY_BBOX = new Uint32Array(new Uint8Array([255, 255, 0, 0]).buffer)[0];
-class BBoxReader {
- #bboxes;
- #coords;
- constructor(bboxes, coords) {
- this.#bboxes = bboxes;
- this.#coords = coords;
- }
- get length() {
- return this.#bboxes.length;
- }
- isEmpty(i) {
- return this.#bboxes[i] === EMPTY_BBOX;
- }
- minX(i) {
- return this.#coords[i * 4 + 0] / 256;
- }
- minY(i) {
- return this.#coords[i * 4 + 1] / 256;
- }
- maxX(i) {
- return (this.#coords[i * 4 + 2] + 1) / 256;
- }
- maxY(i) {
- return (this.#coords[i * 4 + 3] + 1) / 256;
- }
-}
-const ensureDebugMetadata = (map, key) => {
- if (!map) {
- return undefined;
- }
- let value = map.get(key);
- if (!value) {
- value = {
- dependencies: new Set(),
- isRenderingOperation: false
- };
- map.set(key, value);
- }
- return value;
-};
-class CanvasDependencyTracker {
- #simple = {
- __proto__: null
- };
- #incremental = {
- __proto__: null,
- transform: [],
- moveText: [],
- sameLineText: [],
- [FORCED_DEPENDENCY_LABEL]: []
- };
- #namedDependencies = new Map();
- #savesStack = [];
- #markedContentStack = [];
- #baseTransformStack = [[1, 0, 0, 1, 0, 0]];
- #clipBox = [-Infinity, -Infinity, Infinity, Infinity];
- #pendingBBox = new Float64Array([Infinity, Infinity, -Infinity, -Infinity]);
- #pendingBBoxIdx = -1;
- #pendingDependencies = new Set();
- #operations = new Map();
- #fontBBoxTrustworthy = new Map();
- #canvasWidth;
- #canvasHeight;
- #bboxesCoords;
- #bboxes;
- #debugMetadata;
- constructor(canvas, operationsCount, recordDebugMetadata = false) {
- this.#canvasWidth = canvas.width;
- this.#canvasHeight = canvas.height;
- this.#initializeBBoxes(operationsCount);
- if (recordDebugMetadata) {
- this.#debugMetadata = new Map();
- }
- }
- growOperationsCount(operationsCount) {
- if (operationsCount >= this.#bboxes.length) {
- this.#initializeBBoxes(operationsCount, this.#bboxes);
- }
- }
- #initializeBBoxes(operationsCount, oldBBoxes) {
- const buffer = new ArrayBuffer(operationsCount * 4);
- this.#bboxesCoords = new Uint8ClampedArray(buffer);
- this.#bboxes = new Uint32Array(buffer);
- if (oldBBoxes && oldBBoxes.length > 0) {
- this.#bboxes.set(oldBBoxes);
- this.#bboxes.fill(EMPTY_BBOX, oldBBoxes.length);
- } else {
- this.#bboxes.fill(EMPTY_BBOX);
- }
- }
- save(opIdx) {
- this.#simple = {
- __proto__: this.#simple
- };
- this.#incremental = {
- __proto__: this.#incremental,
- transform: {
- __proto__: this.#incremental.transform
- },
- moveText: {
- __proto__: this.#incremental.moveText
- },
- sameLineText: {
- __proto__: this.#incremental.sameLineText
- },
- [FORCED_DEPENDENCY_LABEL]: {
- __proto__: this.#incremental[FORCED_DEPENDENCY_LABEL]
- }
- };
- this.#clipBox = {
- __proto__: this.#clipBox
- };
- this.#savesStack.push(opIdx);
- return this;
- }
- restore(opIdx) {
- const previous = Object.getPrototypeOf(this.#simple);
- if (previous === null) {
- return this;
- }
- this.#simple = previous;
- this.#incremental = Object.getPrototypeOf(this.#incremental);
- this.#clipBox = Object.getPrototypeOf(this.#clipBox);
- const lastSave = this.#savesStack.pop();
- if (lastSave !== undefined) {
- ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave);
- this.#bboxes[opIdx] = this.#bboxes[lastSave];
- }
- return this;
- }
- recordOpenMarker(idx) {
- this.#savesStack.push(idx);
- return this;
- }
- getOpenMarker() {
- if (this.#savesStack.length === 0) {
- return null;
- }
- return this.#savesStack.at(-1);
- }
- recordCloseMarker(opIdx) {
- const lastSave = this.#savesStack.pop();
- if (lastSave !== undefined) {
- ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave);
- this.#bboxes[opIdx] = this.#bboxes[lastSave];
- }
- return this;
- }
- beginMarkedContent(opIdx) {
- this.#markedContentStack.push(opIdx);
- return this;
- }
- endMarkedContent(opIdx) {
- const lastSave = this.#markedContentStack.pop();
- if (lastSave !== undefined) {
- ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave);
- this.#bboxes[opIdx] = this.#bboxes[lastSave];
- }
- return this;
- }
- pushBaseTransform(ctx) {
- this.#baseTransformStack.push(Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform()));
- return this;
- }
- popBaseTransform() {
- if (this.#baseTransformStack.length > 1) {
- this.#baseTransformStack.pop();
- }
- return this;
- }
- recordSimpleData(name, idx) {
- this.#simple[name] = idx;
- return this;
- }
- recordIncrementalData(name, idx) {
- this.#incremental[name].push(idx);
- return this;
- }
- resetIncrementalData(name, idx) {
- this.#incremental[name].length = 0;
- return this;
- }
- recordNamedData(name, idx) {
- this.#namedDependencies.set(name, idx);
- return this;
- }
- recordSimpleDataFromNamed(name, depName, fallbackIdx) {
- this.#simple[name] = this.#namedDependencies.get(depName) ?? fallbackIdx;
- }
- recordFutureForcedDependency(name, idx) {
- this.recordIncrementalData(FORCED_DEPENDENCY_LABEL, idx);
- return this;
- }
- inheritSimpleDataAsFutureForcedDependencies(names) {
- for (const name of names) {
- if (name in this.#simple) {
- this.recordFutureForcedDependency(name, this.#simple[name]);
- }
- }
- return this;
+ clearData() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + systemFontInfoLength;
+ const cssFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + cssFontInfoLength;
+ const length = this.#view.getUint32(offset);
+ const data = new Uint8Array(this.#buffer, offset + 4, length);
+ data.fill(0);
+ this.#view.setUint32(offset, 0);
}
- inheritPendingDependenciesAsFutureForcedDependencies() {
- for (const dep of this.#pendingDependencies) {
- this.recordFutureForcedDependency(FORCED_DEPENDENCY_LABEL, dep);
+ get cssFontInfo() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + systemFontInfoLength;
+ const cssFontInfoLength = this.#view.getUint32(offset);
+ if (cssFontInfoLength === 0) {
+ return null;
}
- return this;
+ const cssFontInfoData = new Uint8Array(cssFontInfoLength);
+ cssFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength));
+ return new CssFontInfo(cssFontInfoData.buffer);
}
- resetBBox(idx) {
- if (this.#pendingBBoxIdx !== idx) {
- this.#pendingBBoxIdx = idx;
- this.#pendingBBox[0] = Infinity;
- this.#pendingBBox[1] = Infinity;
- this.#pendingBBox[2] = -Infinity;
- this.#pendingBBox[3] = -Infinity;
+ get systemFontInfo() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ if (systemFontInfoLength === 0) {
+ return null;
}
- return this;
+ const systemFontInfoData = new Uint8Array(systemFontInfoLength);
+ systemFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength));
+ return new SystemFontInfo(systemFontInfoData.buffer);
}
- recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
- const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform());
- const clipBox = [Infinity, Infinity, -Infinity, -Infinity];
- Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, clipBox);
- const intersection = Util.intersect(this.#clipBox, clipBox);
- if (intersection) {
- this.#clipBox[0] = intersection[0];
- this.#clipBox[1] = intersection[1];
- this.#clipBox[2] = intersection[2];
- this.#clipBox[3] = intersection[3];
- } else {
- this.#clipBox[0] = this.#clipBox[1] = Infinity;
- this.#clipBox[2] = this.#clipBox[3] = -Infinity;
+ static write(font) {
+ const systemFontInfoBuffer = font.systemFontInfo ? SystemFontInfo.write(font.systemFontInfo) : null;
+ const cssFontInfoBuffer = font.cssFontInfo ? CssFontInfo.write(font.cssFontInfo) : null;
+ const encoder = new TextEncoder();
+ const encodedStrings = {};
+ let stringsLength = 0;
+ for (const prop of FontInfo.strings) {
+ encodedStrings[prop] = encoder.encode(font[prop]);
+ stringsLength += 4 + encodedStrings[prop].length;
}
- return this;
- }
- recordBBox(idx, ctx, minX, maxX, minY, maxY) {
- const clipBox = this.#clipBox;
- if (clipBox[0] === Infinity) {
- return this;
+ const lengthEstimate = FontInfo.#OFFSET_STRINGS + 4 + stringsLength + 4 + (systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) + 4 + (cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) + 4 + (font.data ? font.data.length : 0);
+ const buffer = new ArrayBuffer(lengthEstimate);
+ const data = new Uint8Array(buffer);
+ const view = new DataView(buffer);
+ let offset = 0;
+ const numBools = FontInfo.bools.length;
+ let boolByte = 0,
+ boolBit = 0;
+ for (let i = 0; i < numBools; i++) {
+ const value = font[FontInfo.bools[i]];
+ const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01;
+ boolByte |= bits << boolBit;
+ boolBit += 2;
+ if (boolBit === 8 || i === numBools - 1) {
+ view.setUint8(offset++, boolByte);
+ boolByte = 0;
+ boolBit = 0;
+ }
}
- const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform());
- if (clipBox[0] === -Infinity) {
- Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, this.#pendingBBox);
- return this;
+ assert(offset === FontInfo.#OFFSET_NUMBERS, "FontInfo.write: Boolean properties offset mismatch");
+ for (const prop of FontInfo.numbers) {
+ view.setFloat64(offset, font[prop]);
+ offset += 8;
}
- const bbox = [Infinity, Infinity, -Infinity, -Infinity];
- Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, bbox);
- this.#pendingBBox[0] = Math.min(this.#pendingBBox[0], Math.max(bbox[0], clipBox[0]));
- this.#pendingBBox[1] = Math.min(this.#pendingBBox[1], Math.max(bbox[1], clipBox[1]));
- this.#pendingBBox[2] = Math.max(this.#pendingBBox[2], Math.min(bbox[2], clipBox[2]));
- this.#pendingBBox[3] = Math.max(this.#pendingBBox[3], Math.min(bbox[3], clipBox[3]));
- return this;
- }
- recordCharacterBBox(idx, ctx, font, scale = 1, x = 0, y = 0, getMeasure) {
- const fontBBox = font.bbox;
- let isBBoxTrustworthy;
- let computedBBox;
- if (fontBBox) {
- isBBoxTrustworthy = fontBBox[2] !== fontBBox[0] && fontBBox[3] !== fontBBox[1] && this.#fontBBoxTrustworthy.get(font);
- if (isBBoxTrustworthy !== false) {
- computedBBox = [0, 0, 0, 0];
- Util.axialAlignedBoundingBox(fontBBox, font.fontMatrix, computedBBox);
- if (scale !== 1 || x !== 0 || y !== 0) {
- Util.scaleMinMax([scale, 0, 0, -scale, x, y], computedBBox);
- }
- if (isBBoxTrustworthy) {
- return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]);
- }
+ assert(offset === FontInfo.#OFFSET_BBOX, "FontInfo.write: Number properties offset mismatch");
+ if (font.bbox) {
+ view.setUint8(offset++, 4);
+ for (const coord of font.bbox) {
+ view.setInt16(offset, coord, true);
+ offset += 2;
}
+ } else {
+ view.setUint8(offset++, 0);
+ offset += 2 * 4;
}
- if (!getMeasure) {
- return this.recordFullPageBBox(idx);
- }
- const measure = getMeasure();
- if (fontBBox && computedBBox && isBBoxTrustworthy === undefined) {
- isBBoxTrustworthy = computedBBox[0] <= x - measure.actualBoundingBoxLeft && computedBBox[2] >= x + measure.actualBoundingBoxRight && computedBBox[1] <= y - measure.actualBoundingBoxAscent && computedBBox[3] >= y + measure.actualBoundingBoxDescent;
- this.#fontBBoxTrustworthy.set(font, isBBoxTrustworthy);
- if (isBBoxTrustworthy) {
- return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]);
+ assert(offset === FontInfo.#OFFSET_FONT_MATRIX, "FontInfo.write: BBox properties offset mismatch");
+ if (font.fontMatrix) {
+ view.setUint8(offset++, 6);
+ for (const point of font.fontMatrix) {
+ view.setFloat64(offset, point, true);
+ offset += 8;
}
+ } else {
+ view.setUint8(offset++, 0);
+ offset += 8 * 6;
}
- return this.recordBBox(idx, ctx, x - measure.actualBoundingBoxLeft, x + measure.actualBoundingBoxRight, y - measure.actualBoundingBoxAscent, y + measure.actualBoundingBoxDescent);
- }
- recordFullPageBBox(idx) {
- this.#pendingBBox[0] = Math.max(0, this.#clipBox[0]);
- this.#pendingBBox[1] = Math.max(0, this.#clipBox[1]);
- this.#pendingBBox[2] = Math.min(this.#canvasWidth, this.#clipBox[2]);
- this.#pendingBBox[3] = Math.min(this.#canvasHeight, this.#clipBox[3]);
- return this;
- }
- getSimpleIndex(dependencyName) {
- return this.#simple[dependencyName];
- }
- recordDependencies(idx, dependencyNames) {
- const pendingDependencies = this.#pendingDependencies;
- const simple = this.#simple;
- const incremental = this.#incremental;
- for (const name of dependencyNames) {
- if (name in this.#simple) {
- pendingDependencies.add(simple[name]);
- } else if (name in incremental) {
- incremental[name].forEach(pendingDependencies.add, pendingDependencies);
+ assert(offset === FontInfo.#OFFSET_DEFAULT_VMETRICS, "FontInfo.write: FontMatrix properties offset mismatch");
+ if (font.defaultVMetrics) {
+ view.setUint8(offset++, 1);
+ for (const metric of font.defaultVMetrics) {
+ view.setInt16(offset, metric, true);
+ offset += 2;
}
+ } else {
+ view.setUint8(offset++, 0);
+ offset += 3 * 2;
}
- return this;
- }
- recordNamedDependency(idx, name) {
- if (this.#namedDependencies.has(name)) {
- this.#pendingDependencies.add(this.#namedDependencies.get(name));
+ assert(offset === FontInfo.#OFFSET_STRINGS, "FontInfo.write: DefaultVMetrics properties offset mismatch");
+ view.setUint32(FontInfo.#OFFSET_STRINGS, 0);
+ offset += 4;
+ for (const prop of FontInfo.strings) {
+ const encoded = encodedStrings[prop];
+ const length = encoded.length;
+ view.setUint32(offset, length);
+ data.set(encoded, offset + 4);
+ offset += 4 + length;
}
- return this;
- }
- recordOperation(idx, preserve = false) {
- this.recordDependencies(idx, [FORCED_DEPENDENCY_LABEL]);
- if (this.#debugMetadata) {
- const metadata = ensureDebugMetadata(this.#debugMetadata, idx);
- const {
- dependencies
- } = metadata;
- this.#pendingDependencies.forEach(dependencies.add, dependencies);
- this.#savesStack.forEach(dependencies.add, dependencies);
- this.#markedContentStack.forEach(dependencies.add, dependencies);
- dependencies.delete(idx);
- metadata.isRenderingOperation = true;
+ view.setUint32(FontInfo.#OFFSET_STRINGS, offset - FontInfo.#OFFSET_STRINGS - 4);
+ if (!systemFontInfoBuffer) {
+ view.setUint32(offset, 0);
+ offset += 4;
+ } else {
+ const length = systemFontInfoBuffer.byteLength;
+ view.setUint32(offset, length);
+ assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at systemFontInfo");
+ data.set(new Uint8Array(systemFontInfoBuffer), offset + 4);
+ offset += 4 + length;
}
- if (this.#pendingBBoxIdx === idx) {
- const minX = floor(this.#pendingBBox[0] * 256 / this.#canvasWidth);
- const minY = floor(this.#pendingBBox[1] * 256 / this.#canvasHeight);
- const maxX = ceil(this.#pendingBBox[2] * 256 / this.#canvasWidth);
- const maxY = ceil(this.#pendingBBox[3] * 256 / this.#canvasHeight);
- expandBBox(this.#bboxesCoords, idx, minX, minY, maxX, maxY);
- for (const depIdx of this.#pendingDependencies) {
- if (depIdx !== idx) {
- expandBBox(this.#bboxesCoords, depIdx, minX, minY, maxX, maxY);
- }
- }
- for (const saveIdx of this.#savesStack) {
- if (saveIdx !== idx) {
- expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY);
- }
- }
- for (const saveIdx of this.#markedContentStack) {
- if (saveIdx !== idx) {
- expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY);
- }
- }
- if (!preserve) {
- this.#pendingDependencies.clear();
- this.#pendingBBoxIdx = -1;
- }
+ if (!cssFontInfoBuffer) {
+ view.setUint32(offset, 0);
+ offset += 4;
+ } else {
+ const length = cssFontInfoBuffer.byteLength;
+ view.setUint32(offset, length);
+ assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at cssFontInfo");
+ data.set(new Uint8Array(cssFontInfoBuffer), offset + 4);
+ offset += 4 + length;
+ }
+ if (font.data === undefined) {
+ view.setUint32(offset, 0);
+ offset += 4;
+ } else {
+ view.setUint32(offset, font.data.length);
+ data.set(font.data, offset + 4);
+ offset += 4 + font.data.length;
}
- return this;
+ assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow");
+ return buffer.transferToFixedLength(offset);
}
- recordShowTextOperation(idx, preserve = false) {
- const deps = Array.from(this.#pendingDependencies);
- this.recordOperation(idx, preserve);
- this.recordIncrementalData("sameLineText", idx);
- for (const dep of deps) {
- this.recordIncrementalData("sameLineText", dep);
+}
+class PatternInfo {
+ static #KIND = 0;
+ static #HAS_BBOX = 1;
+ static #HAS_BACKGROUND = 2;
+ static #SHADING_TYPE = 3;
+ static #N_COORD = 4;
+ static #N_COLOR = 8;
+ static #N_STOP = 12;
+ static #N_FIGURES = 16;
+ constructor(buffer) {
+ this.buffer = buffer;
+ this.view = new DataView(buffer);
+ this.data = new Uint8Array(buffer);
+ }
+ static write(ir) {
+ let kind,
+ bbox = null,
+ coords = [],
+ colors = [],
+ colorStops = [],
+ figures = [],
+ shadingType = null,
+ background = null;
+ switch (ir[0]) {
+ case "RadialAxial":
+ kind = ir[1] === "axial" ? 1 : 2;
+ bbox = ir[2];
+ colorStops = ir[3];
+ if (kind === 1) {
+ coords.push(...ir[4], ...ir[5]);
+ } else {
+ coords.push(ir[4][0], ir[4][1], ir[6], ir[5][0], ir[5][1], ir[7]);
+ }
+ break;
+ case "Mesh":
+ kind = 3;
+ shadingType = ir[1];
+ coords = ir[2];
+ colors = ir[3];
+ figures = ir[4] || [];
+ bbox = ir[6];
+ background = ir[7];
+ break;
+ default:
+ throw new Error(`Unsupported pattern type: ${ir[0]}`);
+ }
+ const nCoord = Math.floor(coords.length / 2);
+ const nColor = Math.floor(colors.length / 3);
+ const nStop = colorStops.length;
+ const nFigures = figures.length;
+ let figuresSize = 0;
+ for (const figure of figures) {
+ figuresSize += 1;
+ figuresSize = Math.ceil(figuresSize / 4) * 4;
+ figuresSize += 4 + figure.coords.length * 4;
+ figuresSize += 4 + figure.colors.length * 4;
+ if (figure.verticesPerRow !== undefined) {
+ figuresSize += 4;
+ }
+ }
+ const byteLen = 20 + nCoord * 8 + nColor * 3 + nStop * 8 + (bbox ? 16 : 0) + (background ? 3 : 0) + figuresSize;
+ const buffer = new ArrayBuffer(byteLen);
+ const dataView = new DataView(buffer);
+ const u8data = new Uint8Array(buffer);
+ dataView.setUint8(PatternInfo.#KIND, kind);
+ dataView.setUint8(PatternInfo.#HAS_BBOX, bbox ? 1 : 0);
+ dataView.setUint8(PatternInfo.#HAS_BACKGROUND, background ? 1 : 0);
+ dataView.setUint8(PatternInfo.#SHADING_TYPE, shadingType);
+ dataView.setUint32(PatternInfo.#N_COORD, nCoord, true);
+ dataView.setUint32(PatternInfo.#N_COLOR, nColor, true);
+ dataView.setUint32(PatternInfo.#N_STOP, nStop, true);
+ dataView.setUint32(PatternInfo.#N_FIGURES, nFigures, true);
+ let offset = 20;
+ const coordsView = new Float32Array(buffer, offset, nCoord * 2);
+ coordsView.set(coords);
+ offset += nCoord * 8;
+ u8data.set(colors, offset);
+ offset += nColor * 3;
+ for (const [pos, hex] of colorStops) {
+ dataView.setFloat32(offset, pos, true);
+ offset += 4;
+ dataView.setUint32(offset, parseInt(hex.slice(1), 16), true);
+ offset += 4;
}
- return this;
- }
- bboxToClipBoxDropOperation(idx, preserve = false) {
- if (this.#pendingBBoxIdx === idx) {
- this.#pendingBBoxIdx = -1;
- this.#clipBox[0] = Math.max(this.#clipBox[0], this.#pendingBBox[0]);
- this.#clipBox[1] = Math.max(this.#clipBox[1], this.#pendingBBox[1]);
- this.#clipBox[2] = Math.min(this.#clipBox[2], this.#pendingBBox[2]);
- this.#clipBox[3] = Math.min(this.#clipBox[3], this.#pendingBBox[3]);
- if (!preserve) {
- this.#pendingDependencies.clear();
+ if (bbox) {
+ for (const v of bbox) {
+ dataView.setFloat32(offset, v, true);
+ offset += 4;
}
}
- return this;
- }
- _takePendingDependencies() {
- const pendingDependencies = this.#pendingDependencies;
- this.#pendingDependencies = new Set();
- return pendingDependencies;
- }
- _extractOperation(idx) {
- const operation = this.#operations.get(idx);
- this.#operations.delete(idx);
- return operation;
- }
- _pushPendingDependencies(dependencies) {
- for (const dep of dependencies) {
- this.#pendingDependencies.add(dep);
+ if (background) {
+ u8data.set(background, offset);
+ offset += 3;
}
- }
- take() {
- this.#fontBBoxTrustworthy.clear();
- return new BBoxReader(this.#bboxes, this.#bboxesCoords);
- }
- takeDebugMetadata() {
- return this.#debugMetadata;
- }
-}
-class CanvasNestedDependencyTracker {
- #dependencyTracker;
- #opIdx;
- #ignoreBBoxes;
- #nestingLevel = 0;
- #savesLevel = 0;
- constructor(dependencyTracker, opIdx, ignoreBBoxes) {
- if (dependencyTracker instanceof CanvasNestedDependencyTracker && dependencyTracker.#ignoreBBoxes === !!ignoreBBoxes) {
- return dependencyTracker;
+ for (let i = 0; i < figures.length; i++) {
+ const figure = figures[i];
+ dataView.setUint8(offset, figure.type);
+ offset += 1;
+ offset = Math.ceil(offset / 4) * 4;
+ dataView.setUint32(offset, figure.coords.length, true);
+ offset += 4;
+ const figureCoordsView = new Int32Array(buffer, offset, figure.coords.length);
+ figureCoordsView.set(figure.coords);
+ offset += figure.coords.length * 4;
+ dataView.setUint32(offset, figure.colors.length, true);
+ offset += 4;
+ const colorsView = new Int32Array(buffer, offset, figure.colors.length);
+ colorsView.set(figure.colors);
+ offset += figure.colors.length * 4;
+ if (figure.verticesPerRow !== undefined) {
+ dataView.setUint32(offset, figure.verticesPerRow, true);
+ offset += 4;
+ }
}
- this.#dependencyTracker = dependencyTracker;
- this.#opIdx = opIdx;
- this.#ignoreBBoxes = !!ignoreBBoxes;
- }
- growOperationsCount() {
- throw new Error("Unreachable");
- }
- save(opIdx) {
- this.#savesLevel++;
- this.#dependencyTracker.save(this.#opIdx);
- return this;
+ return buffer;
}
- restore(opIdx) {
- if (this.#savesLevel > 0) {
- this.#dependencyTracker.restore(this.#opIdx);
- this.#savesLevel--;
+ getIR() {
+ const dataView = this.view;
+ const kind = this.data[PatternInfo.#KIND];
+ const hasBBox = !!this.data[PatternInfo.#HAS_BBOX];
+ const hasBackground = !!this.data[PatternInfo.#HAS_BACKGROUND];
+ const nCoord = dataView.getUint32(PatternInfo.#N_COORD, true);
+ const nColor = dataView.getUint32(PatternInfo.#N_COLOR, true);
+ const nStop = dataView.getUint32(PatternInfo.#N_STOP, true);
+ const nFigures = dataView.getUint32(PatternInfo.#N_FIGURES, true);
+ let offset = 20;
+ const coords = new Float32Array(this.buffer, offset, nCoord * 2);
+ offset += nCoord * 8;
+ const colors = new Uint8Array(this.buffer, offset, nColor * 3);
+ offset += nColor * 3;
+ const stops = [];
+ for (let i = 0; i < nStop; ++i) {
+ const p = dataView.getFloat32(offset, true);
+ offset += 4;
+ const rgb = dataView.getUint32(offset, true);
+ offset += 4;
+ stops.push([p, `#${rgb.toString(16).padStart(6, "0")}`]);
+ }
+ let bbox = null;
+ if (hasBBox) {
+ bbox = [];
+ for (let i = 0; i < 4; ++i) {
+ bbox.push(dataView.getFloat32(offset, true));
+ offset += 4;
+ }
+ }
+ let background = null;
+ if (hasBackground) {
+ background = new Uint8Array(this.buffer, offset, 3);
+ offset += 3;
+ }
+ const figures = [];
+ for (let i = 0; i < nFigures; ++i) {
+ const type = dataView.getUint8(offset);
+ offset += 1;
+ offset = Math.ceil(offset / 4) * 4;
+ const coordsLength = dataView.getUint32(offset, true);
+ offset += 4;
+ const figureCoords = new Int32Array(this.buffer, offset, coordsLength);
+ offset += coordsLength * 4;
+ const colorsLength = dataView.getUint32(offset, true);
+ offset += 4;
+ const figureColors = new Int32Array(this.buffer, offset, colorsLength);
+ offset += colorsLength * 4;
+ const figure = {
+ type,
+ coords: figureCoords,
+ colors: figureColors
+ };
+ if (type === MeshFigureType.LATTICE) {
+ figure.verticesPerRow = dataView.getUint32(offset, true);
+ offset += 4;
+ }
+ figures.push(figure);
+ }
+ if (kind === 1) {
+ return ["RadialAxial", "axial", bbox, stops, Array.from(coords.slice(0, 2)), Array.from(coords.slice(2, 4)), null, null];
+ }
+ if (kind === 2) {
+ return ["RadialAxial", "radial", bbox, stops, [coords[0], coords[1]], [coords[3], coords[4]], coords[2], coords[5]];
+ }
+ if (kind === 3) {
+ const shadingType = this.data[PatternInfo.#SHADING_TYPE];
+ let bounds = null;
+ if (coords.length > 0) {
+ let minX = coords[0],
+ maxX = coords[0];
+ let minY = coords[1],
+ maxY = coords[1];
+ for (let i = 0; i < coords.length; i += 2) {
+ const x = coords[i],
+ y = coords[i + 1];
+ minX = minX > x ? x : minX;
+ minY = minY > y ? y : minY;
+ maxX = maxX < x ? x : maxX;
+ maxY = maxY < y ? y : maxY;
+ }
+ bounds = [minX, minY, maxX, maxY];
+ }
+ return ["Mesh", shadingType, coords, colors, figures, bounds, bbox, background];
}
- return this;
- }
- recordOpenMarker(idx) {
- this.#nestingLevel++;
- return this;
- }
- getOpenMarker() {
- return this.#nestingLevel > 0 ? this.#opIdx : this.#dependencyTracker.getOpenMarker();
+ throw new Error(`Unsupported pattern kind: ${kind}`);
}
- recordCloseMarker(idx) {
- this.#nestingLevel--;
- return this;
+}
+
+;// ./src/display/api_utils.js
+
+function getUrlProp(val) {
+ return null;
+}
+function getDataProp(val) {
+ if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
+ return val;
}
- beginMarkedContent(opIdx) {
- return this;
+ if (typeof val === "string") {
+ return stringToBytes(val);
}
- endMarkedContent(opIdx) {
- return this;
+ if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) {
+ return new Uint8Array(val);
}
- pushBaseTransform(ctx) {
- this.#dependencyTracker.pushBaseTransform(ctx);
- return this;
+ throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property.");
+}
+function getFactoryUrlProp(val) {
+ if (typeof val !== "string") {
+ return null;
}
- popBaseTransform() {
- this.#dependencyTracker.popBaseTransform();
- return this;
+ if (val.endsWith("/")) {
+ return val;
}
- recordSimpleData(name, idx) {
- this.#dependencyTracker.recordSimpleData(name, this.#opIdx);
- return this;
+ throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
+}
+const isRefProxy = v => typeof v === "object" && Number.isInteger(v?.num) && v.num >= 0 && Number.isInteger(v?.gen) && v.gen >= 0;
+const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
+const isValidExplicitDest = _isValidExplicitDest.bind(null, isRefProxy, isNameProxy);
+class LoopbackPort {
+ #listeners = new Map();
+ #deferred = Promise.resolve();
+ postMessage(obj, transfer) {
+ const event = {
+ data: structuredClone(obj, transfer ? {
+ transfer
+ } : null)
+ };
+ this.#deferred.then(() => {
+ for (const [listener] of this.#listeners) {
+ listener.call(this, event);
+ }
+ });
}
- recordIncrementalData(name, idx) {
- this.#dependencyTracker.recordIncrementalData(name, this.#opIdx);
- return this;
+ addEventListener(name, listener, options = null) {
+ let rmAbort = null;
+ if (options?.signal instanceof AbortSignal) {
+ const {
+ signal
+ } = options;
+ if (signal.aborted) {
+ warn("LoopbackPort - cannot use an `aborted` signal.");
+ return;
+ }
+ const onAbort = () => this.removeEventListener(name, listener);
+ rmAbort = () => signal.removeEventListener("abort", onAbort);
+ signal.addEventListener("abort", onAbort);
+ }
+ this.#listeners.set(listener, rmAbort);
}
- resetIncrementalData(name, idx) {
- this.#dependencyTracker.resetIncrementalData(name, this.#opIdx);
- return this;
+ removeEventListener(name, listener) {
+ const rmAbort = this.#listeners.get(listener);
+ rmAbort?.();
+ this.#listeners.delete(listener);
}
- recordNamedData(name, idx) {
- return this;
+ terminate() {
+ for (const [, rmAbort] of this.#listeners) {
+ rmAbort?.();
+ }
+ this.#listeners.clear();
}
- recordSimpleDataFromNamed(name, depName, fallbackIdx) {
- this.#dependencyTracker.recordSimpleDataFromNamed(name, depName, this.#opIdx);
- return this;
+}
+
+;// ./src/shared/message_handler.js
+
+const CallbackKind = {
+ DATA: 1,
+ ERROR: 2
+};
+const StreamKind = {
+ CANCEL: 1,
+ CANCEL_COMPLETE: 2,
+ CLOSE: 3,
+ ENQUEUE: 4,
+ ERROR: 5,
+ PULL: 6,
+ PULL_COMPLETE: 7,
+ START_COMPLETE: 8
+};
+function onFn() {}
+function wrapReason(ex) {
+ if (ex instanceof AbortException || ex instanceof InvalidPDFException || ex instanceof PasswordException || ex instanceof ResponseException || ex instanceof UnknownErrorException) {
+ return ex;
}
- recordFutureForcedDependency(name, idx) {
- this.#dependencyTracker.recordFutureForcedDependency(name, this.#opIdx);
- return this;
+ if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) {
+ unreachable('wrapReason: Expected "reason" to be a (possibly cloned) Error.');
}
- inheritSimpleDataAsFutureForcedDependencies(names) {
- this.#dependencyTracker.inheritSimpleDataAsFutureForcedDependencies(names);
- return this;
+ switch (ex.name) {
+ case "AbortException":
+ return new AbortException(ex.message);
+ case "InvalidPDFException":
+ return new InvalidPDFException(ex.message);
+ case "PasswordException":
+ return new PasswordException(ex.message, ex.code);
+ case "ResponseException":
+ return new ResponseException(ex.message, ex.status, ex.missing);
+ case "UnknownErrorException":
+ return new UnknownErrorException(ex.message, ex.details);
}
- inheritPendingDependenciesAsFutureForcedDependencies() {
- this.#dependencyTracker.inheritPendingDependenciesAsFutureForcedDependencies();
- return this;
+ return new UnknownErrorException(ex.message, ex.toString());
+}
+class MessageHandler {
+ #messageAC = new AbortController();
+ constructor(sourceName, targetName, comObj) {
+ this.sourceName = sourceName;
+ this.targetName = targetName;
+ this.comObj = comObj;
+ this.callbackId = 1;
+ this.streamId = 1;
+ this.streamSinks = Object.create(null);
+ this.streamControllers = Object.create(null);
+ this.callbackCapabilities = Object.create(null);
+ this.actionHandler = Object.create(null);
+ comObj.addEventListener("message", this.#onMessage.bind(this), {
+ signal: this.#messageAC.signal
+ });
}
- resetBBox(idx) {
- if (!this.#ignoreBBoxes) {
- this.#dependencyTracker.resetBBox(this.#opIdx);
+ #onMessage({
+ data
+ }) {
+ if (data.targetName !== this.sourceName) {
+ return;
}
- return this;
- }
- recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
- if (!this.#ignoreBBoxes) {
- this.#dependencyTracker.recordClipBox(this.#opIdx, ctx, minX, maxX, minY, maxY);
+ if (data.stream) {
+ this.#processStreamMessage(data);
+ return;
}
- return this;
- }
- recordBBox(idx, ctx, minX, maxX, minY, maxY) {
- if (!this.#ignoreBBoxes) {
- this.#dependencyTracker.recordBBox(this.#opIdx, ctx, minX, maxX, minY, maxY);
+ if (data.callback) {
+ const callbackId = data.callbackId;
+ const capability = this.callbackCapabilities[callbackId];
+ if (!capability) {
+ throw new Error(`Cannot resolve callback ${callbackId}`);
+ }
+ delete this.callbackCapabilities[callbackId];
+ if (data.callback === CallbackKind.DATA) {
+ capability.resolve(data.data);
+ } else if (data.callback === CallbackKind.ERROR) {
+ capability.reject(wrapReason(data.reason));
+ } else {
+ throw new Error("Unexpected callback case");
+ }
+ return;
}
- return this;
- }
- recordCharacterBBox(idx, ctx, font, scale, x, y, getMeasure) {
- if (!this.#ignoreBBoxes) {
- this.#dependencyTracker.recordCharacterBBox(this.#opIdx, ctx, font, scale, x, y, getMeasure);
+ const action = this.actionHandler[data.action];
+ if (!action) {
+ throw new Error(`Unknown action from worker: ${data.action}`);
}
- return this;
- }
- recordFullPageBBox(idx) {
- if (!this.#ignoreBBoxes) {
- this.#dependencyTracker.recordFullPageBBox(this.#opIdx);
+ if (data.callbackId) {
+ const sourceName = this.sourceName,
+ targetName = data.sourceName,
+ comObj = this.comObj;
+ Promise.try(action, data.data).then(function (result) {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ callback: CallbackKind.DATA,
+ callbackId: data.callbackId,
+ data: result
+ });
+ }, function (reason) {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ callback: CallbackKind.ERROR,
+ callbackId: data.callbackId,
+ reason: wrapReason(reason)
+ });
+ });
+ return;
}
- return this;
+ if (data.streamId) {
+ this.#createStreamSink(data);
+ return;
+ }
+ action(data.data);
}
- getSimpleIndex(dependencyName) {
- return this.#dependencyTracker.getSimpleIndex(dependencyName);
+ on(actionName, handler) {
+ const ah = this.actionHandler;
+ if (ah[actionName]) {
+ throw new Error(`There is already an actionName called "${actionName}"`);
+ }
+ ah[actionName] = handler;
}
- recordDependencies(idx, dependencyNames) {
- this.#dependencyTracker.recordDependencies(this.#opIdx, dependencyNames);
- return this;
+ send(actionName, data, transfers) {
+ this.comObj.postMessage({
+ sourceName: this.sourceName,
+ targetName: this.targetName,
+ action: actionName,
+ data
+ }, transfers);
}
- recordNamedDependency(idx, name) {
- this.#dependencyTracker.recordNamedDependency(this.#opIdx, name);
- return this;
+ sendWithPromise(actionName, data, transfers) {
+ const callbackId = this.callbackId++;
+ const capability = Promise.withResolvers();
+ this.callbackCapabilities[callbackId] = capability;
+ try {
+ this.comObj.postMessage({
+ sourceName: this.sourceName,
+ targetName: this.targetName,
+ action: actionName,
+ callbackId,
+ data
+ }, transfers);
+ } catch (ex) {
+ capability.reject(ex);
+ }
+ return capability.promise;
}
- recordOperation(idx) {
- this.#dependencyTracker.recordOperation(this.#opIdx, true);
- return this;
+ sendWithStream(actionName, data, queueingStrategy, transfers) {
+ const streamId = this.streamId++,
+ sourceName = this.sourceName,
+ targetName = this.targetName,
+ comObj = this.comObj;
+ return new ReadableStream({
+ start: controller => {
+ const startCapability = Promise.withResolvers();
+ this.streamControllers[streamId] = {
+ controller,
+ startCall: startCapability,
+ pullCall: null,
+ cancelCall: null,
+ isClosed: false
+ };
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ action: actionName,
+ streamId,
+ data,
+ desiredSize: controller.desiredSize
+ }, transfers);
+ return startCapability.promise;
+ },
+ pull: controller => {
+ const pullCapability = Promise.withResolvers();
+ this.streamControllers[streamId].pullCall = pullCapability;
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.PULL,
+ streamId,
+ desiredSize: controller.desiredSize
+ });
+ return pullCapability.promise;
+ },
+ cancel: reason => {
+ assert(reason instanceof Error, "cancel must have a valid reason");
+ const cancelCapability = Promise.withResolvers();
+ this.streamControllers[streamId].cancelCall = cancelCapability;
+ this.streamControllers[streamId].isClosed = true;
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.CANCEL,
+ streamId,
+ reason: wrapReason(reason)
+ });
+ return cancelCapability.promise;
+ }
+ }, queueingStrategy);
}
- recordShowTextOperation(idx) {
- this.#dependencyTracker.recordShowTextOperation(this.#opIdx, true);
- return this;
+ #createStreamSink(data) {
+ const streamId = data.streamId,
+ sourceName = this.sourceName,
+ targetName = data.sourceName,
+ comObj = this.comObj;
+ const self = this,
+ action = this.actionHandler[data.action];
+ const streamSink = {
+ enqueue(chunk, size = 1, transfers) {
+ if (this.isCancelled) {
+ return;
+ }
+ const lastDesiredSize = this.desiredSize;
+ this.desiredSize -= size;
+ if (lastDesiredSize > 0 && this.desiredSize <= 0) {
+ this.sinkCapability = Promise.withResolvers();
+ this.ready = this.sinkCapability.promise;
+ }
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.ENQUEUE,
+ streamId,
+ chunk
+ }, transfers);
+ },
+ close() {
+ if (this.isCancelled) {
+ return;
+ }
+ this.isCancelled = true;
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.CLOSE,
+ streamId
+ });
+ delete self.streamSinks[streamId];
+ },
+ error(reason) {
+ assert(reason instanceof Error, "error must have a valid reason");
+ if (this.isCancelled) {
+ return;
+ }
+ this.isCancelled = true;
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.ERROR,
+ streamId,
+ reason: wrapReason(reason)
+ });
+ },
+ sinkCapability: Promise.withResolvers(),
+ onPull: null,
+ onCancel: null,
+ isCancelled: false,
+ desiredSize: data.desiredSize,
+ ready: null
+ };
+ streamSink.sinkCapability.resolve();
+ streamSink.ready = streamSink.sinkCapability.promise;
+ this.streamSinks[streamId] = streamSink;
+ Promise.try(action, data.data, streamSink).then(function () {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.START_COMPLETE,
+ streamId,
+ success: true
+ });
+ }, function (reason) {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.START_COMPLETE,
+ streamId,
+ reason: wrapReason(reason)
+ });
+ });
}
- bboxToClipBoxDropOperation(idx) {
- if (!this.#ignoreBBoxes) {
- this.#dependencyTracker.bboxToClipBoxDropOperation(this.#opIdx, true);
+ #processStreamMessage(data) {
+ const streamId = data.streamId,
+ sourceName = this.sourceName,
+ targetName = data.sourceName,
+ comObj = this.comObj;
+ const streamController = this.streamControllers[streamId],
+ streamSink = this.streamSinks[streamId];
+ switch (data.stream) {
+ case StreamKind.START_COMPLETE:
+ if (data.success) {
+ streamController.startCall.resolve();
+ } else {
+ streamController.startCall.reject(wrapReason(data.reason));
+ }
+ break;
+ case StreamKind.PULL_COMPLETE:
+ if (data.success) {
+ streamController.pullCall.resolve();
+ } else {
+ streamController.pullCall.reject(wrapReason(data.reason));
+ }
+ break;
+ case StreamKind.PULL:
+ if (!streamSink) {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.PULL_COMPLETE,
+ streamId,
+ success: true
+ });
+ break;
+ }
+ if (streamSink.desiredSize <= 0 && data.desiredSize > 0) {
+ streamSink.sinkCapability.resolve();
+ }
+ streamSink.desiredSize = data.desiredSize;
+ Promise.try(streamSink.onPull || onFn).then(function () {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.PULL_COMPLETE,
+ streamId,
+ success: true
+ });
+ }, function (reason) {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.PULL_COMPLETE,
+ streamId,
+ reason: wrapReason(reason)
+ });
+ });
+ break;
+ case StreamKind.ENQUEUE:
+ assert(streamController, "enqueue should have stream controller");
+ if (streamController.isClosed) {
+ break;
+ }
+ streamController.controller.enqueue(data.chunk);
+ break;
+ case StreamKind.CLOSE:
+ assert(streamController, "close should have stream controller");
+ if (streamController.isClosed) {
+ break;
+ }
+ streamController.isClosed = true;
+ streamController.controller.close();
+ this.#deleteStreamController(streamController, streamId);
+ break;
+ case StreamKind.ERROR:
+ assert(streamController, "error should have stream controller");
+ streamController.controller.error(wrapReason(data.reason));
+ this.#deleteStreamController(streamController, streamId);
+ break;
+ case StreamKind.CANCEL_COMPLETE:
+ if (data.success) {
+ streamController.cancelCall.resolve();
+ } else {
+ streamController.cancelCall.reject(wrapReason(data.reason));
+ }
+ this.#deleteStreamController(streamController, streamId);
+ break;
+ case StreamKind.CANCEL:
+ if (!streamSink) {
+ break;
+ }
+ const dataReason = wrapReason(data.reason);
+ Promise.try(streamSink.onCancel || onFn, dataReason).then(function () {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.CANCEL_COMPLETE,
+ streamId,
+ success: true
+ });
+ }, function (reason) {
+ comObj.postMessage({
+ sourceName,
+ targetName,
+ stream: StreamKind.CANCEL_COMPLETE,
+ streamId,
+ reason: wrapReason(reason)
+ });
+ });
+ streamSink.sinkCapability.reject(dataReason);
+ streamSink.isCancelled = true;
+ delete this.streamSinks[streamId];
+ break;
+ default:
+ throw new Error("Unexpected stream case");
}
- return this;
}
- take() {
- throw new Error("Unreachable");
+ async #deleteStreamController(streamController, streamId) {
+ await Promise.allSettled([streamController.startCall?.promise, streamController.pullCall?.promise, streamController.cancelCall?.promise]);
+ delete this.streamControllers[streamId];
}
- takeDebugMetadata() {
- throw new Error("Unreachable");
+ destroy() {
+ this.#messageAC?.abort();
+ this.#messageAC = null;
}
}
-const Dependencies = {
- stroke: ["path", "transform", "filter", "strokeColor", "strokeAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "dash"],
- fill: ["path", "transform", "filter", "fillColor", "fillAlpha", "globalCompositeOperation", "SMask"],
- imageXObject: ["transform", "SMask", "filter", "fillAlpha", "strokeAlpha", "globalCompositeOperation"],
- rawFillPath: ["filter", "fillColor", "fillAlpha"],
- showText: ["transform", "leading", "charSpacing", "wordSpacing", "hScale", "textRise", "moveText", "textMatrix", "font", "fontObj", "filter", "fillColor", "textRenderingMode", "SMask", "fillAlpha", "strokeAlpha", "globalCompositeOperation", "sameLineText"],
- transform: ["transform"],
- transformAndFill: ["transform", "fillColor"]
-};
-
-;// ./src/display/pattern_helper.js
+;// ./src/display/canvas_dependency_tracker.js
-const PathType = {
- FILL: "Fill",
- STROKE: "Stroke",
- SHADING: "Shading"
-};
-function applyBoundingBox(ctx, bbox) {
- if (!bbox) {
- return;
- }
- const width = bbox[2] - bbox[0];
- const height = bbox[3] - bbox[1];
- const region = new Path2D();
- region.rect(bbox[0], bbox[1], width, height);
- ctx.clip(region);
+const FORCED_DEPENDENCY_LABEL = "__forcedDependency";
+const {
+ floor,
+ ceil
+} = Math;
+function expandBBox(array, index, minX, minY, maxX, maxY) {
+ array[index * 4 + 0] = Math.min(array[index * 4 + 0], minX);
+ array[index * 4 + 1] = Math.min(array[index * 4 + 1], minY);
+ array[index * 4 + 2] = Math.max(array[index * 4 + 2], maxX);
+ array[index * 4 + 3] = Math.max(array[index * 4 + 3], maxY);
}
-class BaseShadingPattern {
- isModifyingCurrentTransform() {
- return false;
- }
- getPattern() {
- unreachable("Abstract method `getPattern` called.");
+const EMPTY_BBOX = new Uint32Array(new Uint8Array([255, 255, 0, 0]).buffer)[0];
+class BBoxReader {
+ #bboxes;
+ #coords;
+ constructor(bboxes, coords) {
+ this.#bboxes = bboxes;
+ this.#coords = coords;
}
-}
-class RadialAxialShadingPattern extends BaseShadingPattern {
- constructor(IR) {
- super();
- this._type = IR[1];
- this._bbox = IR[2];
- this._colorStops = IR[3];
- this._p0 = IR[4];
- this._p1 = IR[5];
- this._r0 = IR[6];
- this._r1 = IR[7];
- this.matrix = null;
+ get length() {
+ return this.#bboxes.length;
}
- _createGradient(ctx) {
- let grad;
- if (this._type === "axial") {
- grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]);
- } else if (this._type === "radial") {
- grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1);
- }
- for (const colorStop of this._colorStops) {
- grad.addColorStop(colorStop[0], colorStop[1]);
- }
- return grad;
+ isEmpty(i) {
+ return this.#bboxes[i] === EMPTY_BBOX;
}
- getPattern(ctx, owner, inverse, pathType) {
- let pattern;
- if (pathType === PathType.STROKE || pathType === PathType.FILL) {
- const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0];
- const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;
- const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1;
- const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height);
- const tmpCtx = tmpCanvas.context;
- tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
- tmpCtx.beginPath();
- tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
- tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]);
- inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]);
- tmpCtx.transform(...owner.baseTransform);
- if (this.matrix) {
- tmpCtx.transform(...this.matrix);
- }
- applyBoundingBox(tmpCtx, this._bbox);
- tmpCtx.fillStyle = this._createGradient(tmpCtx);
- tmpCtx.fill();
- pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat");
- const domMatrix = new DOMMatrix(inverse);
- pattern.setTransform(domMatrix);
- } else {
- applyBoundingBox(ctx, this._bbox);
- pattern = this._createGradient(ctx);
- }
- return pattern;
+ minX(i) {
+ return this.#coords[i * 4 + 0] / 256;
}
-}
-function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
- const coords = context.coords,
- colors = context.colors;
- const bytes = data.data,
- rowSize = data.width * 4;
- let tmp;
- if (coords[p1 + 1] > coords[p2 + 1]) {
- tmp = p1;
- p1 = p2;
- p2 = tmp;
- tmp = c1;
- c1 = c2;
- c2 = tmp;
+ minY(i) {
+ return this.#coords[i * 4 + 1] / 256;
}
- if (coords[p2 + 1] > coords[p3 + 1]) {
- tmp = p2;
- p2 = p3;
- p3 = tmp;
- tmp = c2;
- c2 = c3;
- c3 = tmp;
+ maxX(i) {
+ return (this.#coords[i * 4 + 2] + 1) / 256;
}
- if (coords[p1 + 1] > coords[p2 + 1]) {
- tmp = p1;
- p1 = p2;
- p2 = tmp;
- tmp = c1;
- c1 = c2;
- c2 = tmp;
+ maxY(i) {
+ return (this.#coords[i * 4 + 3] + 1) / 256;
}
- const x1 = (coords[p1] + context.offsetX) * context.scaleX;
- const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
- const x2 = (coords[p2] + context.offsetX) * context.scaleX;
- const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
- const x3 = (coords[p3] + context.offsetX) * context.scaleX;
- const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
- if (y1 >= y3) {
- return;
+}
+const ensureDebugMetadata = (map, key) => {
+ if (!map) {
+ return undefined;
}
- const c1r = colors[c1],
- c1g = colors[c1 + 1],
- c1b = colors[c1 + 2];
- const c2r = colors[c2],
- c2g = colors[c2 + 1],
- c2b = colors[c2 + 2];
- const c3r = colors[c3],
- c3g = colors[c3 + 1],
- c3b = colors[c3 + 2];
- const minY = Math.round(y1),
- maxY = Math.round(y3);
- let xa, car, cag, cab;
- let xb, cbr, cbg, cbb;
- for (let y = minY; y <= maxY; y++) {
- if (y < y2) {
- const k = y < y1 ? 0 : (y1 - y) / (y1 - y2);
- xa = x1 - (x1 - x2) * k;
- car = c1r - (c1r - c2r) * k;
- cag = c1g - (c1g - c2g) * k;
- cab = c1b - (c1b - c2b) * k;
- } else {
- let k;
- if (y > y3) {
- k = 1;
- } else if (y2 === y3) {
- k = 0;
- } else {
- k = (y2 - y) / (y2 - y3);
- }
- xa = x2 - (x2 - x3) * k;
- car = c2r - (c2r - c3r) * k;
- cag = c2g - (c2g - c3g) * k;
- cab = c2b - (c2b - c3b) * k;
+ let value = map.get(key);
+ if (!value) {
+ value = {
+ dependencies: new Set(),
+ isRenderingOperation: false
+ };
+ map.set(key, value);
+ }
+ return value;
+};
+class CanvasDependencyTracker {
+ #simple = {
+ __proto__: null
+ };
+ #incremental = {
+ __proto__: null,
+ transform: [],
+ moveText: [],
+ sameLineText: [],
+ [FORCED_DEPENDENCY_LABEL]: []
+ };
+ #namedDependencies = new Map();
+ #savesStack = [];
+ #markedContentStack = [];
+ #baseTransformStack = [[1, 0, 0, 1, 0, 0]];
+ #clipBox = [-Infinity, -Infinity, Infinity, Infinity];
+ #pendingBBox = new Float64Array([Infinity, Infinity, -Infinity, -Infinity]);
+ #pendingBBoxIdx = -1;
+ #pendingDependencies = new Set();
+ #operations = new Map();
+ #fontBBoxTrustworthy = new Map();
+ #canvasWidth;
+ #canvasHeight;
+ #bboxesCoords;
+ #bboxes;
+ #debugMetadata;
+ constructor(canvas, operationsCount, recordDebugMetadata = false) {
+ this.#canvasWidth = canvas.width;
+ this.#canvasHeight = canvas.height;
+ this.#initializeBBoxes(operationsCount);
+ if (recordDebugMetadata) {
+ this.#debugMetadata = new Map();
}
- let k;
- if (y < y1) {
- k = 0;
- } else if (y > y3) {
- k = 1;
- } else {
- k = (y1 - y) / (y1 - y3);
+ }
+ growOperationsCount(operationsCount) {
+ if (operationsCount >= this.#bboxes.length) {
+ this.#initializeBBoxes(operationsCount, this.#bboxes);
}
- xb = x1 - (x1 - x3) * k;
- cbr = c1r - (c1r - c3r) * k;
- cbg = c1g - (c1g - c3g) * k;
- cbb = c1b - (c1b - c3b) * k;
- const x1_ = Math.round(Math.min(xa, xb));
- const x2_ = Math.round(Math.max(xa, xb));
- let j = rowSize * y + x1_ * 4;
- for (let x = x1_; x <= x2_; x++) {
- k = (xa - x) / (xa - xb);
- if (k < 0) {
- k = 0;
- } else if (k > 1) {
- k = 1;
- }
- bytes[j++] = car - (car - cbr) * k | 0;
- bytes[j++] = cag - (cag - cbg) * k | 0;
- bytes[j++] = cab - (cab - cbb) * k | 0;
- bytes[j++] = 255;
+ }
+ #initializeBBoxes(operationsCount, oldBBoxes) {
+ const buffer = new ArrayBuffer(operationsCount * 4);
+ this.#bboxesCoords = new Uint8ClampedArray(buffer);
+ this.#bboxes = new Uint32Array(buffer);
+ if (oldBBoxes && oldBBoxes.length > 0) {
+ this.#bboxes.set(oldBBoxes);
+ this.#bboxes.fill(EMPTY_BBOX, oldBBoxes.length);
+ } else {
+ this.#bboxes.fill(EMPTY_BBOX);
}
}
-}
-function drawFigure(data, figure, context) {
- const ps = figure.coords;
- const cs = figure.colors;
- let i, ii;
- switch (figure.type) {
- case MeshFigureType.LATTICE:
- const verticesPerRow = figure.verticesPerRow;
- const rows = Math.floor(ps.length / verticesPerRow) - 1;
- const cols = verticesPerRow - 1;
- for (i = 0; i < rows; i++) {
- let q = i * verticesPerRow;
- for (let j = 0; j < cols; j++, q++) {
- drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]);
- drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
- }
- }
- break;
- case MeshFigureType.TRIANGLES:
- for (i = 0, ii = ps.length; i < ii; i += 3) {
- drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]);
+ save(opIdx) {
+ this.#simple = {
+ __proto__: this.#simple
+ };
+ this.#incremental = {
+ __proto__: this.#incremental,
+ transform: {
+ __proto__: this.#incremental.transform
+ },
+ moveText: {
+ __proto__: this.#incremental.moveText
+ },
+ sameLineText: {
+ __proto__: this.#incremental.sameLineText
+ },
+ [FORCED_DEPENDENCY_LABEL]: {
+ __proto__: this.#incremental[FORCED_DEPENDENCY_LABEL]
}
- break;
- default:
- throw new Error("illegal figure");
+ };
+ this.#clipBox = {
+ __proto__: this.#clipBox
+ };
+ this.#savesStack.push(opIdx);
+ return this;
}
-}
-class MeshShadingPattern extends BaseShadingPattern {
- constructor(IR) {
- super();
- this._coords = IR[2];
- this._colors = IR[3];
- this._figures = IR[4];
- this._bounds = IR[5];
- this._bbox = IR[6];
- this._background = IR[7];
- this.matrix = null;
+ restore(opIdx) {
+ const previous = Object.getPrototypeOf(this.#simple);
+ if (previous === null) {
+ return this;
+ }
+ this.#simple = previous;
+ this.#incremental = Object.getPrototypeOf(this.#incremental);
+ this.#clipBox = Object.getPrototypeOf(this.#clipBox);
+ const lastSave = this.#savesStack.pop();
+ if (lastSave !== undefined) {
+ ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave);
+ this.#bboxes[opIdx] = this.#bboxes[lastSave];
+ }
+ return this;
}
- _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) {
- const EXPECTED_SCALE = 1.1;
- const MAX_PATTERN_SIZE = 3000;
- const BORDER_SIZE = 2;
- const offsetX = Math.floor(this._bounds[0]);
- const offsetY = Math.floor(this._bounds[1]);
- const boundsWidth = Math.ceil(this._bounds[2]) - offsetX;
- const boundsHeight = Math.ceil(this._bounds[3]) - offsetY;
- const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
- const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
- const scaleX = boundsWidth / width;
- const scaleY = boundsHeight / height;
- const context = {
- coords: this._coords,
- colors: this._colors,
- offsetX: -offsetX,
- offsetY: -offsetY,
- scaleX: 1 / scaleX,
- scaleY: 1 / scaleY
- };
- const paddedWidth = width + BORDER_SIZE * 2;
- const paddedHeight = height + BORDER_SIZE * 2;
- const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight);
- const tmpCtx = tmpCanvas.context;
- const data = tmpCtx.createImageData(width, height);
- if (backgroundColor) {
- const bytes = data.data;
- for (let i = 0, ii = bytes.length; i < ii; i += 4) {
- bytes[i] = backgroundColor[0];
- bytes[i + 1] = backgroundColor[1];
- bytes[i + 2] = backgroundColor[2];
- bytes[i + 3] = 255;
- }
+ recordOpenMarker(idx) {
+ this.#savesStack.push(idx);
+ return this;
+ }
+ getOpenMarker() {
+ if (this.#savesStack.length === 0) {
+ return null;
}
- for (const figure of this._figures) {
- drawFigure(data, figure, context);
+ return this.#savesStack.at(-1);
+ }
+ recordCloseMarker(opIdx) {
+ const lastSave = this.#savesStack.pop();
+ if (lastSave !== undefined) {
+ ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave);
+ this.#bboxes[opIdx] = this.#bboxes[lastSave];
}
- tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
- const canvas = tmpCanvas.canvas;
- return {
- canvas,
- offsetX: offsetX - BORDER_SIZE * scaleX,
- offsetY: offsetY - BORDER_SIZE * scaleY,
- scaleX,
- scaleY
- };
+ return this;
}
- isModifyingCurrentTransform() {
- return true;
+ beginMarkedContent(opIdx) {
+ this.#markedContentStack.push(opIdx);
+ return this;
}
- getPattern(ctx, owner, inverse, pathType) {
- applyBoundingBox(ctx, this._bbox);
- const scale = new Float32Array(2);
- if (pathType === PathType.SHADING) {
- Util.singularValueDecompose2dScale(getCurrentTransform(ctx), scale);
- } else if (this.matrix) {
- Util.singularValueDecompose2dScale(this.matrix, scale);
- const [matrixScaleX, matrixScaleY] = scale;
- Util.singularValueDecompose2dScale(owner.baseTransform, scale);
- scale[0] *= matrixScaleX;
- scale[1] *= matrixScaleY;
- } else {
- Util.singularValueDecompose2dScale(owner.baseTransform, scale);
+ endMarkedContent(opIdx) {
+ const lastSave = this.#markedContentStack.pop();
+ if (lastSave !== undefined) {
+ ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(lastSave);
+ this.#bboxes[opIdx] = this.#bboxes[lastSave];
}
- const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases);
- if (pathType !== PathType.SHADING) {
- ctx.setTransform(...owner.baseTransform);
- if (this.matrix) {
- ctx.transform(...this.matrix);
- }
+ return this;
+ }
+ pushBaseTransform(ctx) {
+ this.#baseTransformStack.push(Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform()));
+ return this;
+ }
+ popBaseTransform() {
+ if (this.#baseTransformStack.length > 1) {
+ this.#baseTransformStack.pop();
}
- ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
- ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY);
- return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat");
+ return this;
}
-}
-class DummyShadingPattern extends BaseShadingPattern {
- getPattern() {
- return "hotpink";
+ recordSimpleData(name, idx) {
+ this.#simple[name] = idx;
+ return this;
}
-}
-function getShadingPattern(IR) {
- switch (IR[0]) {
- case "RadialAxial":
- return new RadialAxialShadingPattern(IR);
- case "Mesh":
- return new MeshShadingPattern(IR);
- case "Dummy":
- return new DummyShadingPattern();
+ recordIncrementalData(name, idx) {
+ this.#incremental[name].push(idx);
+ return this;
}
- throw new Error(`Unknown IR type: ${IR[0]}`);
-}
-const PaintType = {
- COLORED: 1,
- UNCOLORED: 2
-};
-class TilingPattern {
- static MAX_PATTERN_SIZE = 3000;
- constructor(IR, ctx, canvasGraphicsFactory, baseTransform) {
- this.color = IR[1];
- this.operatorList = IR[2];
- this.matrix = IR[3];
- this.bbox = IR[4];
- this.xstep = IR[5];
- this.ystep = IR[6];
- this.paintType = IR[7];
- this.tilingType = IR[8];
- this.ctx = ctx;
- this.canvasGraphicsFactory = canvasGraphicsFactory;
- this.baseTransform = baseTransform;
+ resetIncrementalData(name, idx) {
+ this.#incremental[name].length = 0;
+ return this;
}
- createPatternCanvas(owner, opIdx) {
- const {
- bbox,
- operatorList,
- paintType,
- tilingType,
- color,
- canvasGraphicsFactory
- } = this;
- let {
- xstep,
- ystep
- } = this;
- xstep = Math.abs(xstep);
- ystep = Math.abs(ystep);
- info("TilingType: " + tilingType);
- const x0 = bbox[0],
- y0 = bbox[1],
- x1 = bbox[2],
- y1 = bbox[3];
- const width = x1 - x0;
- const height = y1 - y0;
- const scale = new Float32Array(2);
- Util.singularValueDecompose2dScale(this.matrix, scale);
- const [matrixScaleX, matrixScaleY] = scale;
- Util.singularValueDecompose2dScale(this.baseTransform, scale);
- const combinedScaleX = matrixScaleX * scale[0];
- const combinedScaleY = matrixScaleY * scale[1];
- let canvasWidth = width,
- canvasHeight = height,
- redrawHorizontally = false,
- redrawVertically = false;
- const xScaledStep = Math.ceil(xstep * combinedScaleX);
- const yScaledStep = Math.ceil(ystep * combinedScaleY);
- const xScaledWidth = Math.ceil(width * combinedScaleX);
- const yScaledHeight = Math.ceil(height * combinedScaleY);
- if (xScaledStep >= xScaledWidth) {
- canvasWidth = xstep;
- } else {
- redrawHorizontally = true;
- }
- if (yScaledStep >= yScaledHeight) {
- canvasHeight = ystep;
- } else {
- redrawVertically = true;
- }
- const dimx = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX);
- const dimy = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY);
- const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size);
- const tmpCtx = tmpCanvas.context;
- const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx, opIdx);
- graphics.groupLevel = owner.groupLevel;
- this.setFillAndStrokeStyleToContext(graphics, paintType, color);
- tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0);
- graphics.transform(0, dimx.scale, 0, 0, dimy.scale, 0, 0);
- tmpCtx.save();
- graphics.dependencyTracker?.save();
- this.clipBbox(graphics, x0, y0, x1, y1);
- graphics.baseTransform = getCurrentTransform(graphics.ctx);
- graphics.executeOperatorList(operatorList);
- graphics.endDrawing();
- graphics.dependencyTracker?.restore();
- tmpCtx.restore();
- if (redrawHorizontally || redrawVertically) {
- const image = tmpCanvas.canvas;
- if (redrawHorizontally) {
- canvasWidth = xstep;
- }
- if (redrawVertically) {
- canvasHeight = ystep;
- }
- const dimx2 = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX);
- const dimy2 = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY);
- const xSize = dimx2.size;
- const ySize = dimy2.size;
- const tmpCanvas2 = owner.cachedCanvases.getCanvas("pattern-workaround", xSize, ySize);
- const tmpCtx2 = tmpCanvas2.context;
- const ii = redrawHorizontally ? Math.floor(width / xstep) : 0;
- const jj = redrawVertically ? Math.floor(height / ystep) : 0;
- for (let i = 0; i <= ii; i++) {
- for (let j = 0; j <= jj; j++) {
- tmpCtx2.drawImage(image, xSize * i, ySize * j, xSize, ySize, 0, 0, xSize, ySize);
- }
+ recordNamedData(name, idx) {
+ this.#namedDependencies.set(name, idx);
+ return this;
+ }
+ recordSimpleDataFromNamed(name, depName, fallbackIdx) {
+ this.#simple[name] = this.#namedDependencies.get(depName) ?? fallbackIdx;
+ }
+ recordFutureForcedDependency(name, idx) {
+ this.recordIncrementalData(FORCED_DEPENDENCY_LABEL, idx);
+ return this;
+ }
+ inheritSimpleDataAsFutureForcedDependencies(names) {
+ for (const name of names) {
+ if (name in this.#simple) {
+ this.recordFutureForcedDependency(name, this.#simple[name]);
}
- return {
- canvas: tmpCanvas2.canvas,
- scaleX: dimx2.scale,
- scaleY: dimy2.scale,
- offsetX: x0,
- offsetY: y0
- };
}
- return {
- canvas: tmpCanvas.canvas,
- scaleX: dimx.scale,
- scaleY: dimy.scale,
- offsetX: x0,
- offsetY: y0
- };
+ return this;
}
- getSizeAndScale(step, realOutputSize, scale) {
- const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize);
- let size = Math.ceil(step * scale);
- if (size >= maxSize) {
- size = maxSize;
- } else {
- scale = size / step;
+ inheritPendingDependenciesAsFutureForcedDependencies() {
+ for (const dep of this.#pendingDependencies) {
+ this.recordFutureForcedDependency(FORCED_DEPENDENCY_LABEL, dep);
}
- return {
- scale,
- size
- };
+ return this;
}
- clipBbox(graphics, x0, y0, x1, y1) {
- const bboxWidth = x1 - x0;
- const bboxHeight = y1 - y0;
- graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
- Util.axialAlignedBoundingBox([x0, y0, x1, y1], getCurrentTransform(graphics.ctx), graphics.current.minMax);
- graphics.clip();
- graphics.endPath();
+ resetBBox(idx) {
+ if (this.#pendingBBoxIdx !== idx) {
+ this.#pendingBBoxIdx = idx;
+ this.#pendingBBox[0] = Infinity;
+ this.#pendingBBox[1] = Infinity;
+ this.#pendingBBox[2] = -Infinity;
+ this.#pendingBBox[3] = -Infinity;
+ }
+ return this;
}
- setFillAndStrokeStyleToContext(graphics, paintType, color) {
- const context = graphics.ctx,
- current = graphics.current;
- switch (paintType) {
- case PaintType.COLORED:
- const {
- fillStyle,
- strokeStyle
- } = this.ctx;
- context.fillStyle = current.fillColor = fillStyle;
- context.strokeStyle = current.strokeColor = strokeStyle;
- break;
- case PaintType.UNCOLORED:
- context.fillStyle = context.strokeStyle = color;
- current.fillColor = current.strokeColor = color;
- break;
- default:
- throw new FormatError(`Unsupported paint type: ${paintType}`);
+ recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
+ const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform());
+ const clipBox = [Infinity, Infinity, -Infinity, -Infinity];
+ Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, clipBox);
+ const intersection = Util.intersect(this.#clipBox, clipBox);
+ if (intersection) {
+ this.#clipBox[0] = intersection[0];
+ this.#clipBox[1] = intersection[1];
+ this.#clipBox[2] = intersection[2];
+ this.#clipBox[3] = intersection[3];
+ } else {
+ this.#clipBox[0] = this.#clipBox[1] = Infinity;
+ this.#clipBox[2] = this.#clipBox[3] = -Infinity;
}
+ return this;
}
- isModifyingCurrentTransform() {
- return false;
+ recordBBox(idx, ctx, minX, maxX, minY, maxY) {
+ const clipBox = this.#clipBox;
+ if (clipBox[0] === Infinity) {
+ return this;
+ }
+ const transform = Util.multiplyByDOMMatrix(this.#baseTransformStack.at(-1), ctx.getTransform());
+ if (clipBox[0] === -Infinity) {
+ Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, this.#pendingBBox);
+ return this;
+ }
+ const bbox = [Infinity, Infinity, -Infinity, -Infinity];
+ Util.axialAlignedBoundingBox([minX, minY, maxX, maxY], transform, bbox);
+ this.#pendingBBox[0] = Math.min(this.#pendingBBox[0], Math.max(bbox[0], clipBox[0]));
+ this.#pendingBBox[1] = Math.min(this.#pendingBBox[1], Math.max(bbox[1], clipBox[1]));
+ this.#pendingBBox[2] = Math.max(this.#pendingBBox[2], Math.min(bbox[2], clipBox[2]));
+ this.#pendingBBox[3] = Math.max(this.#pendingBBox[3], Math.min(bbox[3], clipBox[3]));
+ return this;
}
- getPattern(ctx, owner, inverse, pathType, opIdx) {
- let matrix = inverse;
- if (pathType !== PathType.SHADING) {
- matrix = Util.transform(matrix, owner.baseTransform);
- if (this.matrix) {
- matrix = Util.transform(matrix, this.matrix);
+ recordCharacterBBox(idx, ctx, font, scale = 1, x = 0, y = 0, getMeasure) {
+ const fontBBox = font.bbox;
+ let isBBoxTrustworthy;
+ let computedBBox;
+ if (fontBBox) {
+ isBBoxTrustworthy = fontBBox[2] !== fontBBox[0] && fontBBox[3] !== fontBBox[1] && this.#fontBBoxTrustworthy.get(font);
+ if (isBBoxTrustworthy !== false) {
+ computedBBox = [0, 0, 0, 0];
+ Util.axialAlignedBoundingBox(fontBBox, font.fontMatrix, computedBBox);
+ if (scale !== 1 || x !== 0 || y !== 0) {
+ Util.scaleMinMax([scale, 0, 0, -scale, x, y], computedBBox);
+ }
+ if (isBBoxTrustworthy) {
+ return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]);
+ }
}
}
- const temporaryPatternCanvas = this.createPatternCanvas(owner, opIdx);
- let domMatrix = new DOMMatrix(matrix);
- domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
- domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY);
- const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat");
- pattern.setTransform(domMatrix);
- return pattern;
- }
-}
-
-;// ./src/shared/image_utils.js
-
-function convertToRGBA(params) {
- switch (params.kind) {
- case ImageKind.GRAYSCALE_1BPP:
- return convertBlackAndWhiteToRGBA(params);
- case ImageKind.RGB_24BPP:
- return convertRGBToRGBA(params);
- }
- return null;
-}
-function convertBlackAndWhiteToRGBA({
- src,
- srcPos = 0,
- dest,
- width,
- height,
- nonBlackColor = 0xffffffff,
- inverseDecode = false
-}) {
- const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;
- const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor];
- const widthInSource = width >> 3;
- const widthRemainder = width & 7;
- const srcLength = src.length;
- dest = new Uint32Array(dest.buffer);
- let destPos = 0;
- for (let i = 0; i < height; i++) {
- for (const max = srcPos + widthInSource; srcPos < max; srcPos++) {
- const elem = srcPos < srcLength ? src[srcPos] : 255;
- dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping;
- dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping;
+ if (!getMeasure) {
+ return this.recordFullPageBBox(idx);
}
- if (widthRemainder === 0) {
- continue;
+ const measure = getMeasure();
+ if (fontBBox && computedBBox && isBBoxTrustworthy === undefined) {
+ isBBoxTrustworthy = computedBBox[0] <= x - measure.actualBoundingBoxLeft && computedBBox[2] >= x + measure.actualBoundingBoxRight && computedBBox[1] <= y - measure.actualBoundingBoxAscent && computedBBox[3] >= y + measure.actualBoundingBoxDescent;
+ this.#fontBBoxTrustworthy.set(font, isBBoxTrustworthy);
+ if (isBBoxTrustworthy) {
+ return this.recordBBox(idx, ctx, computedBBox[0], computedBBox[2], computedBBox[1], computedBBox[3]);
+ }
}
- const elem = srcPos < srcLength ? src[srcPos++] : 255;
- for (let j = 0; j < widthRemainder; j++) {
- dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping;
+ return this.recordBBox(idx, ctx, x - measure.actualBoundingBoxLeft, x + measure.actualBoundingBoxRight, y - measure.actualBoundingBoxAscent, y + measure.actualBoundingBoxDescent);
+ }
+ recordFullPageBBox(idx) {
+ this.#pendingBBox[0] = Math.max(0, this.#clipBox[0]);
+ this.#pendingBBox[1] = Math.max(0, this.#clipBox[1]);
+ this.#pendingBBox[2] = Math.min(this.#canvasWidth, this.#clipBox[2]);
+ this.#pendingBBox[3] = Math.min(this.#canvasHeight, this.#clipBox[3]);
+ return this;
+ }
+ getSimpleIndex(dependencyName) {
+ return this.#simple[dependencyName];
+ }
+ recordDependencies(idx, dependencyNames) {
+ const pendingDependencies = this.#pendingDependencies;
+ const simple = this.#simple;
+ const incremental = this.#incremental;
+ for (const name of dependencyNames) {
+ if (name in this.#simple) {
+ pendingDependencies.add(simple[name]);
+ } else if (name in incremental) {
+ incremental[name].forEach(pendingDependencies.add, pendingDependencies);
+ }
}
+ return this;
}
- return {
- srcPos,
- destPos
- };
-}
-function convertRGBToRGBA({
- src,
- srcPos = 0,
- dest,
- destPos = 0,
- width,
- height
-}) {
- let i = 0;
- const len = width * height * 3;
- const len32 = len >> 2;
- const src32 = new Uint32Array(src.buffer, srcPos, len32);
- if (FeatureTest.isLittleEndian) {
- for (; i < len32 - 2; i += 3, destPos += 4) {
- const s1 = src32[i];
- const s2 = src32[i + 1];
- const s3 = src32[i + 2];
- dest[destPos] = s1 | 0xff000000;
- dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000;
- dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000;
- dest[destPos + 3] = s3 >>> 8 | 0xff000000;
+ recordNamedDependency(idx, name) {
+ if (this.#namedDependencies.has(name)) {
+ this.#pendingDependencies.add(this.#namedDependencies.get(name));
}
- for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
- dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000;
+ return this;
+ }
+ recordOperation(idx, preserve = false) {
+ this.recordDependencies(idx, [FORCED_DEPENDENCY_LABEL]);
+ if (this.#debugMetadata) {
+ const metadata = ensureDebugMetadata(this.#debugMetadata, idx);
+ const {
+ dependencies
+ } = metadata;
+ this.#pendingDependencies.forEach(dependencies.add, dependencies);
+ this.#savesStack.forEach(dependencies.add, dependencies);
+ this.#markedContentStack.forEach(dependencies.add, dependencies);
+ dependencies.delete(idx);
+ metadata.isRenderingOperation = true;
}
- } else {
- for (; i < len32 - 2; i += 3, destPos += 4) {
- const s1 = src32[i];
- const s2 = src32[i + 1];
- const s3 = src32[i + 2];
- dest[destPos] = s1 | 0xff;
- dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff;
- dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff;
- dest[destPos + 3] = s3 << 8 | 0xff;
+ if (this.#pendingBBoxIdx === idx) {
+ const minX = floor(this.#pendingBBox[0] * 256 / this.#canvasWidth);
+ const minY = floor(this.#pendingBBox[1] * 256 / this.#canvasHeight);
+ const maxX = ceil(this.#pendingBBox[2] * 256 / this.#canvasWidth);
+ const maxY = ceil(this.#pendingBBox[3] * 256 / this.#canvasHeight);
+ expandBBox(this.#bboxesCoords, idx, minX, minY, maxX, maxY);
+ for (const depIdx of this.#pendingDependencies) {
+ if (depIdx !== idx) {
+ expandBBox(this.#bboxesCoords, depIdx, minX, minY, maxX, maxY);
+ }
+ }
+ for (const saveIdx of this.#savesStack) {
+ if (saveIdx !== idx) {
+ expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY);
+ }
+ }
+ for (const saveIdx of this.#markedContentStack) {
+ if (saveIdx !== idx) {
+ expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY);
+ }
+ }
+ if (!preserve) {
+ this.#pendingDependencies.clear();
+ this.#pendingBBoxIdx = -1;
+ }
}
- for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
- dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff;
+ return this;
+ }
+ recordShowTextOperation(idx, preserve = false) {
+ const deps = Array.from(this.#pendingDependencies);
+ this.recordOperation(idx, preserve);
+ this.recordIncrementalData("sameLineText", idx);
+ for (const dep of deps) {
+ this.recordIncrementalData("sameLineText", dep);
}
+ return this;
}
- return {
- srcPos: srcPos + len,
- destPos
- };
-}
-function grayToRGBA(src, dest) {
- if (FeatureTest.isLittleEndian) {
- for (let i = 0, ii = src.length; i < ii; i++) {
- dest[i] = src[i] * 0x10101 | 0xff000000;
+ bboxToClipBoxDropOperation(idx, preserve = false) {
+ if (this.#pendingBBoxIdx === idx) {
+ this.#pendingBBoxIdx = -1;
+ this.#clipBox[0] = Math.max(this.#clipBox[0], this.#pendingBBox[0]);
+ this.#clipBox[1] = Math.max(this.#clipBox[1], this.#pendingBBox[1]);
+ this.#clipBox[2] = Math.min(this.#clipBox[2], this.#pendingBBox[2]);
+ this.#clipBox[3] = Math.min(this.#clipBox[3], this.#pendingBBox[3]);
+ if (!preserve) {
+ this.#pendingDependencies.clear();
+ }
}
- } else {
- for (let i = 0, ii = src.length; i < ii; i++) {
- dest[i] = src[i] * 0x1010100 | 0x000000ff;
+ return this;
+ }
+ _takePendingDependencies() {
+ const pendingDependencies = this.#pendingDependencies;
+ this.#pendingDependencies = new Set();
+ return pendingDependencies;
+ }
+ _extractOperation(idx) {
+ const operation = this.#operations.get(idx);
+ this.#operations.delete(idx);
+ return operation;
+ }
+ _pushPendingDependencies(dependencies) {
+ for (const dep of dependencies) {
+ this.#pendingDependencies.add(dep);
}
}
-}
-
-;// ./src/display/canvas.js
-
-
-
-
-
-const MIN_FONT_SIZE = 16;
-const MAX_FONT_SIZE = 100;
-const EXECUTION_TIME = 15;
-const EXECUTION_STEPS = 10;
-const FULL_CHUNK_HEIGHT = 16;
-const SCALE_MATRIX = new DOMMatrix();
-const XY = new Float32Array(2);
-const MIN_MAX_INIT = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]);
-function mirrorContextOperations(ctx, destCtx) {
- if (ctx._removeMirroring) {
- throw new Error("Context is already forwarding operations.");
+ take() {
+ this.#fontBBoxTrustworthy.clear();
+ return new BBoxReader(this.#bboxes, this.#bboxesCoords);
}
- ctx.__originalSave = ctx.save;
- ctx.__originalRestore = ctx.restore;
- ctx.__originalRotate = ctx.rotate;
- ctx.__originalScale = ctx.scale;
- ctx.__originalTranslate = ctx.translate;
- ctx.__originalTransform = ctx.transform;
- ctx.__originalSetTransform = ctx.setTransform;
- ctx.__originalResetTransform = ctx.resetTransform;
- ctx.__originalClip = ctx.clip;
- ctx.__originalMoveTo = ctx.moveTo;
- ctx.__originalLineTo = ctx.lineTo;
- ctx.__originalBezierCurveTo = ctx.bezierCurveTo;
- ctx.__originalRect = ctx.rect;
- ctx.__originalClosePath = ctx.closePath;
- ctx.__originalBeginPath = ctx.beginPath;
- ctx._removeMirroring = () => {
- ctx.save = ctx.__originalSave;
- ctx.restore = ctx.__originalRestore;
- ctx.rotate = ctx.__originalRotate;
- ctx.scale = ctx.__originalScale;
- ctx.translate = ctx.__originalTranslate;
- ctx.transform = ctx.__originalTransform;
- ctx.setTransform = ctx.__originalSetTransform;
- ctx.resetTransform = ctx.__originalResetTransform;
- ctx.clip = ctx.__originalClip;
- ctx.moveTo = ctx.__originalMoveTo;
- ctx.lineTo = ctx.__originalLineTo;
- ctx.bezierCurveTo = ctx.__originalBezierCurveTo;
- ctx.rect = ctx.__originalRect;
- ctx.closePath = ctx.__originalClosePath;
- ctx.beginPath = ctx.__originalBeginPath;
- delete ctx._removeMirroring;
- };
- ctx.save = function () {
- destCtx.save();
- this.__originalSave();
- };
- ctx.restore = function () {
- destCtx.restore();
- this.__originalRestore();
- };
- ctx.translate = function (x, y) {
- destCtx.translate(x, y);
- this.__originalTranslate(x, y);
- };
- ctx.scale = function (x, y) {
- destCtx.scale(x, y);
- this.__originalScale(x, y);
- };
- ctx.transform = function (a, b, c, d, e, f) {
- destCtx.transform(a, b, c, d, e, f);
- this.__originalTransform(a, b, c, d, e, f);
- };
- ctx.setTransform = function (a, b, c, d, e, f) {
- destCtx.setTransform(a, b, c, d, e, f);
- this.__originalSetTransform(a, b, c, d, e, f);
- };
- ctx.resetTransform = function () {
- destCtx.resetTransform();
- this.__originalResetTransform();
- };
- ctx.rotate = function (angle) {
- destCtx.rotate(angle);
- this.__originalRotate(angle);
- };
- ctx.clip = function (rule) {
- destCtx.clip(rule);
- this.__originalClip(rule);
- };
- ctx.moveTo = function (x, y) {
- destCtx.moveTo(x, y);
- this.__originalMoveTo(x, y);
- };
- ctx.lineTo = function (x, y) {
- destCtx.lineTo(x, y);
- this.__originalLineTo(x, y);
- };
- ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
- destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
- this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
- };
- ctx.rect = function (x, y, width, height) {
- destCtx.rect(x, y, width, height);
- this.__originalRect(x, y, width, height);
- };
- ctx.closePath = function () {
- destCtx.closePath();
- this.__originalClosePath();
- };
- ctx.beginPath = function () {
- destCtx.beginPath();
- this.__originalBeginPath();
- };
-}
-class CachedCanvases {
- constructor(canvasFactory) {
- this.canvasFactory = canvasFactory;
- this.cache = Object.create(null);
+ takeDebugMetadata() {
+ return this.#debugMetadata;
}
- getCanvas(id, width, height) {
- let canvasEntry;
- if (this.cache[id] !== undefined) {
- canvasEntry = this.cache[id];
- this.canvasFactory.reset(canvasEntry, width, height);
- } else {
- canvasEntry = this.canvasFactory.create(width, height);
- this.cache[id] = canvasEntry;
+}
+class CanvasNestedDependencyTracker {
+ #dependencyTracker;
+ #opIdx;
+ #ignoreBBoxes;
+ #nestingLevel = 0;
+ #savesLevel = 0;
+ constructor(dependencyTracker, opIdx, ignoreBBoxes) {
+ if (dependencyTracker instanceof CanvasNestedDependencyTracker && dependencyTracker.#ignoreBBoxes === !!ignoreBBoxes) {
+ return dependencyTracker;
}
- return canvasEntry;
+ this.#dependencyTracker = dependencyTracker;
+ this.#opIdx = opIdx;
+ this.#ignoreBBoxes = !!ignoreBBoxes;
}
- delete(id) {
- delete this.cache[id];
+ growOperationsCount() {
+ throw new Error("Unreachable");
}
- clear() {
- for (const id in this.cache) {
- const canvasEntry = this.cache[id];
- this.canvasFactory.destroy(canvasEntry);
- delete this.cache[id];
+ save(opIdx) {
+ this.#savesLevel++;
+ this.#dependencyTracker.save(this.#opIdx);
+ return this;
+ }
+ restore(opIdx) {
+ if (this.#savesLevel > 0) {
+ this.#dependencyTracker.restore(this.#opIdx);
+ this.#savesLevel--;
}
+ return this;
}
-}
-function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) {
- const [a, b, c, d, tx, ty] = getCurrentTransform(ctx);
- if (b === 0 && c === 0) {
- const tlX = destX * a + tx;
- const rTlX = Math.round(tlX);
- const tlY = destY * d + ty;
- const rTlY = Math.round(tlY);
- const brX = (destX + destW) * a + tx;
- const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
- const brY = (destY + destH) * d + ty;
- const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
- ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY);
- ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight);
- ctx.setTransform(a, b, c, d, tx, ty);
- return [rWidth, rHeight];
+ recordOpenMarker(idx) {
+ this.#nestingLevel++;
+ return this;
}
- if (a === 0 && d === 0) {
- const tlX = destY * c + tx;
- const rTlX = Math.round(tlX);
- const tlY = destX * b + ty;
- const rTlY = Math.round(tlY);
- const brX = (destY + destH) * c + tx;
- const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
- const brY = (destX + destW) * b + ty;
- const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
- ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY);
- ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth);
- ctx.setTransform(a, b, c, d, tx, ty);
- return [rHeight, rWidth];
+ getOpenMarker() {
+ return this.#nestingLevel > 0 ? this.#opIdx : this.#dependencyTracker.getOpenMarker();
}
- ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
- const scaleX = Math.hypot(a, b);
- const scaleY = Math.hypot(c, d);
- return [scaleX * destW, scaleY * destH];
-}
-class CanvasExtraState {
- alphaIsShape = false;
- fontSize = 0;
- fontSizeScale = 1;
- textMatrix = null;
- textMatrixScale = 1;
- fontMatrix = FONT_IDENTITY_MATRIX;
- leading = 0;
- x = 0;
- y = 0;
- lineX = 0;
- lineY = 0;
- charSpacing = 0;
- wordSpacing = 0;
- textHScale = 1;
- textRenderingMode = TextRenderingMode.FILL;
- textRise = 0;
- fillColor = "#000000";
- strokeColor = "#000000";
- patternFill = false;
- patternStroke = false;
- fillAlpha = 1;
- strokeAlpha = 1;
- lineWidth = 1;
- activeSMask = null;
- transferMaps = "none";
- constructor(width, height, preInit) {
- preInit?.(this);
- this.clipBox = new Float32Array([0, 0, width, height]);
- this.minMax = MIN_MAX_INIT.slice();
+ recordCloseMarker(idx) {
+ this.#nestingLevel--;
+ return this;
+ }
+ beginMarkedContent(opIdx) {
+ return this;
+ }
+ endMarkedContent(opIdx) {
+ return this;
+ }
+ pushBaseTransform(ctx) {
+ this.#dependencyTracker.pushBaseTransform(ctx);
+ return this;
+ }
+ popBaseTransform() {
+ this.#dependencyTracker.popBaseTransform();
+ return this;
+ }
+ recordSimpleData(name, idx) {
+ this.#dependencyTracker.recordSimpleData(name, this.#opIdx);
+ return this;
+ }
+ recordIncrementalData(name, idx) {
+ this.#dependencyTracker.recordIncrementalData(name, this.#opIdx);
+ return this;
}
- clone() {
- const clone = Object.create(this);
- clone.clipBox = this.clipBox.slice();
- clone.minMax = this.minMax.slice();
- return clone;
+ resetIncrementalData(name, idx) {
+ this.#dependencyTracker.resetIncrementalData(name, this.#opIdx);
+ return this;
}
- getPathBoundingBox(pathType = PathType.FILL, transform = null) {
- const box = this.minMax.slice();
- if (pathType === PathType.STROKE) {
- if (!transform) {
- unreachable("Stroke bounding box must include transform.");
- }
- Util.singularValueDecompose2dScale(transform, XY);
- const xStrokePad = XY[0] * this.lineWidth / 2;
- const yStrokePad = XY[1] * this.lineWidth / 2;
- box[0] -= xStrokePad;
- box[1] -= yStrokePad;
- box[2] += xStrokePad;
- box[3] += yStrokePad;
- }
- return box;
+ recordNamedData(name, idx) {
+ return this;
}
- updateClipFromPath() {
- const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox());
- this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]);
+ recordSimpleDataFromNamed(name, depName, fallbackIdx) {
+ this.#dependencyTracker.recordSimpleDataFromNamed(name, depName, this.#opIdx);
+ return this;
}
- isEmptyClip() {
- return this.minMax[0] === Infinity;
+ recordFutureForcedDependency(name, idx) {
+ this.#dependencyTracker.recordFutureForcedDependency(name, this.#opIdx);
+ return this;
}
- startNewPathAndClipBox(box) {
- this.clipBox.set(box, 0);
- this.minMax.set(MIN_MAX_INIT, 0);
+ inheritSimpleDataAsFutureForcedDependencies(names) {
+ this.#dependencyTracker.inheritSimpleDataAsFutureForcedDependencies(names);
+ return this;
}
- getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
- return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform));
+ inheritPendingDependenciesAsFutureForcedDependencies() {
+ this.#dependencyTracker.inheritPendingDependenciesAsFutureForcedDependencies();
+ return this;
}
-}
-function putBinaryImageData(ctx, imgData) {
- if (imgData instanceof ImageData) {
- ctx.putImageData(imgData, 0, 0);
- return;
+ resetBBox(idx) {
+ if (!this.#ignoreBBoxes) {
+ this.#dependencyTracker.resetBBox(this.#opIdx);
+ }
+ return this;
}
- const height = imgData.height,
- width = imgData.width;
- const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
- const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
- const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
- const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
- let srcPos = 0,
- destPos;
- const src = imgData.data;
- const dest = chunkImgData.data;
- let i, j, thisChunkHeight, elemsInThisChunk;
- if (imgData.kind === util_ImageKind.GRAYSCALE_1BPP) {
- const srcLength = src.byteLength;
- const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);
- const dest32DataLength = dest32.length;
- const fullSrcDiff = width + 7 >> 3;
- const white = 0xffffffff;
- const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;
- for (i = 0; i < totalChunks; i++) {
- thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
- destPos = 0;
- for (j = 0; j < thisChunkHeight; j++) {
- const srcDiff = srcLength - srcPos;
- let k = 0;
- const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7;
- const kEndUnrolled = kEnd & ~7;
- let mask = 0;
- let srcByte = 0;
- for (; k < kEndUnrolled; k += 8) {
- srcByte = src[srcPos++];
- dest32[destPos++] = srcByte & 128 ? white : black;
- dest32[destPos++] = srcByte & 64 ? white : black;
- dest32[destPos++] = srcByte & 32 ? white : black;
- dest32[destPos++] = srcByte & 16 ? white : black;
- dest32[destPos++] = srcByte & 8 ? white : black;
- dest32[destPos++] = srcByte & 4 ? white : black;
- dest32[destPos++] = srcByte & 2 ? white : black;
- dest32[destPos++] = srcByte & 1 ? white : black;
- }
- for (; k < kEnd; k++) {
- if (mask === 0) {
- srcByte = src[srcPos++];
- mask = 128;
- }
- dest32[destPos++] = srcByte & mask ? white : black;
- mask >>= 1;
- }
- }
- while (destPos < dest32DataLength) {
- dest32[destPos++] = 0;
- }
- ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
+ recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
+ if (!this.#ignoreBBoxes) {
+ this.#dependencyTracker.recordClipBox(this.#opIdx, ctx, minX, maxX, minY, maxY);
}
- } else if (imgData.kind === util_ImageKind.RGBA_32BPP) {
- j = 0;
- elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
- for (i = 0; i < fullChunks; i++) {
- dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
- srcPos += elemsInThisChunk;
- ctx.putImageData(chunkImgData, 0, j);
- j += FULL_CHUNK_HEIGHT;
+ return this;
+ }
+ recordBBox(idx, ctx, minX, maxX, minY, maxY) {
+ if (!this.#ignoreBBoxes) {
+ this.#dependencyTracker.recordBBox(this.#opIdx, ctx, minX, maxX, minY, maxY);
}
- if (i < totalChunks) {
- elemsInThisChunk = width * partialChunkHeight * 4;
- dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
- ctx.putImageData(chunkImgData, 0, j);
+ return this;
+ }
+ recordCharacterBBox(idx, ctx, font, scale, x, y, getMeasure) {
+ if (!this.#ignoreBBoxes) {
+ this.#dependencyTracker.recordCharacterBBox(this.#opIdx, ctx, font, scale, x, y, getMeasure);
}
- } else if (imgData.kind === util_ImageKind.RGB_24BPP) {
- thisChunkHeight = FULL_CHUNK_HEIGHT;
- elemsInThisChunk = width * thisChunkHeight;
- for (i = 0; i < totalChunks; i++) {
- if (i >= fullChunks) {
- thisChunkHeight = partialChunkHeight;
- elemsInThisChunk = width * thisChunkHeight;
- }
- destPos = 0;
- for (j = elemsInThisChunk; j--;) {
- dest[destPos++] = src[srcPos++];
- dest[destPos++] = src[srcPos++];
- dest[destPos++] = src[srcPos++];
- dest[destPos++] = 255;
- }
- ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
+ return this;
+ }
+ recordFullPageBBox(idx) {
+ if (!this.#ignoreBBoxes) {
+ this.#dependencyTracker.recordFullPageBBox(this.#opIdx);
}
- } else {
- throw new Error(`bad image kind: ${imgData.kind}`);
+ return this;
}
-}
-function putBinaryImageMask(ctx, imgData) {
- if (imgData.bitmap) {
- ctx.drawImage(imgData.bitmap, 0, 0);
- return;
+ getSimpleIndex(dependencyName) {
+ return this.#dependencyTracker.getSimpleIndex(dependencyName);
}
- const height = imgData.height,
- width = imgData.width;
- const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
- const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
- const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
- const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
- let srcPos = 0;
- const src = imgData.data;
- const dest = chunkImgData.data;
- for (let i = 0; i < totalChunks; i++) {
- const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
- ({
- srcPos
- } = convertBlackAndWhiteToRGBA({
- src,
- srcPos,
- dest,
- width,
- height: thisChunkHeight,
- nonBlackColor: 0
- }));
- ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
+ recordDependencies(idx, dependencyNames) {
+ this.#dependencyTracker.recordDependencies(this.#opIdx, dependencyNames);
+ return this;
}
-}
-function copyCtxState(sourceCtx, destCtx) {
- const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"];
- for (const property of properties) {
- if (sourceCtx[property] !== undefined) {
- destCtx[property] = sourceCtx[property];
+ recordNamedDependency(idx, name) {
+ this.#dependencyTracker.recordNamedDependency(this.#opIdx, name);
+ return this;
+ }
+ recordOperation(idx) {
+ this.#dependencyTracker.recordOperation(this.#opIdx, true);
+ return this;
+ }
+ recordShowTextOperation(idx) {
+ this.#dependencyTracker.recordShowTextOperation(this.#opIdx, true);
+ return this;
+ }
+ bboxToClipBoxDropOperation(idx) {
+ if (!this.#ignoreBBoxes) {
+ this.#dependencyTracker.bboxToClipBoxDropOperation(this.#opIdx, true);
}
+ return this;
}
- if (sourceCtx.setLineDash !== undefined) {
- destCtx.setLineDash(sourceCtx.getLineDash());
- destCtx.lineDashOffset = sourceCtx.lineDashOffset;
+ take() {
+ throw new Error("Unreachable");
}
-}
-function resetCtxToDefault(ctx) {
- ctx.strokeStyle = ctx.fillStyle = "#000000";
- ctx.fillRule = "nonzero";
- ctx.globalAlpha = 1;
- ctx.lineWidth = 1;
- ctx.lineCap = "butt";
- ctx.lineJoin = "miter";
- ctx.miterLimit = 10;
- ctx.globalCompositeOperation = "source-over";
- ctx.font = "10px sans-serif";
- if (ctx.setLineDash !== undefined) {
- ctx.setLineDash([]);
- ctx.lineDashOffset = 0;
+ takeDebugMetadata() {
+ throw new Error("Unreachable");
}
- const {
- filter
- } = ctx;
- if (filter !== "none" && filter !== "") {
- ctx.filter = "none";
+}
+const Dependencies = {
+ stroke: ["path", "transform", "filter", "strokeColor", "strokeAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "dash"],
+ fill: ["path", "transform", "filter", "fillColor", "fillAlpha", "globalCompositeOperation", "SMask"],
+ imageXObject: ["transform", "SMask", "filter", "fillAlpha", "strokeAlpha", "globalCompositeOperation"],
+ rawFillPath: ["filter", "fillColor", "fillAlpha"],
+ showText: ["transform", "leading", "charSpacing", "wordSpacing", "hScale", "textRise", "moveText", "textMatrix", "font", "fontObj", "filter", "fillColor", "textRenderingMode", "SMask", "fillAlpha", "strokeAlpha", "globalCompositeOperation", "sameLineText"],
+ transform: ["transform"],
+ transformAndFill: ["transform", "fillColor"]
+};
+
+;// ./src/display/pattern_helper.js
+
+
+const PathType = {
+ FILL: "Fill",
+ STROKE: "Stroke",
+ SHADING: "Shading"
+};
+function applyBoundingBox(ctx, bbox) {
+ if (!bbox) {
+ return;
}
+ const width = bbox[2] - bbox[0];
+ const height = bbox[3] - bbox[1];
+ const region = new Path2D();
+ region.rect(bbox[0], bbox[1], width, height);
+ ctx.clip(region);
}
-function getImageSmoothingEnabled(transform, interpolate) {
- if (interpolate) {
- return true;
+class BaseShadingPattern {
+ isModifyingCurrentTransform() {
+ return false;
}
- Util.singularValueDecompose2dScale(transform, XY);
- const actualScale = Math.fround(OutputScale.pixelRatio * PixelsPerInch.PDF_TO_CSS_UNITS);
- return XY[0] <= actualScale && XY[1] <= actualScale;
-}
-const LINE_CAP_STYLES = ["butt", "round", "square"];
-const LINE_JOIN_STYLES = ["miter", "round", "bevel"];
-const NORMAL_CLIP = {};
-const EO_CLIP = {};
-class CanvasGraphics {
- constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, {
- optionalContentConfig,
- markedContentStack = null
- }, annotationCanvasMap, pageColors, dependencyTracker) {
- this.ctx = canvasCtx;
- this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
- this.stateStack = [];
- this.pendingClip = null;
- this.pendingEOFill = false;
- this.res = null;
- this.xobjs = null;
- this.commonObjs = commonObjs;
- this.objs = objs;
- this.canvasFactory = canvasFactory;
- this.filterFactory = filterFactory;
- this.groupStack = [];
- this.baseTransform = null;
- this.baseTransformStack = [];
- this.groupLevel = 0;
- this.smaskStack = [];
- this.smaskCounter = 0;
- this.tempSMask = null;
- this.suspendedCtx = null;
- this.contentVisible = true;
- this.markedContentStack = markedContentStack || [];
- this.optionalContentConfig = optionalContentConfig;
- this.cachedCanvases = new CachedCanvases(this.canvasFactory);
- this.cachedPatterns = new Map();
- this.annotationCanvasMap = annotationCanvasMap;
- this.viewportScale = 1;
- this.outputScaleX = 1;
- this.outputScaleY = 1;
- this.pageColors = pageColors;
- this._cachedScaleForStroking = [-1, 0];
- this._cachedGetSinglePixelWidth = null;
- this._cachedBitmapsMap = new Map();
- this.dependencyTracker = dependencyTracker ?? null;
+ getPattern() {
+ unreachable("Abstract method `getPattern` called.");
}
- getObject(opIdx, data, fallback = null) {
- if (typeof data === "string") {
- this.dependencyTracker?.recordNamedDependency(opIdx, data);
- return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data);
- }
- return fallback;
+}
+class RadialAxialShadingPattern extends BaseShadingPattern {
+ constructor(IR) {
+ super();
+ this._type = IR[1];
+ this._bbox = IR[2];
+ this._colorStops = IR[3];
+ this._p0 = IR[4];
+ this._p1 = IR[5];
+ this._r0 = IR[6];
+ this._r1 = IR[7];
+ this.matrix = null;
}
- beginDrawing({
- transform,
- viewport,
- transparency = false,
- background = null
- }) {
- const width = this.ctx.canvas.width;
- const height = this.ctx.canvas.height;
- const savedFillStyle = this.ctx.fillStyle;
- this.ctx.fillStyle = background || "#ffffff";
- this.ctx.fillRect(0, 0, width, height);
- this.ctx.fillStyle = savedFillStyle;
- if (transparency) {
- const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height);
- this.compositeCtx = this.ctx;
- this.transparentCanvas = transparentCanvas.canvas;
- this.ctx = transparentCanvas.context;
- this.ctx.save();
- this.ctx.transform(...getCurrentTransform(this.compositeCtx));
+ _createGradient(ctx) {
+ let grad;
+ if (this._type === "axial") {
+ grad = ctx.createLinearGradient(this._p0[0], this._p0[1], this._p1[0], this._p1[1]);
+ } else if (this._type === "radial") {
+ grad = ctx.createRadialGradient(this._p0[0], this._p0[1], this._r0, this._p1[0], this._p1[1], this._r1);
}
- this.ctx.save();
- resetCtxToDefault(this.ctx);
- if (transform) {
- this.ctx.transform(...transform);
- this.outputScaleX = transform[0];
- this.outputScaleY = transform[0];
+ for (const colorStop of this._colorStops) {
+ grad.addColorStop(colorStop[0], colorStop[1]);
}
- this.ctx.transform(...viewport.transform);
- this.viewportScale = viewport.scale;
- this.baseTransform = getCurrentTransform(this.ctx);
+ return grad;
}
- executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper, operationsFilter) {
- const argsArray = operatorList.argsArray;
- const fnArray = operatorList.fnArray;
- let i = executionStartIdx || 0;
- const argsArrayLen = argsArray.length;
- if (argsArrayLen === i) {
- return i;
- }
- const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function";
- const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
- let steps = 0;
- const commonObjs = this.commonObjs;
- const objs = this.objs;
- let fnId, fnArgs;
- while (true) {
- if (stepper !== undefined && i === stepper.nextBreakPoint) {
- stepper.breakIt(i, continueCallback);
- return i;
- }
- if (!operationsFilter || operationsFilter(i)) {
- fnId = fnArray[i];
- fnArgs = argsArray[i] ?? null;
- if (fnId !== OPS.dependency) {
- if (fnArgs === null) {
- this[fnId](i);
- } else {
- this[fnId](i, ...fnArgs);
- }
- } else {
- for (const depObjId of fnArgs) {
- this.dependencyTracker?.recordNamedData(depObjId, i);
- const objsPool = depObjId.startsWith("g_") ? commonObjs : objs;
- if (!objsPool.has(depObjId)) {
- objsPool.get(depObjId, continueCallback);
- return i;
- }
- }
- }
- }
- i++;
- if (i === argsArrayLen) {
- return i;
+ getPattern(ctx, owner, inverse, pathType) {
+ let pattern;
+ if (pathType === PathType.STROKE || pathType === PathType.FILL) {
+ const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, getCurrentTransform(ctx)) || [0, 0, 0, 0];
+ const width = Math.ceil(ownerBBox[2] - ownerBBox[0]) || 1;
+ const height = Math.ceil(ownerBBox[3] - ownerBBox[1]) || 1;
+ const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", width, height);
+ const tmpCtx = tmpCanvas.context;
+ tmpCtx.clearRect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
+ tmpCtx.beginPath();
+ tmpCtx.rect(0, 0, tmpCtx.canvas.width, tmpCtx.canvas.height);
+ tmpCtx.translate(-ownerBBox[0], -ownerBBox[1]);
+ inverse = Util.transform(inverse, [1, 0, 0, 1, ownerBBox[0], ownerBBox[1]]);
+ tmpCtx.transform(...owner.baseTransform);
+ if (this.matrix) {
+ tmpCtx.transform(...this.matrix);
}
- if (chunkOperations && ++steps > EXECUTION_STEPS) {
- if (Date.now() > endTime) {
- continueCallback();
- return i;
- }
- steps = 0;
+ applyBoundingBox(tmpCtx, this._bbox);
+ tmpCtx.fillStyle = this._createGradient(tmpCtx);
+ tmpCtx.fill();
+ pattern = ctx.createPattern(tmpCanvas.canvas, "no-repeat");
+ const domMatrix = new DOMMatrix(inverse);
+ pattern.setTransform(domMatrix);
+ } else {
+ applyBoundingBox(ctx, this._bbox);
+ pattern = this._createGradient(ctx);
+ }
+ return pattern;
+ }
+}
+function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
+ const coords = context.coords,
+ colors = context.colors;
+ const bytes = data.data,
+ rowSize = data.width * 4;
+ let tmp;
+ if (coords[p1 + 1] > coords[p2 + 1]) {
+ tmp = p1;
+ p1 = p2;
+ p2 = tmp;
+ tmp = c1;
+ c1 = c2;
+ c2 = tmp;
+ }
+ if (coords[p2 + 1] > coords[p3 + 1]) {
+ tmp = p2;
+ p2 = p3;
+ p3 = tmp;
+ tmp = c2;
+ c2 = c3;
+ c3 = tmp;
+ }
+ if (coords[p1 + 1] > coords[p2 + 1]) {
+ tmp = p1;
+ p1 = p2;
+ p2 = tmp;
+ tmp = c1;
+ c1 = c2;
+ c2 = tmp;
+ }
+ const x1 = (coords[p1] + context.offsetX) * context.scaleX;
+ const y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
+ const x2 = (coords[p2] + context.offsetX) * context.scaleX;
+ const y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
+ const x3 = (coords[p3] + context.offsetX) * context.scaleX;
+ const y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
+ if (y1 >= y3) {
+ return;
+ }
+ const c1r = colors[c1],
+ c1g = colors[c1 + 1],
+ c1b = colors[c1 + 2];
+ const c2r = colors[c2],
+ c2g = colors[c2 + 1],
+ c2b = colors[c2 + 2];
+ const c3r = colors[c3],
+ c3g = colors[c3 + 1],
+ c3b = colors[c3 + 2];
+ const minY = Math.round(y1),
+ maxY = Math.round(y3);
+ let xa, car, cag, cab;
+ let xb, cbr, cbg, cbb;
+ for (let y = minY; y <= maxY; y++) {
+ if (y < y2) {
+ const k = y < y1 ? 0 : (y1 - y) / (y1 - y2);
+ xa = x1 - (x1 - x2) * k;
+ car = c1r - (c1r - c2r) * k;
+ cag = c1g - (c1g - c2g) * k;
+ cab = c1b - (c1b - c2b) * k;
+ } else {
+ let k;
+ if (y > y3) {
+ k = 1;
+ } else if (y2 === y3) {
+ k = 0;
+ } else {
+ k = (y2 - y) / (y2 - y3);
}
+ xa = x2 - (x2 - x3) * k;
+ car = c2r - (c2r - c3r) * k;
+ cag = c2g - (c2g - c3g) * k;
+ cab = c2b - (c2b - c3b) * k;
}
- }
- #restoreInitialState() {
- while (this.stateStack.length || this.inSMaskMode) {
- this.restore();
+ let k;
+ if (y < y1) {
+ k = 0;
+ } else if (y > y3) {
+ k = 1;
+ } else {
+ k = (y1 - y) / (y1 - y3);
}
- this.current.activeSMask = null;
- this.ctx.restore();
- if (this.transparentCanvas) {
- this.ctx = this.compositeCtx;
- this.ctx.save();
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
- this.ctx.drawImage(this.transparentCanvas, 0, 0);
- this.ctx.restore();
- this.transparentCanvas = null;
+ xb = x1 - (x1 - x3) * k;
+ cbr = c1r - (c1r - c3r) * k;
+ cbg = c1g - (c1g - c3g) * k;
+ cbb = c1b - (c1b - c3b) * k;
+ const x1_ = Math.round(Math.min(xa, xb));
+ const x2_ = Math.round(Math.max(xa, xb));
+ let j = rowSize * y + x1_ * 4;
+ for (let x = x1_; x <= x2_; x++) {
+ k = (xa - x) / (xa - xb);
+ if (k < 0) {
+ k = 0;
+ } else if (k > 1) {
+ k = 1;
+ }
+ bytes[j++] = car - (car - cbr) * k | 0;
+ bytes[j++] = cag - (cag - cbg) * k | 0;
+ bytes[j++] = cab - (cab - cbb) * k | 0;
+ bytes[j++] = 255;
}
}
- endDrawing() {
- this.#restoreInitialState();
- this.cachedCanvases.clear();
- this.cachedPatterns.clear();
- for (const cache of this._cachedBitmapsMap.values()) {
- for (const canvas of cache.values()) {
- if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) {
- canvas.width = canvas.height = 0;
+}
+function drawFigure(data, figure, context) {
+ const ps = figure.coords;
+ const cs = figure.colors;
+ let i, ii;
+ switch (figure.type) {
+ case MeshFigureType.LATTICE:
+ const verticesPerRow = figure.verticesPerRow;
+ const rows = Math.floor(ps.length / verticesPerRow) - 1;
+ const cols = verticesPerRow - 1;
+ for (i = 0; i < rows; i++) {
+ let q = i * verticesPerRow;
+ for (let j = 0; j < cols; j++, q++) {
+ drawTriangle(data, context, ps[q], ps[q + 1], ps[q + verticesPerRow], cs[q], cs[q + 1], cs[q + verticesPerRow]);
+ drawTriangle(data, context, ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow], cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
}
}
- cache.clear();
- }
- this._cachedBitmapsMap.clear();
- this.#drawFilter();
- }
- #drawFilter() {
- if (this.pageColors) {
- const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background);
- if (hcmFilterId !== "none") {
- const savedFilter = this.ctx.filter;
- this.ctx.filter = hcmFilterId;
- this.ctx.drawImage(this.ctx.canvas, 0, 0);
- this.ctx.filter = savedFilter;
+ break;
+ case MeshFigureType.TRIANGLES:
+ for (i = 0, ii = ps.length; i < ii; i += 3) {
+ drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]);
}
- }
+ break;
+ default:
+ throw new Error("illegal figure");
}
- _scaleImage(img, inverseTransform) {
- const width = img.width ?? img.displayWidth;
- const height = img.height ?? img.displayHeight;
- let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1);
- let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1);
- let paintWidth = width,
- paintHeight = height;
- let tmpCanvasId = "prescale1";
- let tmpCanvas, tmpCtx;
- while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {
- let newWidth = paintWidth,
- newHeight = paintHeight;
- if (widthScale > 2 && paintWidth > 1) {
- newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2);
- widthScale /= paintWidth / newWidth;
- }
- if (heightScale > 2 && paintHeight > 1) {
- newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2;
- heightScale /= paintHeight / newHeight;
- }
- tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
- tmpCtx = tmpCanvas.context;
- tmpCtx.clearRect(0, 0, newWidth, newHeight);
- tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);
- img = tmpCanvas.canvas;
- paintWidth = newWidth;
- paintHeight = newHeight;
- tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
- }
- return {
- img,
- paintWidth,
- paintHeight
- };
+}
+class MeshShadingPattern extends BaseShadingPattern {
+ constructor(IR) {
+ super();
+ this._coords = IR[2];
+ this._colors = IR[3];
+ this._figures = IR[4];
+ this._bounds = IR[5];
+ this._bbox = IR[6];
+ this._background = IR[7];
+ this.matrix = null;
}
- _createMaskCanvas(opIdx, img) {
- const ctx = this.ctx;
- const {
- width,
- height
- } = img;
- const fillColor = this.current.fillColor;
- const isPatternFill = this.current.patternFill;
- const currentTransform = getCurrentTransform(ctx);
- let cache, cacheKey, scaled, maskCanvas;
- if ((img.bitmap || img.data) && img.count > 1) {
- const mainKey = img.bitmap || img.data.buffer;
- cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]);
- cache = this._cachedBitmapsMap.get(mainKey);
- if (!cache) {
- cache = new Map();
- this._cachedBitmapsMap.set(mainKey, cache);
- }
- const cachedImage = cache.get(cacheKey);
- if (cachedImage && !isPatternFill) {
- const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]);
- const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]);
- this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill);
- return {
- canvas: cachedImage,
- offsetX,
- offsetY
- };
- }
- scaled = cachedImage;
- }
- if (!scaled) {
- maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
- putBinaryImageMask(maskCanvas.context, img);
- }
- let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]);
- maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
- const minMax = MIN_MAX_INIT.slice();
- Util.axialAlignedBoundingBox([0, 0, width, height], maskToCanvas, minMax);
- const [minX, minY, maxX, maxY] = minMax;
- const drawnWidth = Math.round(maxX - minX) || 1;
- const drawnHeight = Math.round(maxY - minY) || 1;
- const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight);
- const fillCtx = fillCanvas.context;
- const offsetX = minX;
- const offsetY = minY;
- fillCtx.translate(-offsetX, -offsetY);
- fillCtx.transform(...maskToCanvas);
- if (!scaled) {
- scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx));
- scaled = scaled.img;
- if (cache && isPatternFill) {
- cache.set(cacheKey, scaled);
- }
- }
- fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate);
- drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height);
- fillCtx.globalCompositeOperation = "source-in";
- const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]);
- fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL, opIdx) : fillColor;
- fillCtx.fillRect(0, 0, width, height);
- if (cache && !isPatternFill) {
- this.cachedCanvases.delete("fillCanvas");
- cache.set(cacheKey, fillCanvas.canvas);
- }
- this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill);
- return {
- canvas: fillCanvas.canvas,
- offsetX: Math.round(offsetX),
- offsetY: Math.round(offsetY)
+ _createMeshCanvas(combinedScale, backgroundColor, cachedCanvases) {
+ const EXPECTED_SCALE = 1.1;
+ const MAX_PATTERN_SIZE = 3000;
+ const BORDER_SIZE = 2;
+ const offsetX = Math.floor(this._bounds[0]);
+ const offsetY = Math.floor(this._bounds[1]);
+ const boundsWidth = Math.ceil(this._bounds[2]) - offsetX;
+ const boundsHeight = Math.ceil(this._bounds[3]) - offsetY;
+ const width = Math.min(Math.ceil(Math.abs(boundsWidth * combinedScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
+ const height = Math.min(Math.ceil(Math.abs(boundsHeight * combinedScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE);
+ const scaleX = boundsWidth / width;
+ const scaleY = boundsHeight / height;
+ const context = {
+ coords: this._coords,
+ colors: this._colors,
+ offsetX: -offsetX,
+ offsetY: -offsetY,
+ scaleX: 1 / scaleX,
+ scaleY: 1 / scaleY
};
- }
- setLineWidth(opIdx, width) {
- this.dependencyTracker?.recordSimpleData("lineWidth", opIdx);
- if (width !== this.current.lineWidth) {
- this._cachedScaleForStroking[0] = -1;
+ const paddedWidth = width + BORDER_SIZE * 2;
+ const paddedHeight = height + BORDER_SIZE * 2;
+ const tmpCanvas = cachedCanvases.getCanvas("mesh", paddedWidth, paddedHeight);
+ const tmpCtx = tmpCanvas.context;
+ const data = tmpCtx.createImageData(width, height);
+ if (backgroundColor) {
+ const bytes = data.data;
+ for (let i = 0, ii = bytes.length; i < ii; i += 4) {
+ bytes[i] = backgroundColor[0];
+ bytes[i + 1] = backgroundColor[1];
+ bytes[i + 2] = backgroundColor[2];
+ bytes[i + 3] = 255;
+ }
}
- this.current.lineWidth = width;
- this.ctx.lineWidth = width;
- }
- setLineCap(opIdx, style) {
- this.dependencyTracker?.recordSimpleData("lineCap", opIdx);
- this.ctx.lineCap = LINE_CAP_STYLES[style];
- }
- setLineJoin(opIdx, style) {
- this.dependencyTracker?.recordSimpleData("lineJoin", opIdx);
- this.ctx.lineJoin = LINE_JOIN_STYLES[style];
+ for (const figure of this._figures) {
+ drawFigure(data, figure, context);
+ }
+ tmpCtx.putImageData(data, BORDER_SIZE, BORDER_SIZE);
+ const canvas = tmpCanvas.canvas;
+ return {
+ canvas,
+ offsetX: offsetX - BORDER_SIZE * scaleX,
+ offsetY: offsetY - BORDER_SIZE * scaleY,
+ scaleX,
+ scaleY
+ };
}
- setMiterLimit(opIdx, limit) {
- this.dependencyTracker?.recordSimpleData("miterLimit", opIdx);
- this.ctx.miterLimit = limit;
+ isModifyingCurrentTransform() {
+ return true;
}
- setDash(opIdx, dashArray, dashPhase) {
- this.dependencyTracker?.recordSimpleData("dash", opIdx);
- const ctx = this.ctx;
- if (ctx.setLineDash !== undefined) {
- ctx.setLineDash(dashArray);
- ctx.lineDashOffset = dashPhase;
+ getPattern(ctx, owner, inverse, pathType) {
+ applyBoundingBox(ctx, this._bbox);
+ const scale = new Float32Array(2);
+ if (pathType === PathType.SHADING) {
+ Util.singularValueDecompose2dScale(getCurrentTransform(ctx), scale);
+ } else if (this.matrix) {
+ Util.singularValueDecompose2dScale(this.matrix, scale);
+ const [matrixScaleX, matrixScaleY] = scale;
+ Util.singularValueDecompose2dScale(owner.baseTransform, scale);
+ scale[0] *= matrixScaleX;
+ scale[1] *= matrixScaleY;
+ } else {
+ Util.singularValueDecompose2dScale(owner.baseTransform, scale);
}
- }
- setRenderingIntent(opIdx, intent) {}
- setFlatness(opIdx, flatness) {}
- setGState(opIdx, states) {
- for (const [key, value] of states) {
- switch (key) {
- case "LW":
- this.setLineWidth(opIdx, value);
- break;
- case "LC":
- this.setLineCap(opIdx, value);
- break;
- case "LJ":
- this.setLineJoin(opIdx, value);
- break;
- case "ML":
- this.setMiterLimit(opIdx, value);
- break;
- case "D":
- this.setDash(opIdx, value[0], value[1]);
- break;
- case "RI":
- this.setRenderingIntent(opIdx, value);
- break;
- case "FL":
- this.setFlatness(opIdx, value);
- break;
- case "Font":
- this.setFont(opIdx, value[0], value[1]);
- break;
- case "CA":
- this.dependencyTracker?.recordSimpleData("strokeAlpha", opIdx);
- this.current.strokeAlpha = value;
- break;
- case "ca":
- this.dependencyTracker?.recordSimpleData("fillAlpha", opIdx);
- this.ctx.globalAlpha = this.current.fillAlpha = value;
- break;
- case "BM":
- this.dependencyTracker?.recordSimpleData("globalCompositeOperation", opIdx);
- this.ctx.globalCompositeOperation = value;
- break;
- case "SMask":
- this.dependencyTracker?.recordSimpleData("SMask", opIdx);
- this.current.activeSMask = value ? this.tempSMask : null;
- this.tempSMask = null;
- this.checkSMaskState();
- break;
- case "TR":
- this.dependencyTracker?.recordSimpleData("filter", opIdx);
- this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value);
- break;
+ const temporaryPatternCanvas = this._createMeshCanvas(scale, pathType === PathType.SHADING ? null : this._background, owner.cachedCanvases);
+ if (pathType !== PathType.SHADING) {
+ ctx.setTransform(...owner.baseTransform);
+ if (this.matrix) {
+ ctx.transform(...this.matrix);
}
}
+ ctx.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
+ ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY);
+ return ctx.createPattern(temporaryPatternCanvas.canvas, "no-repeat");
}
- get inSMaskMode() {
- return !!this.suspendedCtx;
+}
+class DummyShadingPattern extends BaseShadingPattern {
+ getPattern() {
+ return "hotpink";
}
- checkSMaskState() {
- const inSMaskMode = this.inSMaskMode;
- if (this.current.activeSMask && !inSMaskMode) {
- this.beginSMaskMode();
- } else if (!this.current.activeSMask && inSMaskMode) {
- this.endSMaskMode();
- }
+}
+function getShadingPattern(IR) {
+ switch (IR[0]) {
+ case "RadialAxial":
+ return new RadialAxialShadingPattern(IR);
+ case "Mesh":
+ return new MeshShadingPattern(IR);
+ case "Dummy":
+ return new DummyShadingPattern();
}
- beginSMaskMode(opIdx) {
- if (this.inSMaskMode) {
- throw new Error("beginSMaskMode called while already in smask mode");
- }
- const drawnWidth = this.ctx.canvas.width;
- const drawnHeight = this.ctx.canvas.height;
- const cacheId = "smaskGroupAt" + this.groupLevel;
- const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);
- this.suspendedCtx = this.ctx;
- const ctx = this.ctx = scratchCanvas.context;
- ctx.setTransform(this.suspendedCtx.getTransform());
- copyCtxState(this.suspendedCtx, ctx);
- mirrorContextOperations(ctx, this.suspendedCtx);
- this.setGState(opIdx, [["BM", "source-over"]]);
+ throw new Error(`Unknown IR type: ${IR[0]}`);
+}
+const PaintType = {
+ COLORED: 1,
+ UNCOLORED: 2
+};
+class TilingPattern {
+ static MAX_PATTERN_SIZE = 3000;
+ constructor(IR, ctx, canvasGraphicsFactory, baseTransform) {
+ this.color = IR[1];
+ this.operatorList = IR[2];
+ this.matrix = IR[3];
+ this.bbox = IR[4];
+ this.xstep = IR[5];
+ this.ystep = IR[6];
+ this.paintType = IR[7];
+ this.tilingType = IR[8];
+ this.ctx = ctx;
+ this.canvasGraphicsFactory = canvasGraphicsFactory;
+ this.baseTransform = baseTransform;
}
- endSMaskMode() {
- if (!this.inSMaskMode) {
- throw new Error("endSMaskMode called while not in smask mode");
+ createPatternCanvas(owner, opIdx) {
+ const {
+ bbox,
+ operatorList,
+ paintType,
+ tilingType,
+ color,
+ canvasGraphicsFactory
+ } = this;
+ let {
+ xstep,
+ ystep
+ } = this;
+ xstep = Math.abs(xstep);
+ ystep = Math.abs(ystep);
+ info("TilingType: " + tilingType);
+ const x0 = bbox[0],
+ y0 = bbox[1],
+ x1 = bbox[2],
+ y1 = bbox[3];
+ const width = x1 - x0;
+ const height = y1 - y0;
+ const scale = new Float32Array(2);
+ Util.singularValueDecompose2dScale(this.matrix, scale);
+ const [matrixScaleX, matrixScaleY] = scale;
+ Util.singularValueDecompose2dScale(this.baseTransform, scale);
+ const combinedScaleX = matrixScaleX * scale[0];
+ const combinedScaleY = matrixScaleY * scale[1];
+ let canvasWidth = width,
+ canvasHeight = height,
+ redrawHorizontally = false,
+ redrawVertically = false;
+ const xScaledStep = Math.ceil(xstep * combinedScaleX);
+ const yScaledStep = Math.ceil(ystep * combinedScaleY);
+ const xScaledWidth = Math.ceil(width * combinedScaleX);
+ const yScaledHeight = Math.ceil(height * combinedScaleY);
+ if (xScaledStep >= xScaledWidth) {
+ canvasWidth = xstep;
+ } else {
+ redrawHorizontally = true;
}
- this.ctx._removeMirroring();
- copyCtxState(this.ctx, this.suspendedCtx);
- this.ctx = this.suspendedCtx;
- this.suspendedCtx = null;
+ if (yScaledStep >= yScaledHeight) {
+ canvasHeight = ystep;
+ } else {
+ redrawVertically = true;
+ }
+ const dimx = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX);
+ const dimy = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY);
+ const tmpCanvas = owner.cachedCanvases.getCanvas("pattern", dimx.size, dimy.size);
+ const tmpCtx = tmpCanvas.context;
+ const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx, opIdx);
+ graphics.groupLevel = owner.groupLevel;
+ this.setFillAndStrokeStyleToContext(graphics, paintType, color);
+ tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0);
+ graphics.transform(0, dimx.scale, 0, 0, dimy.scale, 0, 0);
+ tmpCtx.save();
+ graphics.dependencyTracker?.save();
+ this.clipBbox(graphics, x0, y0, x1, y1);
+ graphics.baseTransform = getCurrentTransform(graphics.ctx);
+ graphics.executeOperatorList(operatorList);
+ graphics.endDrawing();
+ graphics.dependencyTracker?.restore();
+ tmpCtx.restore();
+ if (redrawHorizontally || redrawVertically) {
+ const image = tmpCanvas.canvas;
+ if (redrawHorizontally) {
+ canvasWidth = xstep;
+ }
+ if (redrawVertically) {
+ canvasHeight = ystep;
+ }
+ const dimx2 = this.getSizeAndScale(canvasWidth, this.ctx.canvas.width, combinedScaleX);
+ const dimy2 = this.getSizeAndScale(canvasHeight, this.ctx.canvas.height, combinedScaleY);
+ const xSize = dimx2.size;
+ const ySize = dimy2.size;
+ const tmpCanvas2 = owner.cachedCanvases.getCanvas("pattern-workaround", xSize, ySize);
+ const tmpCtx2 = tmpCanvas2.context;
+ const ii = redrawHorizontally ? Math.floor(width / xstep) : 0;
+ const jj = redrawVertically ? Math.floor(height / ystep) : 0;
+ for (let i = 0; i <= ii; i++) {
+ for (let j = 0; j <= jj; j++) {
+ tmpCtx2.drawImage(image, xSize * i, ySize * j, xSize, ySize, 0, 0, xSize, ySize);
+ }
+ }
+ return {
+ canvas: tmpCanvas2.canvas,
+ scaleX: dimx2.scale,
+ scaleY: dimy2.scale,
+ offsetX: x0,
+ offsetY: y0
+ };
+ }
+ return {
+ canvas: tmpCanvas.canvas,
+ scaleX: dimx.scale,
+ scaleY: dimy.scale,
+ offsetX: x0,
+ offsetY: y0
+ };
}
- compose(dirtyBox) {
- if (!this.current.activeSMask) {
- return;
- }
- if (!dirtyBox) {
- dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];
+ getSizeAndScale(step, realOutputSize, scale) {
+ const maxSize = Math.max(TilingPattern.MAX_PATTERN_SIZE, realOutputSize);
+ let size = Math.ceil(step * scale);
+ if (size >= maxSize) {
+ size = maxSize;
} else {
- dirtyBox[0] = Math.floor(dirtyBox[0]);
- dirtyBox[1] = Math.floor(dirtyBox[1]);
- dirtyBox[2] = Math.ceil(dirtyBox[2]);
- dirtyBox[3] = Math.ceil(dirtyBox[3]);
+ scale = size / step;
}
- const smask = this.current.activeSMask;
- const suspendedCtx = this.suspendedCtx;
- this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);
- this.ctx.save();
- this.ctx.setTransform(1, 0, 0, 1, 0, 0);
- this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
- this.ctx.restore();
+ return {
+ scale,
+ size
+ };
}
- composeSMask(ctx, smask, layerCtx, layerBox) {
- const layerOffsetX = layerBox[0];
- const layerOffsetY = layerBox[1];
- const layerWidth = layerBox[2] - layerOffsetX;
- const layerHeight = layerBox[3] - layerOffsetY;
- if (layerWidth === 0 || layerHeight === 0) {
- return;
- }
- this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY);
- ctx.save();
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "source-over";
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- ctx.drawImage(layerCtx.canvas, 0, 0);
- ctx.restore();
+ clipBbox(graphics, x0, y0, x1, y1) {
+ const bboxWidth = x1 - x0;
+ const bboxHeight = y1 - y0;
+ graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
+ Util.axialAlignedBoundingBox([x0, y0, x1, y1], getCurrentTransform(graphics.ctx), graphics.current.minMax);
+ graphics.clip();
+ graphics.endPath();
}
- genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) {
- let maskCanvas = maskCtx.canvas;
- let maskX = layerOffsetX - maskOffsetX;
- let maskY = layerOffsetY - maskOffsetY;
- if (backdrop) {
- if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) {
- const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height);
- const ctx = canvas.context;
- ctx.drawImage(maskCanvas, -maskX, -maskY);
- ctx.globalCompositeOperation = "destination-atop";
- ctx.fillStyle = backdrop;
- ctx.fillRect(0, 0, width, height);
- ctx.globalCompositeOperation = "source-over";
- maskCanvas = canvas.canvas;
- maskX = maskY = 0;
- } else {
- maskCtx.save();
- maskCtx.globalAlpha = 1;
- maskCtx.setTransform(1, 0, 0, 1, 0, 0);
- const clip = new Path2D();
- clip.rect(maskX, maskY, width, height);
- maskCtx.clip(clip);
- maskCtx.globalCompositeOperation = "destination-atop";
- maskCtx.fillStyle = backdrop;
- maskCtx.fillRect(maskX, maskY, width, height);
- maskCtx.restore();
- }
- }
- layerCtx.save();
- layerCtx.globalAlpha = 1;
- layerCtx.setTransform(1, 0, 0, 1, 0, 0);
- if (subtype === "Alpha" && transferMap) {
- layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap);
- } else if (subtype === "Luminosity") {
- layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap);
+ setFillAndStrokeStyleToContext(graphics, paintType, color) {
+ const context = graphics.ctx,
+ current = graphics.current;
+ switch (paintType) {
+ case PaintType.COLORED:
+ const {
+ fillStyle,
+ strokeStyle
+ } = this.ctx;
+ context.fillStyle = current.fillColor = fillStyle;
+ context.strokeStyle = current.strokeColor = strokeStyle;
+ break;
+ case PaintType.UNCOLORED:
+ context.fillStyle = context.strokeStyle = color;
+ current.fillColor = current.strokeColor = color;
+ break;
+ default:
+ throw new FormatError(`Unsupported paint type: ${paintType}`);
}
- const clip = new Path2D();
- clip.rect(layerOffsetX, layerOffsetY, width, height);
- layerCtx.clip(clip);
- layerCtx.globalCompositeOperation = "destination-in";
- layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height);
- layerCtx.restore();
}
- save(opIdx) {
- if (this.inSMaskMode) {
- copyCtxState(this.ctx, this.suspendedCtx);
- }
- this.ctx.save();
- const old = this.current;
- this.stateStack.push(old);
- this.current = old.clone();
- this.dependencyTracker?.save(opIdx);
+ isModifyingCurrentTransform() {
+ return false;
}
- restore(opIdx) {
- this.dependencyTracker?.restore(opIdx);
- if (this.stateStack.length === 0) {
- if (this.inSMaskMode) {
- this.endSMaskMode();
+ getPattern(ctx, owner, inverse, pathType, opIdx) {
+ let matrix = inverse;
+ if (pathType !== PathType.SHADING) {
+ matrix = Util.transform(matrix, owner.baseTransform);
+ if (this.matrix) {
+ matrix = Util.transform(matrix, this.matrix);
}
- return;
- }
- this.current = this.stateStack.pop();
- this.ctx.restore();
- if (this.inSMaskMode) {
- copyCtxState(this.suspendedCtx, this.ctx);
}
- this.checkSMaskState();
- this.pendingClip = null;
- this._cachedScaleForStroking[0] = -1;
- this._cachedGetSinglePixelWidth = null;
+ const temporaryPatternCanvas = this.createPatternCanvas(owner, opIdx);
+ let domMatrix = new DOMMatrix(matrix);
+ domMatrix = domMatrix.translate(temporaryPatternCanvas.offsetX, temporaryPatternCanvas.offsetY);
+ domMatrix = domMatrix.scale(1 / temporaryPatternCanvas.scaleX, 1 / temporaryPatternCanvas.scaleY);
+ const pattern = ctx.createPattern(temporaryPatternCanvas.canvas, "repeat");
+ pattern.setTransform(domMatrix);
+ return pattern;
}
- transform(opIdx, a, b, c, d, e, f) {
- this.dependencyTracker?.recordIncrementalData("transform", opIdx);
- this.ctx.transform(a, b, c, d, e, f);
- this._cachedScaleForStroking[0] = -1;
- this._cachedGetSinglePixelWidth = null;
+}
+
+;// ./src/shared/image_utils.js
+
+function convertToRGBA(params) {
+ switch (params.kind) {
+ case ImageKind.GRAYSCALE_1BPP:
+ return convertBlackAndWhiteToRGBA(params);
+ case ImageKind.RGB_24BPP:
+ return convertRGBToRGBA(params);
}
- constructPath(opIdx, op, data, minMax) {
- let [path] = data;
- if (!minMax) {
- path ||= data[0] = new Path2D();
- this[op](opIdx, path);
- return;
+ return null;
+}
+function convertBlackAndWhiteToRGBA({
+ src,
+ srcPos = 0,
+ dest,
+ width,
+ height,
+ nonBlackColor = 0xffffffff,
+ inverseDecode = false
+}) {
+ const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;
+ const [zeroMapping, oneMapping] = inverseDecode ? [nonBlackColor, black] : [black, nonBlackColor];
+ const widthInSource = width >> 3;
+ const widthRemainder = width & 7;
+ const srcLength = src.length;
+ dest = new Uint32Array(dest.buffer);
+ let destPos = 0;
+ for (let i = 0; i < height; i++) {
+ for (const max = srcPos + widthInSource; srcPos < max; srcPos++) {
+ const elem = srcPos < srcLength ? src[srcPos] : 255;
+ dest[destPos++] = elem & 0b10000000 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b1000000 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b100000 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b10000 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b1000 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b100 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b10 ? oneMapping : zeroMapping;
+ dest[destPos++] = elem & 0b1 ? oneMapping : zeroMapping;
}
- if (this.dependencyTracker !== null) {
- const outerExtraSize = op === OPS.stroke ? this.current.lineWidth / 2 : 0;
- this.dependencyTracker.resetBBox(opIdx).recordBBox(opIdx, this.ctx, minMax[0] - outerExtraSize, minMax[2] + outerExtraSize, minMax[1] - outerExtraSize, minMax[3] + outerExtraSize).recordDependencies(opIdx, ["transform"]);
+ if (widthRemainder === 0) {
+ continue;
}
- if (!(path instanceof Path2D)) {
- const path2d = data[0] = new Path2D();
- for (let i = 0, ii = path.length; i < ii;) {
- switch (path[i++]) {
- case DrawOPS.moveTo:
- path2d.moveTo(path[i++], path[i++]);
- break;
- case DrawOPS.lineTo:
- path2d.lineTo(path[i++], path[i++]);
- break;
- case DrawOPS.curveTo:
- path2d.bezierCurveTo(path[i++], path[i++], path[i++], path[i++], path[i++], path[i++]);
- break;
- case DrawOPS.closePath:
- path2d.closePath();
- break;
- default:
- warn(`Unrecognized drawing path operator: ${path[i - 1]}`);
- break;
- }
- }
- path = path2d;
+ const elem = srcPos < srcLength ? src[srcPos++] : 255;
+ for (let j = 0; j < widthRemainder; j++) {
+ dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping;
}
- Util.axialAlignedBoundingBox(minMax, getCurrentTransform(this.ctx), this.current.minMax);
- this[op](opIdx, path);
- this._pathStartIdx = opIdx;
- }
- closePath(opIdx) {
- this.ctx.closePath();
}
- stroke(opIdx, path, consumePath = true) {
- const ctx = this.ctx;
- const strokeColor = this.current.strokeColor;
- ctx.globalAlpha = this.current.strokeAlpha;
- if (this.contentVisible) {
- if (typeof strokeColor === "object" && strokeColor?.getPattern) {
- const baseTransform = strokeColor.isModifyingCurrentTransform() ? ctx.getTransform() : null;
- ctx.save();
- ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx);
- if (baseTransform) {
- const newPath = new Path2D();
- newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
- path = newPath;
- }
- this.rescaleAndStroke(path, false);
- ctx.restore();
- } else {
- this.rescaleAndStroke(path, true);
- }
+ return {
+ srcPos,
+ destPos
+ };
+}
+function convertRGBToRGBA({
+ src,
+ srcPos = 0,
+ dest,
+ destPos = 0,
+ width,
+ height
+}) {
+ let i = 0;
+ const len = width * height * 3;
+ const len32 = len >> 2;
+ const src32 = new Uint32Array(src.buffer, srcPos, len32);
+ if (FeatureTest.isLittleEndian) {
+ for (; i < len32 - 2; i += 3, destPos += 4) {
+ const s1 = src32[i];
+ const s2 = src32[i + 1];
+ const s3 = src32[i + 2];
+ dest[destPos] = s1 | 0xff000000;
+ dest[destPos + 1] = s1 >>> 24 | s2 << 8 | 0xff000000;
+ dest[destPos + 2] = s2 >>> 16 | s3 << 16 | 0xff000000;
+ dest[destPos + 3] = s3 >>> 8 | 0xff000000;
}
- this.dependencyTracker?.recordDependencies(opIdx, Dependencies.stroke);
- if (consumePath) {
- this.consumePath(opIdx, path, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx)));
+ for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
+ dest[destPos++] = src[j] | src[j + 1] << 8 | src[j + 2] << 16 | 0xff000000;
}
- ctx.globalAlpha = this.current.fillAlpha;
- }
- closeStroke(opIdx, path) {
- this.stroke(opIdx, path);
- }
- fill(opIdx, path, consumePath = true) {
- const ctx = this.ctx;
- const fillColor = this.current.fillColor;
- const isPatternFill = this.current.patternFill;
- let needRestore = false;
- if (isPatternFill) {
- const baseTransform = fillColor.isModifyingCurrentTransform() ? ctx.getTransform() : null;
- this.dependencyTracker?.save(opIdx);
- ctx.save();
- ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx);
- if (baseTransform) {
- const newPath = new Path2D();
- newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
- path = newPath;
- }
- needRestore = true;
+ } else {
+ for (; i < len32 - 2; i += 3, destPos += 4) {
+ const s1 = src32[i];
+ const s2 = src32[i + 1];
+ const s3 = src32[i + 2];
+ dest[destPos] = s1 | 0xff;
+ dest[destPos + 1] = s1 << 24 | s2 >>> 8 | 0xff;
+ dest[destPos + 2] = s2 << 16 | s3 >>> 16 | 0xff;
+ dest[destPos + 3] = s3 << 8 | 0xff;
}
- const intersect = this.current.getClippedPathBoundingBox();
- if (this.contentVisible && intersect !== null) {
- if (this.pendingEOFill) {
- ctx.fill(path, "evenodd");
- this.pendingEOFill = false;
- } else {
- ctx.fill(path);
- }
+ for (let j = i * 4, jj = srcPos + len; j < jj; j += 3) {
+ dest[destPos++] = src[j] << 24 | src[j + 1] << 16 | src[j + 2] << 8 | 0xff;
}
- this.dependencyTracker?.recordDependencies(opIdx, Dependencies.fill);
- if (needRestore) {
- ctx.restore();
- this.dependencyTracker?.restore(opIdx);
+ }
+ return {
+ srcPos: srcPos + len,
+ destPos
+ };
+}
+function grayToRGBA(src, dest) {
+ if (FeatureTest.isLittleEndian) {
+ for (let i = 0, ii = src.length; i < ii; i++) {
+ dest[i] = src[i] * 0x10101 | 0xff000000;
}
- if (consumePath) {
- this.consumePath(opIdx, path, intersect);
+ } else {
+ for (let i = 0, ii = src.length; i < ii; i++) {
+ dest[i] = src[i] * 0x1010100 | 0x000000ff;
}
}
- eoFill(opIdx, path) {
- this.pendingEOFill = true;
- this.fill(opIdx, path);
- }
- fillStroke(opIdx, path) {
- this.fill(opIdx, path, false);
- this.stroke(opIdx, path, false);
- this.consumePath(opIdx, path);
- }
- eoFillStroke(opIdx, path) {
- this.pendingEOFill = true;
- this.fillStroke(opIdx, path);
- }
- closeFillStroke(opIdx, path) {
- this.fillStroke(opIdx, path);
- }
- closeEOFillStroke(opIdx, path) {
- this.pendingEOFill = true;
- this.fillStroke(opIdx, path);
- }
- endPath(opIdx, path) {
- this.consumePath(opIdx, path);
- }
- rawFillPath(opIdx, path) {
- this.ctx.fill(path);
- this.dependencyTracker?.recordDependencies(opIdx, Dependencies.rawFillPath).recordOperation(opIdx);
- }
- clip(opIdx) {
- this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx);
- this.pendingClip = NORMAL_CLIP;
- }
- eoClip(opIdx) {
- this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx);
- this.pendingClip = EO_CLIP;
- }
- beginText(opIdx) {
- this.current.textMatrix = null;
- this.current.textMatrixScale = 1;
- this.current.x = this.current.lineX = 0;
- this.current.y = this.current.lineY = 0;
- this.dependencyTracker?.recordOpenMarker(opIdx).resetIncrementalData("sameLineText").resetIncrementalData("moveText", opIdx);
+}
+
+;// ./src/display/canvas.js
+
+
+
+
+
+const MIN_FONT_SIZE = 16;
+const MAX_FONT_SIZE = 100;
+const EXECUTION_TIME = 15;
+const EXECUTION_STEPS = 10;
+const FULL_CHUNK_HEIGHT = 16;
+const SCALE_MATRIX = new DOMMatrix();
+const XY = new Float32Array(2);
+const MIN_MAX_INIT = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]);
+function mirrorContextOperations(ctx, destCtx) {
+ if (ctx._removeMirroring) {
+ throw new Error("Context is already forwarding operations.");
}
- endText(opIdx) {
- const paths = this.pendingTextPaths;
- const ctx = this.ctx;
- if (this.dependencyTracker) {
- const {
- dependencyTracker
- } = this;
- if (paths !== undefined) {
- dependencyTracker.recordFutureForcedDependency("textClip", dependencyTracker.getOpenMarker()).recordFutureForcedDependency("textClip", opIdx);
- }
- dependencyTracker.recordCloseMarker(opIdx);
- }
- if (paths !== undefined) {
- const newPath = new Path2D();
- const invTransf = ctx.getTransform().invertSelf();
- for (const {
- transform,
- x,
- y,
- fontSize,
- path
- } of paths) {
- if (!path) {
- continue;
- }
- newPath.addPath(path, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x, y).scale(fontSize, -fontSize));
- }
- ctx.clip(newPath);
+ ctx.__originalSave = ctx.save;
+ ctx.__originalRestore = ctx.restore;
+ ctx.__originalRotate = ctx.rotate;
+ ctx.__originalScale = ctx.scale;
+ ctx.__originalTranslate = ctx.translate;
+ ctx.__originalTransform = ctx.transform;
+ ctx.__originalSetTransform = ctx.setTransform;
+ ctx.__originalResetTransform = ctx.resetTransform;
+ ctx.__originalClip = ctx.clip;
+ ctx.__originalMoveTo = ctx.moveTo;
+ ctx.__originalLineTo = ctx.lineTo;
+ ctx.__originalBezierCurveTo = ctx.bezierCurveTo;
+ ctx.__originalRect = ctx.rect;
+ ctx.__originalClosePath = ctx.closePath;
+ ctx.__originalBeginPath = ctx.beginPath;
+ ctx._removeMirroring = () => {
+ ctx.save = ctx.__originalSave;
+ ctx.restore = ctx.__originalRestore;
+ ctx.rotate = ctx.__originalRotate;
+ ctx.scale = ctx.__originalScale;
+ ctx.translate = ctx.__originalTranslate;
+ ctx.transform = ctx.__originalTransform;
+ ctx.setTransform = ctx.__originalSetTransform;
+ ctx.resetTransform = ctx.__originalResetTransform;
+ ctx.clip = ctx.__originalClip;
+ ctx.moveTo = ctx.__originalMoveTo;
+ ctx.lineTo = ctx.__originalLineTo;
+ ctx.bezierCurveTo = ctx.__originalBezierCurveTo;
+ ctx.rect = ctx.__originalRect;
+ ctx.closePath = ctx.__originalClosePath;
+ ctx.beginPath = ctx.__originalBeginPath;
+ delete ctx._removeMirroring;
+ };
+ ctx.save = function () {
+ destCtx.save();
+ this.__originalSave();
+ };
+ ctx.restore = function () {
+ destCtx.restore();
+ this.__originalRestore();
+ };
+ ctx.translate = function (x, y) {
+ destCtx.translate(x, y);
+ this.__originalTranslate(x, y);
+ };
+ ctx.scale = function (x, y) {
+ destCtx.scale(x, y);
+ this.__originalScale(x, y);
+ };
+ ctx.transform = function (a, b, c, d, e, f) {
+ destCtx.transform(a, b, c, d, e, f);
+ this.__originalTransform(a, b, c, d, e, f);
+ };
+ ctx.setTransform = function (a, b, c, d, e, f) {
+ destCtx.setTransform(a, b, c, d, e, f);
+ this.__originalSetTransform(a, b, c, d, e, f);
+ };
+ ctx.resetTransform = function () {
+ destCtx.resetTransform();
+ this.__originalResetTransform();
+ };
+ ctx.rotate = function (angle) {
+ destCtx.rotate(angle);
+ this.__originalRotate(angle);
+ };
+ ctx.clip = function (rule) {
+ destCtx.clip(rule);
+ this.__originalClip(rule);
+ };
+ ctx.moveTo = function (x, y) {
+ destCtx.moveTo(x, y);
+ this.__originalMoveTo(x, y);
+ };
+ ctx.lineTo = function (x, y) {
+ destCtx.lineTo(x, y);
+ this.__originalLineTo(x, y);
+ };
+ ctx.bezierCurveTo = function (cp1x, cp1y, cp2x, cp2y, x, y) {
+ destCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+ this.__originalBezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+ };
+ ctx.rect = function (x, y, width, height) {
+ destCtx.rect(x, y, width, height);
+ this.__originalRect(x, y, width, height);
+ };
+ ctx.closePath = function () {
+ destCtx.closePath();
+ this.__originalClosePath();
+ };
+ ctx.beginPath = function () {
+ destCtx.beginPath();
+ this.__originalBeginPath();
+ };
+}
+class CachedCanvases {
+ constructor(canvasFactory) {
+ this.canvasFactory = canvasFactory;
+ this.cache = Object.create(null);
+ }
+ getCanvas(id, width, height) {
+ let canvasEntry;
+ if (this.cache[id] !== undefined) {
+ canvasEntry = this.cache[id];
+ this.canvasFactory.reset(canvasEntry, width, height);
+ } else {
+ canvasEntry = this.canvasFactory.create(width, height);
+ this.cache[id] = canvasEntry;
}
- delete this.pendingTextPaths;
+ return canvasEntry;
}
- setCharSpacing(opIdx, spacing) {
- this.dependencyTracker?.recordSimpleData("charSpacing", opIdx);
- this.current.charSpacing = spacing;
+ delete(id) {
+ delete this.cache[id];
}
- setWordSpacing(opIdx, spacing) {
- this.dependencyTracker?.recordSimpleData("wordSpacing", opIdx);
- this.current.wordSpacing = spacing;
+ clear() {
+ for (const id in this.cache) {
+ const canvasEntry = this.cache[id];
+ this.canvasFactory.destroy(canvasEntry);
+ delete this.cache[id];
+ }
}
- setHScale(opIdx, scale) {
- this.dependencyTracker?.recordSimpleData("hScale", opIdx);
- this.current.textHScale = scale / 100;
+}
+function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) {
+ const [a, b, c, d, tx, ty] = getCurrentTransform(ctx);
+ if (b === 0 && c === 0) {
+ const tlX = destX * a + tx;
+ const rTlX = Math.round(tlX);
+ const tlY = destY * d + ty;
+ const rTlY = Math.round(tlY);
+ const brX = (destX + destW) * a + tx;
+ const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
+ const brY = (destY + destH) * d + ty;
+ const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
+ ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY);
+ ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight);
+ ctx.setTransform(a, b, c, d, tx, ty);
+ return [rWidth, rHeight];
}
- setLeading(opIdx, leading) {
- this.dependencyTracker?.recordSimpleData("leading", opIdx);
- this.current.leading = -leading;
+ if (a === 0 && d === 0) {
+ const tlX = destY * c + tx;
+ const rTlX = Math.round(tlX);
+ const tlY = destX * b + ty;
+ const rTlY = Math.round(tlY);
+ const brX = (destY + destH) * c + tx;
+ const rWidth = Math.abs(Math.round(brX) - rTlX) || 1;
+ const brY = (destX + destW) * b + ty;
+ const rHeight = Math.abs(Math.round(brY) - rTlY) || 1;
+ ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY);
+ ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth);
+ ctx.setTransform(a, b, c, d, tx, ty);
+ return [rHeight, rWidth];
}
- setFont(opIdx, fontRefName, size) {
- this.dependencyTracker?.recordSimpleData("font", opIdx).recordSimpleDataFromNamed("fontObj", fontRefName, opIdx);
- const fontObj = this.commonObjs.get(fontRefName);
- const current = this.current;
- if (!fontObj) {
- throw new Error(`Can't find font for ${fontRefName}`);
- }
- current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX;
- if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
- warn("Invalid font matrix for font " + fontRefName);
- }
- if (size < 0) {
- size = -size;
- current.fontDirection = -1;
- } else {
- current.fontDirection = 1;
- }
- this.current.font = fontObj;
- this.current.fontSize = size;
- if (fontObj.isType3Font) {
- return;
- }
- const name = fontObj.loadedName || "sans-serif";
- const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`;
- let bold = "normal";
- if (fontObj.black) {
- bold = "900";
- } else if (fontObj.bold) {
- bold = "bold";
- }
- const italic = fontObj.italic ? "italic" : "normal";
- let browserFontSize = size;
- if (size < MIN_FONT_SIZE) {
- browserFontSize = MIN_FONT_SIZE;
- } else if (size > MAX_FONT_SIZE) {
- browserFontSize = MAX_FONT_SIZE;
- }
- this.current.fontSizeScale = size / browserFontSize;
- this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;
+ ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
+ const scaleX = Math.hypot(a, b);
+ const scaleY = Math.hypot(c, d);
+ return [scaleX * destW, scaleY * destH];
+}
+class CanvasExtraState {
+ alphaIsShape = false;
+ fontSize = 0;
+ fontSizeScale = 1;
+ textMatrix = null;
+ textMatrixScale = 1;
+ fontMatrix = FONT_IDENTITY_MATRIX;
+ leading = 0;
+ x = 0;
+ y = 0;
+ lineX = 0;
+ lineY = 0;
+ charSpacing = 0;
+ wordSpacing = 0;
+ textHScale = 1;
+ textRenderingMode = TextRenderingMode.FILL;
+ textRise = 0;
+ fillColor = "#000000";
+ strokeColor = "#000000";
+ patternFill = false;
+ patternStroke = false;
+ fillAlpha = 1;
+ strokeAlpha = 1;
+ lineWidth = 1;
+ activeSMask = null;
+ transferMaps = "none";
+ constructor(width, height, preInit) {
+ preInit?.(this);
+ this.clipBox = new Float32Array([0, 0, width, height]);
+ this.minMax = MIN_MAX_INIT.slice();
}
- setTextRenderingMode(opIdx, mode) {
- this.dependencyTracker?.recordSimpleData("textRenderingMode", opIdx);
- this.current.textRenderingMode = mode;
+ clone() {
+ const clone = Object.create(this);
+ clone.clipBox = this.clipBox.slice();
+ clone.minMax = this.minMax.slice();
+ return clone;
}
- setTextRise(opIdx, rise) {
- this.dependencyTracker?.recordSimpleData("textRise", opIdx);
- this.current.textRise = rise;
+ getPathBoundingBox(pathType = PathType.FILL, transform = null) {
+ const box = this.minMax.slice();
+ if (pathType === PathType.STROKE) {
+ if (!transform) {
+ unreachable("Stroke bounding box must include transform.");
+ }
+ Util.singularValueDecompose2dScale(transform, XY);
+ const xStrokePad = XY[0] * this.lineWidth / 2;
+ const yStrokePad = XY[1] * this.lineWidth / 2;
+ box[0] -= xStrokePad;
+ box[1] -= yStrokePad;
+ box[2] += xStrokePad;
+ box[3] += yStrokePad;
+ }
+ return box;
}
- moveText(opIdx, x, y) {
- this.dependencyTracker?.resetIncrementalData("sameLineText").recordIncrementalData("moveText", opIdx);
- this.current.x = this.current.lineX += x;
- this.current.y = this.current.lineY += y;
+ updateClipFromPath() {
+ const intersect = Util.intersect(this.clipBox, this.getPathBoundingBox());
+ this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]);
}
- setLeadingMoveText(opIdx, x, y) {
- this.setLeading(opIdx, -y);
- this.moveText(opIdx, x, y);
+ isEmptyClip() {
+ return this.minMax[0] === Infinity;
}
- setTextMatrix(opIdx, matrix) {
- this.dependencyTracker?.recordSimpleData("textMatrix", opIdx);
- const {
- current
- } = this;
- current.textMatrix = matrix;
- current.textMatrixScale = Math.hypot(matrix[0], matrix[1]);
- current.x = current.lineX = 0;
- current.y = current.lineY = 0;
+ startNewPathAndClipBox(box) {
+ this.clipBox.set(box, 0);
+ this.minMax.set(MIN_MAX_INIT, 0);
}
- nextLine(opIdx) {
- this.moveText(opIdx, 0, this.current.leading);
- this.dependencyTracker?.recordIncrementalData("moveText", this.dependencyTracker.getSimpleIndex("leading") ?? opIdx);
+ getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
+ return Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform));
}
- #getScaledPath(path, currentTransform, transform) {
- const newPath = new Path2D();
- newPath.addPath(path, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform));
- return newPath;
+}
+function putBinaryImageData(ctx, imgData) {
+ if (imgData instanceof ImageData) {
+ ctx.putImageData(imgData, 0, 0);
+ return;
}
- paintChar(opIdx, character, x, y, patternFillTransform, patternStrokeTransform) {
- const ctx = this.ctx;
- const current = this.current;
- const font = current.font;
- const textRenderingMode = current.textRenderingMode;
- const fontSize = current.fontSize / current.fontSizeScale;
- const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
- const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);
- const patternFill = current.patternFill && !font.missingFile;
- const patternStroke = current.patternStroke && !font.missingFile;
- let path;
- if ((font.disableFontFace || isAddToPathSet || patternFill || patternStroke) && !font.missingFile) {
- path = font.getPathGenerator(this.commonObjs, character);
- }
- if (path && (font.disableFontFace || patternFill || patternStroke)) {
- ctx.save();
- ctx.translate(x, y);
- ctx.scale(fontSize, -fontSize);
- this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font);
- let currentTransform;
- if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
- if (patternFillTransform) {
- currentTransform = ctx.getTransform();
- ctx.setTransform(...patternFillTransform);
- const scaledPath = this.#getScaledPath(path, currentTransform, patternFillTransform);
- ctx.fill(scaledPath);
- } else {
- ctx.fill(path);
+ const height = imgData.height,
+ width = imgData.width;
+ const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
+ const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
+ const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
+ const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
+ let srcPos = 0,
+ destPos;
+ const src = imgData.data;
+ const dest = chunkImgData.data;
+ let i, j, thisChunkHeight, elemsInThisChunk;
+ if (imgData.kind === util_ImageKind.GRAYSCALE_1BPP) {
+ const srcLength = src.byteLength;
+ const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2);
+ const dest32DataLength = dest32.length;
+ const fullSrcDiff = width + 7 >> 3;
+ const white = 0xffffffff;
+ const black = util_FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff;
+ for (i = 0; i < totalChunks; i++) {
+ thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
+ destPos = 0;
+ for (j = 0; j < thisChunkHeight; j++) {
+ const srcDiff = srcLength - srcPos;
+ let k = 0;
+ const kEnd = srcDiff > fullSrcDiff ? width : srcDiff * 8 - 7;
+ const kEndUnrolled = kEnd & ~7;
+ let mask = 0;
+ let srcByte = 0;
+ for (; k < kEndUnrolled; k += 8) {
+ srcByte = src[srcPos++];
+ dest32[destPos++] = srcByte & 128 ? white : black;
+ dest32[destPos++] = srcByte & 64 ? white : black;
+ dest32[destPos++] = srcByte & 32 ? white : black;
+ dest32[destPos++] = srcByte & 16 ? white : black;
+ dest32[destPos++] = srcByte & 8 ? white : black;
+ dest32[destPos++] = srcByte & 4 ? white : black;
+ dest32[destPos++] = srcByte & 2 ? white : black;
+ dest32[destPos++] = srcByte & 1 ? white : black;
}
- }
- if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
- if (patternStrokeTransform) {
- currentTransform ||= ctx.getTransform();
- ctx.setTransform(...patternStrokeTransform);
- const {
- a,
- b,
- c,
- d
- } = currentTransform;
- const invPatternTransform = Util.inverseTransform(patternStrokeTransform);
- const transf = Util.transform([a, b, c, d, 0, 0], invPatternTransform);
- Util.singularValueDecompose2dScale(transf, XY);
- ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize;
- ctx.stroke(this.#getScaledPath(path, currentTransform, patternStrokeTransform));
- } else {
- ctx.lineWidth /= fontSize;
- ctx.stroke(path);
+ for (; k < kEnd; k++) {
+ if (mask === 0) {
+ srcByte = src[srcPos++];
+ mask = 128;
+ }
+ dest32[destPos++] = srcByte & mask ? white : black;
+ mask >>= 1;
}
}
- ctx.restore();
- } else {
- if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
- ctx.fillText(character, x, y);
- this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character));
- }
- if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
- if (this.dependencyTracker) {
- this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character)).recordDependencies(opIdx, Dependencies.stroke);
- }
- ctx.strokeText(character, x, y);
+ while (destPos < dest32DataLength) {
+ dest32[destPos++] = 0;
}
+ ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
}
- if (isAddToPathSet) {
- const paths = this.pendingTextPaths ||= [];
- paths.push({
- transform: getCurrentTransform(ctx),
- x,
- y,
- fontSize,
- path
- });
- this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y);
+ } else if (imgData.kind === util_ImageKind.RGBA_32BPP) {
+ j = 0;
+ elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4;
+ for (i = 0; i < fullChunks; i++) {
+ dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
+ srcPos += elemsInThisChunk;
+ ctx.putImageData(chunkImgData, 0, j);
+ j += FULL_CHUNK_HEIGHT;
}
- }
- get isFontSubpixelAAEnabled() {
- const {
- context: ctx
- } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10);
- ctx.scale(1.5, 1);
- ctx.fillText("I", 0, 10);
- const data = ctx.getImageData(0, 0, 10, 10).data;
- let enabled = false;
- for (let i = 3; i < data.length; i += 4) {
- if (data[i] > 0 && data[i] < 255) {
- enabled = true;
- break;
- }
+ if (i < totalChunks) {
+ elemsInThisChunk = width * partialChunkHeight * 4;
+ dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
+ ctx.putImageData(chunkImgData, 0, j);
}
- return shadow(this, "isFontSubpixelAAEnabled", enabled);
- }
- showText(opIdx, glyphs) {
- if (this.dependencyTracker) {
- this.dependencyTracker.recordDependencies(opIdx, Dependencies.showText).resetBBox(opIdx);
- if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
- this.dependencyTracker.recordFutureForcedDependency("textClip", opIdx).inheritPendingDependenciesAsFutureForcedDependencies();
+ } else if (imgData.kind === util_ImageKind.RGB_24BPP) {
+ thisChunkHeight = FULL_CHUNK_HEIGHT;
+ elemsInThisChunk = width * thisChunkHeight;
+ for (i = 0; i < totalChunks; i++) {
+ if (i >= fullChunks) {
+ thisChunkHeight = partialChunkHeight;
+ elemsInThisChunk = width * thisChunkHeight;
}
+ destPos = 0;
+ for (j = elemsInThisChunk; j--;) {
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = 255;
+ }
+ ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
}
- const current = this.current;
- const font = current.font;
- if (font.isType3Font) {
- this.showType3Text(opIdx, glyphs);
- this.dependencyTracker?.recordShowTextOperation(opIdx);
- return undefined;
- }
- const fontSize = current.fontSize;
- if (fontSize === 0) {
- this.dependencyTracker?.recordOperation(opIdx);
- return undefined;
- }
- const ctx = this.ctx;
- const fontSizeScale = current.fontSizeScale;
- const charSpacing = current.charSpacing;
- const wordSpacing = current.wordSpacing;
- const fontDirection = current.fontDirection;
- const textHScale = current.textHScale * fontDirection;
- const glyphsLength = glyphs.length;
- const vertical = font.vertical;
- const spacingDir = vertical ? 1 : -1;
- const defaultVMetrics = font.defaultVMetrics;
- const widthAdvanceScale = fontSize * current.fontMatrix[0];
- const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
- ctx.save();
- if (current.textMatrix) {
- ctx.transform(...current.textMatrix);
- }
- ctx.translate(current.x, current.y + current.textRise);
- if (fontDirection > 0) {
- ctx.scale(textHScale, -1);
- } else {
- ctx.scale(textHScale, 1);
+ } else {
+ throw new Error(`bad image kind: ${imgData.kind}`);
+ }
+}
+function putBinaryImageMask(ctx, imgData) {
+ if (imgData.bitmap) {
+ ctx.drawImage(imgData.bitmap, 0, 0);
+ return;
+ }
+ const height = imgData.height,
+ width = imgData.width;
+ const partialChunkHeight = height % FULL_CHUNK_HEIGHT;
+ const fullChunks = (height - partialChunkHeight) / FULL_CHUNK_HEIGHT;
+ const totalChunks = partialChunkHeight === 0 ? fullChunks : fullChunks + 1;
+ const chunkImgData = ctx.createImageData(width, FULL_CHUNK_HEIGHT);
+ let srcPos = 0;
+ const src = imgData.data;
+ const dest = chunkImgData.data;
+ for (let i = 0; i < totalChunks; i++) {
+ const thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight;
+ ({
+ srcPos
+ } = convertBlackAndWhiteToRGBA({
+ src,
+ srcPos,
+ dest,
+ width,
+ height: thisChunkHeight,
+ nonBlackColor: 0
+ }));
+ ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT);
+ }
+}
+function copyCtxState(sourceCtx, destCtx) {
+ const properties = ["strokeStyle", "fillStyle", "fillRule", "globalAlpha", "lineWidth", "lineCap", "lineJoin", "miterLimit", "globalCompositeOperation", "font", "filter"];
+ for (const property of properties) {
+ if (sourceCtx[property] !== undefined) {
+ destCtx[property] = sourceCtx[property];
}
- let patternFillTransform, patternStrokeTransform;
- if (current.patternFill) {
- ctx.save();
- const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx);
- patternFillTransform = getCurrentTransform(ctx);
- ctx.restore();
- ctx.fillStyle = pattern;
+ }
+ if (sourceCtx.setLineDash !== undefined) {
+ destCtx.setLineDash(sourceCtx.getLineDash());
+ destCtx.lineDashOffset = sourceCtx.lineDashOffset;
+ }
+}
+function resetCtxToDefault(ctx) {
+ ctx.strokeStyle = ctx.fillStyle = "#000000";
+ ctx.fillRule = "nonzero";
+ ctx.globalAlpha = 1;
+ ctx.lineWidth = 1;
+ ctx.lineCap = "butt";
+ ctx.lineJoin = "miter";
+ ctx.miterLimit = 10;
+ ctx.globalCompositeOperation = "source-over";
+ ctx.font = "10px sans-serif";
+ if (ctx.setLineDash !== undefined) {
+ ctx.setLineDash([]);
+ ctx.lineDashOffset = 0;
+ }
+ const {
+ filter
+ } = ctx;
+ if (filter !== "none" && filter !== "") {
+ ctx.filter = "none";
+ }
+}
+function getImageSmoothingEnabled(transform, interpolate) {
+ if (interpolate) {
+ return true;
+ }
+ Util.singularValueDecompose2dScale(transform, XY);
+ const actualScale = Math.fround(OutputScale.pixelRatio * PixelsPerInch.PDF_TO_CSS_UNITS);
+ return XY[0] <= actualScale && XY[1] <= actualScale;
+}
+const LINE_CAP_STYLES = ["butt", "round", "square"];
+const LINE_JOIN_STYLES = ["miter", "round", "bevel"];
+const NORMAL_CLIP = {};
+const EO_CLIP = {};
+class CanvasGraphics {
+ constructor(canvasCtx, commonObjs, objs, canvasFactory, filterFactory, {
+ optionalContentConfig,
+ markedContentStack = null
+ }, annotationCanvasMap, pageColors, dependencyTracker) {
+ this.ctx = canvasCtx;
+ this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
+ this.stateStack = [];
+ this.pendingClip = null;
+ this.pendingEOFill = false;
+ this.res = null;
+ this.xobjs = null;
+ this.commonObjs = commonObjs;
+ this.objs = objs;
+ this.canvasFactory = canvasFactory;
+ this.filterFactory = filterFactory;
+ this.groupStack = [];
+ this.baseTransform = null;
+ this.baseTransformStack = [];
+ this.groupLevel = 0;
+ this.smaskStack = [];
+ this.smaskCounter = 0;
+ this.tempSMask = null;
+ this.suspendedCtx = null;
+ this.contentVisible = true;
+ this.markedContentStack = markedContentStack || [];
+ this.optionalContentConfig = optionalContentConfig;
+ this.cachedCanvases = new CachedCanvases(this.canvasFactory);
+ this.cachedPatterns = new Map();
+ this.annotationCanvasMap = annotationCanvasMap;
+ this.viewportScale = 1;
+ this.outputScaleX = 1;
+ this.outputScaleY = 1;
+ this.pageColors = pageColors;
+ this._cachedScaleForStroking = [-1, 0];
+ this._cachedGetSinglePixelWidth = null;
+ this._cachedBitmapsMap = new Map();
+ this.dependencyTracker = dependencyTracker ?? null;
+ }
+ getObject(opIdx, data, fallback = null) {
+ if (typeof data === "string") {
+ this.dependencyTracker?.recordNamedDependency(opIdx, data);
+ return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data);
}
- if (current.patternStroke) {
- ctx.save();
- const pattern = current.strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx);
- patternStrokeTransform = getCurrentTransform(ctx);
- ctx.restore();
- ctx.strokeStyle = pattern;
+ return fallback;
+ }
+ beginDrawing({
+ transform,
+ viewport,
+ transparency = false,
+ background = null
+ }) {
+ const width = this.ctx.canvas.width;
+ const height = this.ctx.canvas.height;
+ const savedFillStyle = this.ctx.fillStyle;
+ this.ctx.fillStyle = background || "#ffffff";
+ this.ctx.fillRect(0, 0, width, height);
+ this.ctx.fillStyle = savedFillStyle;
+ if (transparency) {
+ const transparentCanvas = this.cachedCanvases.getCanvas("transparent", width, height);
+ this.compositeCtx = this.ctx;
+ this.transparentCanvas = transparentCanvas.canvas;
+ this.ctx = transparentCanvas.context;
+ this.ctx.save();
+ this.ctx.transform(...getCurrentTransform(this.compositeCtx));
}
- let lineWidth = current.lineWidth;
- const scale = current.textMatrixScale;
- if (scale === 0 || lineWidth === 0) {
- const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
- if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
- lineWidth = this.getSinglePixelWidth();
- }
- } else {
- lineWidth /= scale;
+ this.ctx.save();
+ resetCtxToDefault(this.ctx);
+ if (transform) {
+ this.ctx.transform(...transform);
+ this.outputScaleX = transform[0];
+ this.outputScaleY = transform[0];
}
- if (fontSizeScale !== 1.0) {
- ctx.scale(fontSizeScale, fontSizeScale);
- lineWidth /= fontSizeScale;
+ this.ctx.transform(...viewport.transform);
+ this.viewportScale = viewport.scale;
+ this.baseTransform = getCurrentTransform(this.ctx);
+ }
+ executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper, operationsFilter) {
+ const argsArray = operatorList.argsArray;
+ const fnArray = operatorList.fnArray;
+ let i = executionStartIdx || 0;
+ const argsArrayLen = argsArray.length;
+ if (argsArrayLen === i) {
+ return i;
}
- ctx.lineWidth = lineWidth;
- if (font.isInvalidPDFjsFont) {
- const chars = [];
- let width = 0;
- for (const glyph of glyphs) {
- chars.push(glyph.unicode);
- width += glyph.width;
- }
- const joinedChars = chars.join("");
- ctx.fillText(joinedChars, 0, 0);
- if (this.dependencyTracker !== null) {
- const measure = ctx.measureText(joinedChars);
- this.dependencyTracker.recordBBox(opIdx, this.ctx, -measure.actualBoundingBoxLeft, measure.actualBoundingBoxRight, -measure.actualBoundingBoxAscent, measure.actualBoundingBoxDescent).recordShowTextOperation(opIdx);
+ const chunkOperations = argsArrayLen - i > EXECUTION_STEPS && typeof continueCallback === "function";
+ const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;
+ let steps = 0;
+ const commonObjs = this.commonObjs;
+ const objs = this.objs;
+ let fnId, fnArgs;
+ while (true) {
+ if (stepper !== undefined && i === stepper.nextBreakPoint) {
+ stepper.breakIt(i, continueCallback);
+ return i;
}
- current.x += width * widthAdvanceScale * textHScale;
- ctx.restore();
- this.compose();
- return undefined;
- }
- let x = 0,
- i;
- for (i = 0; i < glyphsLength; ++i) {
- const glyph = glyphs[i];
- if (typeof glyph === "number") {
- x += spacingDir * glyph * fontSize / 1000;
- continue;
+ if (!operationsFilter || operationsFilter(i)) {
+ fnId = fnArray[i];
+ fnArgs = argsArray[i] ?? null;
+ if (fnId !== OPS.dependency) {
+ if (fnArgs === null) {
+ this[fnId](i);
+ } else {
+ this[fnId](i, ...fnArgs);
+ }
+ } else {
+ for (const depObjId of fnArgs) {
+ this.dependencyTracker?.recordNamedData(depObjId, i);
+ const objsPool = depObjId.startsWith("g_") ? commonObjs : objs;
+ if (!objsPool.has(depObjId)) {
+ objsPool.get(depObjId, continueCallback);
+ return i;
+ }
+ }
+ }
}
- let restoreNeeded = false;
- const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
- const character = glyph.fontChar;
- const accent = glyph.accent;
- let scaledX, scaledY;
- let width = glyph.width;
- if (vertical) {
- const vmetric = glyph.vmetric || defaultVMetrics;
- const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale;
- const vy = vmetric[2] * widthAdvanceScale;
- width = vmetric ? -vmetric[0] : width;
- scaledX = vx / fontSizeScale;
- scaledY = (x + vy) / fontSizeScale;
- } else {
- scaledX = x / fontSizeScale;
- scaledY = 0;
+ i++;
+ if (i === argsArrayLen) {
+ return i;
}
- let measure;
- if (font.remeasure && width > 0) {
- measure = ctx.measureText(character);
- const measuredWidth = measure.width * 1000 / fontSize * fontSizeScale;
- if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
- const characterScaleX = width / measuredWidth;
- restoreNeeded = true;
- ctx.save();
- ctx.scale(characterScaleX, 1);
- scaledX /= characterScaleX;
- } else if (width !== measuredWidth) {
- scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale;
+ if (chunkOperations && ++steps > EXECUTION_STEPS) {
+ if (Date.now() > endTime) {
+ continueCallback();
+ return i;
}
+ steps = 0;
}
- if (this.contentVisible && (glyph.isInFont || font.missingFile)) {
- if (simpleFillText && !accent) {
- ctx.fillText(character, scaledX, scaledY);
- this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, measure ? {
- bbox: null
- } : font, fontSize / fontSizeScale, scaledX, scaledY, () => measure ?? ctx.measureText(character));
- } else {
- this.paintChar(opIdx, character, scaledX, scaledY, patternFillTransform, patternStrokeTransform);
- if (accent) {
- const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale;
- const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale;
- this.paintChar(opIdx, accent.fontChar, scaledAccentX, scaledAccentY, patternFillTransform, patternStrokeTransform);
- }
+ }
+ }
+ #restoreInitialState() {
+ while (this.stateStack.length || this.inSMaskMode) {
+ this.restore();
+ }
+ this.current.activeSMask = null;
+ this.ctx.restore();
+ if (this.transparentCanvas) {
+ this.ctx = this.compositeCtx;
+ this.ctx.save();
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
+ this.ctx.drawImage(this.transparentCanvas, 0, 0);
+ this.ctx.restore();
+ this.transparentCanvas = null;
+ }
+ }
+ endDrawing() {
+ this.#restoreInitialState();
+ this.cachedCanvases.clear();
+ this.cachedPatterns.clear();
+ for (const cache of this._cachedBitmapsMap.values()) {
+ for (const canvas of cache.values()) {
+ if (typeof HTMLCanvasElement !== "undefined" && canvas instanceof HTMLCanvasElement) {
+ canvas.width = canvas.height = 0;
}
}
- const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;
- x += charWidth;
- if (restoreNeeded) {
- ctx.restore();
+ cache.clear();
+ }
+ this._cachedBitmapsMap.clear();
+ this.#drawFilter();
+ }
+ #drawFilter() {
+ if (this.pageColors) {
+ const hcmFilterId = this.filterFactory.addHCMFilter(this.pageColors.foreground, this.pageColors.background);
+ if (hcmFilterId !== "none") {
+ const savedFilter = this.ctx.filter;
+ this.ctx.filter = hcmFilterId;
+ this.ctx.drawImage(this.ctx.canvas, 0, 0);
+ this.ctx.filter = savedFilter;
}
}
- if (vertical) {
- current.y -= x;
- } else {
- current.x += x * textHScale;
+ }
+ _scaleImage(img, inverseTransform) {
+ const width = img.width ?? img.displayWidth;
+ const height = img.height ?? img.displayHeight;
+ let widthScale = Math.max(Math.hypot(inverseTransform[0], inverseTransform[1]), 1);
+ let heightScale = Math.max(Math.hypot(inverseTransform[2], inverseTransform[3]), 1);
+ let paintWidth = width,
+ paintHeight = height;
+ let tmpCanvasId = "prescale1";
+ let tmpCanvas, tmpCtx;
+ while (widthScale > 2 && paintWidth > 1 || heightScale > 2 && paintHeight > 1) {
+ let newWidth = paintWidth,
+ newHeight = paintHeight;
+ if (widthScale > 2 && paintWidth > 1) {
+ newWidth = paintWidth >= 16384 ? Math.floor(paintWidth / 2) - 1 || 1 : Math.ceil(paintWidth / 2);
+ widthScale /= paintWidth / newWidth;
+ }
+ if (heightScale > 2 && paintHeight > 1) {
+ newHeight = paintHeight >= 16384 ? Math.floor(paintHeight / 2) - 1 || 1 : Math.ceil(paintHeight) / 2;
+ heightScale /= paintHeight / newHeight;
+ }
+ tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight);
+ tmpCtx = tmpCanvas.context;
+ tmpCtx.clearRect(0, 0, newWidth, newHeight);
+ tmpCtx.drawImage(img, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight);
+ img = tmpCanvas.canvas;
+ paintWidth = newWidth;
+ paintHeight = newHeight;
+ tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";
}
- ctx.restore();
- this.compose();
- this.dependencyTracker?.recordShowTextOperation(opIdx);
- return undefined;
+ return {
+ img,
+ paintWidth,
+ paintHeight
+ };
}
- showType3Text(opIdx, glyphs) {
+ _createMaskCanvas(opIdx, img) {
const ctx = this.ctx;
- const current = this.current;
- const font = current.font;
- const fontSize = current.fontSize;
- const fontDirection = current.fontDirection;
- const spacingDir = font.vertical ? 1 : -1;
- const charSpacing = current.charSpacing;
- const wordSpacing = current.wordSpacing;
- const textHScale = current.textHScale * fontDirection;
- const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
- const glyphsLength = glyphs.length;
- const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE;
- let i, glyph, width, spacingLength;
- if (isTextInvisible || fontSize === 0) {
- return;
+ const {
+ width,
+ height
+ } = img;
+ const fillColor = this.current.fillColor;
+ const isPatternFill = this.current.patternFill;
+ const currentTransform = getCurrentTransform(ctx);
+ let cache, cacheKey, scaled, maskCanvas;
+ if ((img.bitmap || img.data) && img.count > 1) {
+ const mainKey = img.bitmap || img.data.buffer;
+ cacheKey = JSON.stringify(isPatternFill ? currentTransform : [currentTransform.slice(0, 4), fillColor]);
+ cache = this._cachedBitmapsMap.get(mainKey);
+ if (!cache) {
+ cache = new Map();
+ this._cachedBitmapsMap.set(mainKey, cache);
+ }
+ const cachedImage = cache.get(cacheKey);
+ if (cachedImage && !isPatternFill) {
+ const offsetX = Math.round(Math.min(currentTransform[0], currentTransform[2]) + currentTransform[4]);
+ const offsetY = Math.round(Math.min(currentTransform[1], currentTransform[3]) + currentTransform[5]);
+ this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill);
+ return {
+ canvas: cachedImage,
+ offsetX,
+ offsetY
+ };
+ }
+ scaled = cachedImage;
}
- this._cachedScaleForStroking[0] = -1;
- this._cachedGetSinglePixelWidth = null;
- ctx.save();
- if (current.textMatrix) {
- ctx.transform(...current.textMatrix);
+ if (!scaled) {
+ maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
+ putBinaryImageMask(maskCanvas.context, img);
}
- ctx.translate(current.x, current.y + current.textRise);
- ctx.scale(textHScale, fontDirection);
- const dependencyTracker = this.dependencyTracker;
- this.dependencyTracker = dependencyTracker ? new CanvasNestedDependencyTracker(dependencyTracker, opIdx) : null;
- for (i = 0; i < glyphsLength; ++i) {
- glyph = glyphs[i];
- if (typeof glyph === "number") {
- spacingLength = spacingDir * glyph * fontSize / 1000;
- this.ctx.translate(spacingLength, 0);
- current.x += spacingLength * textHScale;
- continue;
- }
- const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
- const operatorList = font.charProcOperatorList[glyph.operatorListId];
- if (!operatorList) {
- warn(`Type3 character "${glyph.operatorListId}" is not available.`);
- } else if (this.contentVisible) {
- this.save();
- ctx.scale(fontSize, fontSize);
- ctx.transform(...fontMatrix);
- this.executeOperatorList(operatorList);
- this.restore();
+ let maskToCanvas = Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]);
+ maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
+ const minMax = MIN_MAX_INIT.slice();
+ Util.axialAlignedBoundingBox([0, 0, width, height], maskToCanvas, minMax);
+ const [minX, minY, maxX, maxY] = minMax;
+ const drawnWidth = Math.round(maxX - minX) || 1;
+ const drawnHeight = Math.round(maxY - minY) || 1;
+ const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight);
+ const fillCtx = fillCanvas.context;
+ const offsetX = minX;
+ const offsetY = minY;
+ fillCtx.translate(-offsetX, -offsetY);
+ fillCtx.transform(...maskToCanvas);
+ if (!scaled) {
+ scaled = this._scaleImage(maskCanvas.canvas, getCurrentTransformInverse(fillCtx));
+ scaled = scaled.img;
+ if (cache && isPatternFill) {
+ cache.set(cacheKey, scaled);
}
- const p = [glyph.width, 0];
- Util.applyTransform(p, fontMatrix);
- width = p[0] * fontSize + spacing;
- ctx.translate(width, 0);
- current.x += width * textHScale;
}
- ctx.restore();
- if (dependencyTracker) {
- this.dependencyTracker = dependencyTracker;
+ fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(fillCtx), img.interpolate);
+ drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height);
+ fillCtx.globalCompositeOperation = "source-in";
+ const inverse = Util.transform(getCurrentTransformInverse(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]);
+ fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL, opIdx) : fillColor;
+ fillCtx.fillRect(0, 0, width, height);
+ if (cache && !isPatternFill) {
+ this.cachedCanvases.delete("fillCanvas");
+ cache.set(cacheKey, fillCanvas.canvas);
}
+ this.dependencyTracker?.recordDependencies(opIdx, Dependencies.transformAndFill);
+ return {
+ canvas: fillCanvas.canvas,
+ offsetX: Math.round(offsetX),
+ offsetY: Math.round(offsetY)
+ };
}
- setCharWidth(opIdx, xWidth, yWidth) {}
- setCharWidthAndBounds(opIdx, xWidth, yWidth, llx, lly, urx, ury) {
- const clip = new Path2D();
- clip.rect(llx, lly, urx - llx, ury - lly);
- this.ctx.clip(clip);
- this.dependencyTracker?.recordBBox(opIdx, this.ctx, llx, urx, lly, ury).recordClipBox(opIdx, this.ctx, llx, urx, lly, ury);
- this.endPath(opIdx);
- }
- getColorN_Pattern(opIdx, IR) {
- let pattern;
- if (IR[0] === "TilingPattern") {
- const baseTransform = this.baseTransform || getCurrentTransform(this.ctx);
- const canvasGraphicsFactory = {
- createCanvasGraphics: (ctx, renderingOpIdx) => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {
- optionalContentConfig: this.optionalContentConfig,
- markedContentStack: this.markedContentStack
- }, undefined, undefined, this.dependencyTracker ? new CanvasNestedDependencyTracker(this.dependencyTracker, renderingOpIdx, true) : null)
- };
- pattern = new TilingPattern(IR, this.ctx, canvasGraphicsFactory, baseTransform);
- } else {
- pattern = this._getPattern(opIdx, IR[1], IR[2]);
+ setLineWidth(opIdx, width) {
+ this.dependencyTracker?.recordSimpleData("lineWidth", opIdx);
+ if (width !== this.current.lineWidth) {
+ this._cachedScaleForStroking[0] = -1;
}
- return pattern;
- }
- setStrokeColorN(opIdx, ...args) {
- this.dependencyTracker?.recordSimpleData("strokeColor", opIdx);
- this.current.strokeColor = this.getColorN_Pattern(opIdx, args);
- this.current.patternStroke = true;
- }
- setFillColorN(opIdx, ...args) {
- this.dependencyTracker?.recordSimpleData("fillColor", opIdx);
- this.current.fillColor = this.getColorN_Pattern(opIdx, args);
- this.current.patternFill = true;
- }
- setStrokeRGBColor(opIdx, color) {
- this.dependencyTracker?.recordSimpleData("strokeColor", opIdx);
- this.ctx.strokeStyle = this.current.strokeColor = color;
- this.current.patternStroke = false;
+ this.current.lineWidth = width;
+ this.ctx.lineWidth = width;
}
- setStrokeTransparent(opIdx) {
- this.dependencyTracker?.recordSimpleData("strokeColor", opIdx);
- this.ctx.strokeStyle = this.current.strokeColor = "transparent";
- this.current.patternStroke = false;
+ setLineCap(opIdx, style) {
+ this.dependencyTracker?.recordSimpleData("lineCap", opIdx);
+ this.ctx.lineCap = LINE_CAP_STYLES[style];
}
- setFillRGBColor(opIdx, color) {
- this.dependencyTracker?.recordSimpleData("fillColor", opIdx);
- this.ctx.fillStyle = this.current.fillColor = color;
- this.current.patternFill = false;
+ setLineJoin(opIdx, style) {
+ this.dependencyTracker?.recordSimpleData("lineJoin", opIdx);
+ this.ctx.lineJoin = LINE_JOIN_STYLES[style];
}
- setFillTransparent(opIdx) {
- this.dependencyTracker?.recordSimpleData("fillColor", opIdx);
- this.ctx.fillStyle = this.current.fillColor = "transparent";
- this.current.patternFill = false;
+ setMiterLimit(opIdx, limit) {
+ this.dependencyTracker?.recordSimpleData("miterLimit", opIdx);
+ this.ctx.miterLimit = limit;
}
- _getPattern(opIdx, objId, matrix = null) {
- let pattern;
- if (this.cachedPatterns.has(objId)) {
- pattern = this.cachedPatterns.get(objId);
- } else {
- pattern = getShadingPattern(this.getObject(opIdx, objId));
- this.cachedPatterns.set(objId, pattern);
- }
- if (matrix) {
- pattern.matrix = matrix;
+ setDash(opIdx, dashArray, dashPhase) {
+ this.dependencyTracker?.recordSimpleData("dash", opIdx);
+ const ctx = this.ctx;
+ if (ctx.setLineDash !== undefined) {
+ ctx.setLineDash(dashArray);
+ ctx.lineDashOffset = dashPhase;
}
- return pattern;
}
- shadingFill(opIdx, objId) {
- if (!this.contentVisible) {
- return;
- }
- const ctx = this.ctx;
- this.save(opIdx);
- const pattern = this._getPattern(opIdx, objId);
- ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING, opIdx);
- const inv = getCurrentTransformInverse(ctx);
- if (inv) {
- const {
- width,
- height
- } = ctx.canvas;
- const minMax = MIN_MAX_INIT.slice();
- Util.axialAlignedBoundingBox([0, 0, width, height], inv, minMax);
- const [x0, y0, x1, y1] = minMax;
- this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
- } else {
- this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
+ setRenderingIntent(opIdx, intent) {}
+ setFlatness(opIdx, flatness) {}
+ setGState(opIdx, states) {
+ for (const [key, value] of states) {
+ switch (key) {
+ case "LW":
+ this.setLineWidth(opIdx, value);
+ break;
+ case "LC":
+ this.setLineCap(opIdx, value);
+ break;
+ case "LJ":
+ this.setLineJoin(opIdx, value);
+ break;
+ case "ML":
+ this.setMiterLimit(opIdx, value);
+ break;
+ case "D":
+ this.setDash(opIdx, value[0], value[1]);
+ break;
+ case "RI":
+ this.setRenderingIntent(opIdx, value);
+ break;
+ case "FL":
+ this.setFlatness(opIdx, value);
+ break;
+ case "Font":
+ this.setFont(opIdx, value[0], value[1]);
+ break;
+ case "CA":
+ this.dependencyTracker?.recordSimpleData("strokeAlpha", opIdx);
+ this.current.strokeAlpha = value;
+ break;
+ case "ca":
+ this.dependencyTracker?.recordSimpleData("fillAlpha", opIdx);
+ this.ctx.globalAlpha = this.current.fillAlpha = value;
+ break;
+ case "BM":
+ this.dependencyTracker?.recordSimpleData("globalCompositeOperation", opIdx);
+ this.ctx.globalCompositeOperation = value;
+ break;
+ case "SMask":
+ this.dependencyTracker?.recordSimpleData("SMask", opIdx);
+ this.current.activeSMask = value ? this.tempSMask : null;
+ this.tempSMask = null;
+ this.checkSMaskState();
+ break;
+ case "TR":
+ this.dependencyTracker?.recordSimpleData("filter", opIdx);
+ this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value);
+ break;
+ }
}
- this.dependencyTracker?.resetBBox(opIdx).recordFullPageBBox(opIdx).recordDependencies(opIdx, Dependencies.transform).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx);
- this.compose(this.current.getClippedPathBoundingBox());
- this.restore(opIdx);
- }
- beginInlineImage() {
- unreachable("Should not call beginInlineImage");
- }
- beginImageData() {
- unreachable("Should not call beginImageData");
}
- paintFormXObjectBegin(opIdx, matrix, bbox) {
- if (!this.contentVisible) {
- return;
- }
- this.save(opIdx);
- this.baseTransformStack.push(this.baseTransform);
- if (matrix) {
- this.transform(opIdx, ...matrix);
- }
- this.baseTransform = getCurrentTransform(this.ctx);
- if (bbox) {
- Util.axialAlignedBoundingBox(bbox, this.baseTransform, this.current.minMax);
- const [x0, y0, x1, y1] = bbox;
- const clip = new Path2D();
- clip.rect(x0, y0, x1 - x0, y1 - y0);
- this.ctx.clip(clip);
- this.dependencyTracker?.recordClipBox(opIdx, this.ctx, x0, x1, y0, y1);
- this.endPath(opIdx);
- }
+ get inSMaskMode() {
+ return !!this.suspendedCtx;
}
- paintFormXObjectEnd(opIdx) {
- if (!this.contentVisible) {
- return;
+ checkSMaskState() {
+ const inSMaskMode = this.inSMaskMode;
+ if (this.current.activeSMask && !inSMaskMode) {
+ this.beginSMaskMode();
+ } else if (!this.current.activeSMask && inSMaskMode) {
+ this.endSMaskMode();
}
- this.restore(opIdx);
- this.baseTransform = this.baseTransformStack.pop();
}
- beginGroup(opIdx, group) {
- if (!this.contentVisible) {
- return;
- }
- this.save(opIdx);
+ beginSMaskMode(opIdx) {
if (this.inSMaskMode) {
- this.endSMaskMode();
- this.current.activeSMask = null;
- }
- const currentCtx = this.ctx;
- if (!group.isolated) {
- info("TODO: Support non-isolated groups.");
- }
- if (group.knockout) {
- warn("Knockout groups not supported.");
- }
- const currentTransform = getCurrentTransform(currentCtx);
- if (group.matrix) {
- currentCtx.transform(...group.matrix);
- }
- if (!group.bbox) {
- throw new Error("Bounding box is required.");
- }
- let bounds = MIN_MAX_INIT.slice();
- Util.axialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx), bounds);
- const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];
- bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
- const offsetX = Math.floor(bounds[0]);
- const offsetY = Math.floor(bounds[1]);
- const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
- const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
- this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);
- let cacheId = "groupAt" + this.groupLevel;
- if (group.smask) {
- cacheId += "_smask_" + this.smaskCounter++ % 2;
+ throw new Error("beginSMaskMode called while already in smask mode");
}
+ const drawnWidth = this.ctx.canvas.width;
+ const drawnHeight = this.ctx.canvas.height;
+ const cacheId = "smaskGroupAt" + this.groupLevel;
const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);
- const groupCtx = scratchCanvas.context;
- groupCtx.translate(-offsetX, -offsetY);
- groupCtx.transform(...currentTransform);
- let clip = new Path2D();
- const [x0, y0, x1, y1] = group.bbox;
- clip.rect(x0, y0, x1 - x0, y1 - y0);
- if (group.matrix) {
- const path = new Path2D();
- path.addPath(clip, new DOMMatrix(group.matrix));
- clip = path;
- }
- groupCtx.clip(clip);
- if (group.smask) {
- this.smaskStack.push({
- canvas: scratchCanvas.canvas,
- context: groupCtx,
- offsetX,
- offsetY,
- subtype: group.smask.subtype,
- backdrop: group.smask.backdrop,
- transferMap: group.smask.transferMap || null,
- startTransformInverse: null
- });
- }
- if (!group.smask || this.dependencyTracker) {
- currentCtx.setTransform(1, 0, 0, 1, 0, 0);
- currentCtx.translate(offsetX, offsetY);
- currentCtx.save();
+ this.suspendedCtx = this.ctx;
+ const ctx = this.ctx = scratchCanvas.context;
+ ctx.setTransform(this.suspendedCtx.getTransform());
+ copyCtxState(this.suspendedCtx, ctx);
+ mirrorContextOperations(ctx, this.suspendedCtx);
+ this.setGState(opIdx, [["BM", "source-over"]]);
+ }
+ endSMaskMode() {
+ if (!this.inSMaskMode) {
+ throw new Error("endSMaskMode called while not in smask mode");
}
- copyCtxState(currentCtx, groupCtx);
- this.ctx = groupCtx;
- this.dependencyTracker?.inheritSimpleDataAsFutureForcedDependencies(["fillAlpha", "strokeAlpha", "globalCompositeOperation"]).pushBaseTransform(currentCtx);
- this.setGState(opIdx, [["BM", "source-over"], ["ca", 1], ["CA", 1]]);
- this.groupStack.push(currentCtx);
- this.groupLevel++;
+ this.ctx._removeMirroring();
+ copyCtxState(this.ctx, this.suspendedCtx);
+ this.ctx = this.suspendedCtx;
+ this.suspendedCtx = null;
}
- endGroup(opIdx, group) {
- if (!this.contentVisible) {
+ compose(dirtyBox) {
+ if (!this.current.activeSMask) {
return;
}
- this.groupLevel--;
- const groupCtx = this.ctx;
- const ctx = this.groupStack.pop();
- this.ctx = ctx;
- this.ctx.imageSmoothingEnabled = false;
- this.dependencyTracker?.popBaseTransform();
- if (group.smask) {
- this.tempSMask = this.smaskStack.pop();
- this.restore(opIdx);
- if (this.dependencyTracker) {
- this.ctx.restore();
- }
+ if (!dirtyBox) {
+ dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];
} else {
- this.ctx.restore();
- const currentMtx = getCurrentTransform(this.ctx);
- this.restore(opIdx);
- this.ctx.save();
- this.ctx.setTransform(...currentMtx);
- const dirtyBox = MIN_MAX_INIT.slice();
- Util.axialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx, dirtyBox);
- this.ctx.drawImage(groupCtx.canvas, 0, 0);
- this.ctx.restore();
- this.compose(dirtyBox);
+ dirtyBox[0] = Math.floor(dirtyBox[0]);
+ dirtyBox[1] = Math.floor(dirtyBox[1]);
+ dirtyBox[2] = Math.ceil(dirtyBox[2]);
+ dirtyBox[3] = Math.ceil(dirtyBox[3]);
}
- }
- beginAnnotation(opIdx, id, rect, transform, matrix, hasOwnCanvas) {
- this.#restoreInitialState();
- resetCtxToDefault(this.ctx);
+ const smask = this.current.activeSMask;
+ const suspendedCtx = this.suspendedCtx;
+ this.composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);
this.ctx.save();
- this.save(opIdx);
- if (this.baseTransform) {
- this.ctx.setTransform(...this.baseTransform);
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
+ this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
+ this.ctx.restore();
+ }
+ composeSMask(ctx, smask, layerCtx, layerBox) {
+ const layerOffsetX = layerBox[0];
+ const layerOffsetY = layerBox[1];
+ const layerWidth = layerBox[2] - layerOffsetX;
+ const layerHeight = layerBox[3] - layerOffsetY;
+ if (layerWidth === 0 || layerHeight === 0) {
+ return;
}
- if (rect) {
- const width = rect[2] - rect[0];
- const height = rect[3] - rect[1];
- if (hasOwnCanvas && this.annotationCanvasMap) {
- transform = transform.slice();
- transform[4] -= rect[0];
- transform[5] -= rect[1];
- rect = rect.slice();
- rect[0] = rect[1] = 0;
- rect[2] = width;
- rect[3] = height;
- Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx), XY);
- const {
- viewportScale
- } = this;
- const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale);
- const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale);
- this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight);
- const {
- canvas,
- context
- } = this.annotationCanvas;
- this.annotationCanvasMap.set(id, canvas);
- this.annotationCanvas.savedCtx = this.ctx;
- this.ctx = context;
- this.ctx.save();
- this.ctx.setTransform(XY[0], 0, 0, -XY[1], 0, height * XY[1]);
- resetCtxToDefault(this.ctx);
+ this.genericComposeSMask(smask.context, layerCtx, layerWidth, layerHeight, smask.subtype, smask.backdrop, smask.transferMap, layerOffsetX, layerOffsetY, smask.offsetX, smask.offsetY);
+ ctx.save();
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "source-over";
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.drawImage(layerCtx.canvas, 0, 0);
+ ctx.restore();
+ }
+ genericComposeSMask(maskCtx, layerCtx, width, height, subtype, backdrop, transferMap, layerOffsetX, layerOffsetY, maskOffsetX, maskOffsetY) {
+ let maskCanvas = maskCtx.canvas;
+ let maskX = layerOffsetX - maskOffsetX;
+ let maskY = layerOffsetY - maskOffsetY;
+ if (backdrop) {
+ if (maskX < 0 || maskY < 0 || maskX + width > maskCanvas.width || maskY + height > maskCanvas.height) {
+ const canvas = this.cachedCanvases.getCanvas("maskExtension", width, height);
+ const ctx = canvas.context;
+ ctx.drawImage(maskCanvas, -maskX, -maskY);
+ ctx.globalCompositeOperation = "destination-atop";
+ ctx.fillStyle = backdrop;
+ ctx.fillRect(0, 0, width, height);
+ ctx.globalCompositeOperation = "source-over";
+ maskCanvas = canvas.canvas;
+ maskX = maskY = 0;
} else {
- resetCtxToDefault(this.ctx);
- this.endPath(opIdx);
+ maskCtx.save();
+ maskCtx.globalAlpha = 1;
+ maskCtx.setTransform(1, 0, 0, 1, 0, 0);
const clip = new Path2D();
- clip.rect(rect[0], rect[1], width, height);
- this.ctx.clip(clip);
+ clip.rect(maskX, maskY, width, height);
+ maskCtx.clip(clip);
+ maskCtx.globalCompositeOperation = "destination-atop";
+ maskCtx.fillStyle = backdrop;
+ maskCtx.fillRect(maskX, maskY, width, height);
+ maskCtx.restore();
}
}
- this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
- this.transform(opIdx, ...transform);
- this.transform(opIdx, ...matrix);
+ layerCtx.save();
+ layerCtx.globalAlpha = 1;
+ layerCtx.setTransform(1, 0, 0, 1, 0, 0);
+ if (subtype === "Alpha" && transferMap) {
+ layerCtx.filter = this.filterFactory.addAlphaFilter(transferMap);
+ } else if (subtype === "Luminosity") {
+ layerCtx.filter = this.filterFactory.addLuminosityFilter(transferMap);
+ }
+ const clip = new Path2D();
+ clip.rect(layerOffsetX, layerOffsetY, width, height);
+ layerCtx.clip(clip);
+ layerCtx.globalCompositeOperation = "destination-in";
+ layerCtx.drawImage(maskCanvas, maskX, maskY, width, height, layerOffsetX, layerOffsetY, width, height);
+ layerCtx.restore();
}
- endAnnotation(opIdx) {
- if (this.annotationCanvas) {
- this.ctx.restore();
- this.#drawFilter();
- this.ctx = this.annotationCanvas.savedCtx;
- delete this.annotationCanvas.savedCtx;
- delete this.annotationCanvas;
+ save(opIdx) {
+ if (this.inSMaskMode) {
+ copyCtxState(this.ctx, this.suspendedCtx);
}
+ this.ctx.save();
+ const old = this.current;
+ this.stateStack.push(old);
+ this.current = old.clone();
+ this.dependencyTracker?.save(opIdx);
}
- paintImageMaskXObject(opIdx, img) {
- if (!this.contentVisible) {
+ restore(opIdx) {
+ this.dependencyTracker?.restore(opIdx);
+ if (this.stateStack.length === 0) {
+ if (this.inSMaskMode) {
+ this.endSMaskMode();
+ }
return;
}
- const count = img.count;
- img = this.getObject(opIdx, img.data, img);
- img.count = count;
- const ctx = this.ctx;
- const mask = this._createMaskCanvas(opIdx, img);
- const maskCanvas = mask.canvas;
- ctx.save();
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
- this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, mask.offsetX, mask.offsetX + maskCanvas.width, mask.offsetY, mask.offsetY + maskCanvas.height).recordOperation(opIdx);
- ctx.restore();
- this.compose();
+ this.current = this.stateStack.pop();
+ this.ctx.restore();
+ if (this.inSMaskMode) {
+ copyCtxState(this.suspendedCtx, this.ctx);
+ }
+ this.checkSMaskState();
+ this.pendingClip = null;
+ this._cachedScaleForStroking[0] = -1;
+ this._cachedGetSinglePixelWidth = null;
}
- paintImageMaskXObjectRepeat(opIdx, img, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
- if (!this.contentVisible) {
+ transform(opIdx, a, b, c, d, e, f) {
+ this.dependencyTracker?.recordIncrementalData("transform", opIdx);
+ this.ctx.transform(a, b, c, d, e, f);
+ this._cachedScaleForStroking[0] = -1;
+ this._cachedGetSinglePixelWidth = null;
+ }
+ constructPath(opIdx, op, data, minMax) {
+ let [path] = data;
+ if (!minMax) {
+ path ||= data[0] = new Path2D();
+ this[op](opIdx, path);
return;
}
- img = this.getObject(opIdx, img.data, img);
- const ctx = this.ctx;
- ctx.save();
- const currentTransform = getCurrentTransform(ctx);
- ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
- const mask = this._createMaskCanvas(opIdx, img);
- ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]);
- this.dependencyTracker?.resetBBox(opIdx);
- for (let i = 0, ii = positions.length; i < ii; i += 2) {
- const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]);
- ctx.drawImage(mask.canvas, trans[4], trans[5]);
- this.dependencyTracker?.recordBBox(opIdx, this.ctx, trans[4], trans[4] + mask.canvas.width, trans[5], trans[5] + mask.canvas.height);
+ if (this.dependencyTracker !== null) {
+ const outerExtraSize = op === OPS.stroke ? this.current.lineWidth / 2 : 0;
+ this.dependencyTracker.resetBBox(opIdx).recordBBox(opIdx, this.ctx, minMax[0] - outerExtraSize, minMax[2] + outerExtraSize, minMax[1] - outerExtraSize, minMax[3] + outerExtraSize).recordDependencies(opIdx, ["transform"]);
}
- ctx.restore();
- this.compose();
- this.dependencyTracker?.recordOperation(opIdx);
+ if (!(path instanceof Path2D)) {
+ path = data[0] = makePathFromDrawOPS(path);
+ }
+ Util.axialAlignedBoundingBox(minMax, getCurrentTransform(this.ctx), this.current.minMax);
+ this[op](opIdx, path);
+ this._pathStartIdx = opIdx;
}
- paintImageMaskXObjectGroup(opIdx, images) {
- if (!this.contentVisible) {
- return;
+ closePath(opIdx) {
+ this.ctx.closePath();
+ }
+ stroke(opIdx, path, consumePath = true) {
+ const ctx = this.ctx;
+ const strokeColor = this.current.strokeColor;
+ ctx.globalAlpha = this.current.strokeAlpha;
+ if (this.contentVisible) {
+ if (typeof strokeColor === "object" && strokeColor?.getPattern) {
+ const baseTransform = strokeColor.isModifyingCurrentTransform() ? ctx.getTransform() : null;
+ ctx.save();
+ ctx.strokeStyle = strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx);
+ if (baseTransform) {
+ const newPath = new Path2D();
+ newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
+ path = newPath;
+ }
+ this.rescaleAndStroke(path, false);
+ ctx.restore();
+ } else {
+ this.rescaleAndStroke(path, true);
+ }
+ }
+ this.dependencyTracker?.recordDependencies(opIdx, Dependencies.stroke);
+ if (consumePath) {
+ this.consumePath(opIdx, path, this.current.getClippedPathBoundingBox(PathType.STROKE, getCurrentTransform(this.ctx)));
}
+ ctx.globalAlpha = this.current.fillAlpha;
+ }
+ closeStroke(opIdx, path) {
+ this.stroke(opIdx, path);
+ }
+ fill(opIdx, path, consumePath = true) {
const ctx = this.ctx;
const fillColor = this.current.fillColor;
const isPatternFill = this.current.patternFill;
- this.dependencyTracker?.resetBBox(opIdx).recordDependencies(opIdx, Dependencies.transformAndFill);
- for (const image of images) {
- const {
- data,
- width,
- height,
- transform
- } = image;
- const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
- const maskCtx = maskCanvas.context;
- maskCtx.save();
- const img = this.getObject(opIdx, data, image);
- putBinaryImageMask(maskCtx, img);
- maskCtx.globalCompositeOperation = "source-in";
- maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx) : fillColor;
- maskCtx.fillRect(0, 0, width, height);
- maskCtx.restore();
+ let needRestore = false;
+ if (isPatternFill) {
+ const baseTransform = fillColor.isModifyingCurrentTransform() ? ctx.getTransform() : null;
+ this.dependencyTracker?.save(opIdx);
ctx.save();
- ctx.transform(...transform);
- ctx.scale(1, -1);
- drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
- this.dependencyTracker?.recordBBox(opIdx, ctx, 0, width, 0, height);
+ ctx.fillStyle = fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx);
+ if (baseTransform) {
+ const newPath = new Path2D();
+ newPath.addPath(path, ctx.getTransform().invertSelf().multiplySelf(baseTransform));
+ path = newPath;
+ }
+ needRestore = true;
+ }
+ const intersect = this.current.getClippedPathBoundingBox();
+ if (this.contentVisible && intersect !== null) {
+ if (this.pendingEOFill) {
+ ctx.fill(path, "evenodd");
+ this.pendingEOFill = false;
+ } else {
+ ctx.fill(path);
+ }
+ }
+ this.dependencyTracker?.recordDependencies(opIdx, Dependencies.fill);
+ if (needRestore) {
ctx.restore();
+ this.dependencyTracker?.restore(opIdx);
}
- this.compose();
- this.dependencyTracker?.recordOperation(opIdx);
+ if (consumePath) {
+ this.consumePath(opIdx, path, intersect);
+ }
+ }
+ eoFill(opIdx, path) {
+ this.pendingEOFill = true;
+ this.fill(opIdx, path);
+ }
+ fillStroke(opIdx, path) {
+ this.fill(opIdx, path, false);
+ this.stroke(opIdx, path, false);
+ this.consumePath(opIdx, path);
+ }
+ eoFillStroke(opIdx, path) {
+ this.pendingEOFill = true;
+ this.fillStroke(opIdx, path);
+ }
+ closeFillStroke(opIdx, path) {
+ this.fillStroke(opIdx, path);
+ }
+ closeEOFillStroke(opIdx, path) {
+ this.pendingEOFill = true;
+ this.fillStroke(opIdx, path);
+ }
+ endPath(opIdx, path) {
+ this.consumePath(opIdx, path);
+ }
+ rawFillPath(opIdx, path) {
+ this.ctx.fill(path);
+ this.dependencyTracker?.recordDependencies(opIdx, Dependencies.rawFillPath).recordOperation(opIdx);
+ }
+ clip(opIdx) {
+ this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx);
+ this.pendingClip = NORMAL_CLIP;
+ }
+ eoClip(opIdx) {
+ this.dependencyTracker?.recordFutureForcedDependency("clipMode", opIdx);
+ this.pendingClip = EO_CLIP;
+ }
+ beginText(opIdx) {
+ this.current.textMatrix = null;
+ this.current.textMatrixScale = 1;
+ this.current.x = this.current.lineX = 0;
+ this.current.y = this.current.lineY = 0;
+ this.dependencyTracker?.recordOpenMarker(opIdx).resetIncrementalData("sameLineText").resetIncrementalData("moveText", opIdx);
+ }
+ endText(opIdx) {
+ const paths = this.pendingTextPaths;
+ const ctx = this.ctx;
+ if (this.dependencyTracker) {
+ const {
+ dependencyTracker
+ } = this;
+ if (paths !== undefined) {
+ dependencyTracker.recordFutureForcedDependency("textClip", dependencyTracker.getOpenMarker()).recordFutureForcedDependency("textClip", opIdx);
+ }
+ dependencyTracker.recordCloseMarker(opIdx);
+ }
+ if (paths !== undefined) {
+ const newPath = new Path2D();
+ const invTransf = ctx.getTransform().invertSelf();
+ for (const {
+ transform,
+ x,
+ y,
+ fontSize,
+ path
+ } of paths) {
+ if (!path) {
+ continue;
+ }
+ newPath.addPath(path, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x, y).scale(fontSize, -fontSize));
+ }
+ ctx.clip(newPath);
+ }
+ delete this.pendingTextPaths;
}
- paintImageXObject(opIdx, objId) {
- if (!this.contentVisible) {
- return;
+ setCharSpacing(opIdx, spacing) {
+ this.dependencyTracker?.recordSimpleData("charSpacing", opIdx);
+ this.current.charSpacing = spacing;
+ }
+ setWordSpacing(opIdx, spacing) {
+ this.dependencyTracker?.recordSimpleData("wordSpacing", opIdx);
+ this.current.wordSpacing = spacing;
+ }
+ setHScale(opIdx, scale) {
+ this.dependencyTracker?.recordSimpleData("hScale", opIdx);
+ this.current.textHScale = scale / 100;
+ }
+ setLeading(opIdx, leading) {
+ this.dependencyTracker?.recordSimpleData("leading", opIdx);
+ this.current.leading = -leading;
+ }
+ setFont(opIdx, fontRefName, size) {
+ this.dependencyTracker?.recordSimpleData("font", opIdx).recordSimpleDataFromNamed("fontObj", fontRefName, opIdx);
+ const fontObj = this.commonObjs.get(fontRefName);
+ const current = this.current;
+ if (!fontObj) {
+ throw new Error(`Can't find font for ${fontRefName}`);
}
- const imgData = this.getObject(opIdx, objId);
- if (!imgData) {
- warn("Dependent image isn't ready yet");
- return;
+ current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX;
+ if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
+ warn("Invalid font matrix for font " + fontRefName);
}
- this.paintInlineImageXObject(opIdx, imgData);
- }
- paintImageXObjectRepeat(opIdx, objId, scaleX, scaleY, positions) {
- if (!this.contentVisible) {
- return;
+ if (size < 0) {
+ size = -size;
+ current.fontDirection = -1;
+ } else {
+ current.fontDirection = 1;
}
- const imgData = this.getObject(opIdx, objId);
- if (!imgData) {
- warn("Dependent image isn't ready yet");
+ this.current.font = fontObj;
+ this.current.fontSize = size;
+ if (fontObj.isType3Font) {
return;
}
- const width = imgData.width;
- const height = imgData.height;
- const map = [];
- for (let i = 0, ii = positions.length; i < ii; i += 2) {
- map.push({
- transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]],
- x: 0,
- y: 0,
- w: width,
- h: height
- });
+ const name = fontObj.loadedName || "sans-serif";
+ const typeface = fontObj.systemFontInfo?.css || `"${name}", ${fontObj.fallbackName}`;
+ let bold = "normal";
+ if (fontObj.black) {
+ bold = "900";
+ } else if (fontObj.bold) {
+ bold = "bold";
}
- this.paintInlineImageXObjectGroup(opIdx, imgData, map);
- }
- applyTransferMapsToCanvas(ctx) {
- if (this.current.transferMaps !== "none") {
- ctx.filter = this.current.transferMaps;
- ctx.drawImage(ctx.canvas, 0, 0);
- ctx.filter = "none";
+ const italic = fontObj.italic ? "italic" : "normal";
+ let browserFontSize = size;
+ if (size < MIN_FONT_SIZE) {
+ browserFontSize = MIN_FONT_SIZE;
+ } else if (size > MAX_FONT_SIZE) {
+ browserFontSize = MAX_FONT_SIZE;
}
- return ctx.canvas;
+ this.current.fontSizeScale = size / browserFontSize;
+ this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;
}
- applyTransferMapsToBitmap(imgData) {
- if (this.current.transferMaps === "none") {
- return imgData.bitmap;
- }
+ setTextRenderingMode(opIdx, mode) {
+ this.dependencyTracker?.recordSimpleData("textRenderingMode", opIdx);
+ this.current.textRenderingMode = mode;
+ }
+ setTextRise(opIdx, rise) {
+ this.dependencyTracker?.recordSimpleData("textRise", opIdx);
+ this.current.textRise = rise;
+ }
+ moveText(opIdx, x, y) {
+ this.dependencyTracker?.resetIncrementalData("sameLineText").recordIncrementalData("moveText", opIdx);
+ this.current.x = this.current.lineX += x;
+ this.current.y = this.current.lineY += y;
+ }
+ setLeadingMoveText(opIdx, x, y) {
+ this.setLeading(opIdx, -y);
+ this.moveText(opIdx, x, y);
+ }
+ setTextMatrix(opIdx, matrix) {
+ this.dependencyTracker?.resetIncrementalData("sameLineText").recordSimpleData("textMatrix", opIdx);
const {
- bitmap,
- width,
- height
- } = imgData;
- const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
- const tmpCtx = tmpCanvas.context;
- tmpCtx.filter = this.current.transferMaps;
- tmpCtx.drawImage(bitmap, 0, 0);
- tmpCtx.filter = "none";
- return tmpCanvas.canvas;
+ current
+ } = this;
+ current.textMatrix = matrix;
+ current.textMatrixScale = Math.hypot(matrix[0], matrix[1]);
+ current.x = current.lineX = 0;
+ current.y = current.lineY = 0;
}
- paintInlineImageXObject(opIdx, imgData) {
- if (!this.contentVisible) {
- return;
- }
- const width = imgData.width;
- const height = imgData.height;
+ nextLine(opIdx) {
+ this.moveText(opIdx, 0, this.current.leading);
+ this.dependencyTracker?.recordIncrementalData("moveText", this.dependencyTracker.getSimpleIndex("leading") ?? opIdx);
+ }
+ #getScaledPath(path, currentTransform, transform) {
+ const newPath = new Path2D();
+ newPath.addPath(path, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform));
+ return newPath;
+ }
+ paintChar(opIdx, character, x, y, patternFillTransform, patternStrokeTransform) {
const ctx = this.ctx;
- this.save(opIdx);
- const {
- filter
- } = ctx;
- if (filter !== "none" && filter !== "") {
- ctx.filter = "none";
+ const current = this.current;
+ const font = current.font;
+ const textRenderingMode = current.textRenderingMode;
+ const fontSize = current.fontSize / current.fontSizeScale;
+ const fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
+ const isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG);
+ const patternFill = current.patternFill && !font.missingFile;
+ const patternStroke = current.patternStroke && !font.missingFile;
+ let path;
+ if ((font.disableFontFace || isAddToPathSet || patternFill || patternStroke) && !font.missingFile) {
+ path = font.getPathGenerator(this.commonObjs, character);
}
- ctx.scale(1 / width, -1 / height);
- let imgToPaint;
- if (imgData.bitmap) {
- imgToPaint = this.applyTransferMapsToBitmap(imgData);
- } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) {
- imgToPaint = imgData;
+ if (path && (font.disableFontFace || patternFill || patternStroke)) {
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.scale(fontSize, -fontSize);
+ this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font);
+ let currentTransform;
+ if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
+ if (patternFillTransform) {
+ currentTransform = ctx.getTransform();
+ ctx.setTransform(...patternFillTransform);
+ const scaledPath = this.#getScaledPath(path, currentTransform, patternFillTransform);
+ ctx.fill(scaledPath);
+ } else {
+ ctx.fill(path);
+ }
+ }
+ if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
+ if (patternStrokeTransform) {
+ currentTransform ||= ctx.getTransform();
+ ctx.setTransform(...patternStrokeTransform);
+ const {
+ a,
+ b,
+ c,
+ d
+ } = currentTransform;
+ const invPatternTransform = Util.inverseTransform(patternStrokeTransform);
+ const transf = Util.transform([a, b, c, d, 0, 0], invPatternTransform);
+ Util.singularValueDecompose2dScale(transf, XY);
+ ctx.lineWidth *= Math.max(XY[0], XY[1]) / fontSize;
+ ctx.stroke(this.#getScaledPath(path, currentTransform, patternStrokeTransform));
+ } else {
+ ctx.lineWidth /= fontSize;
+ ctx.stroke(path);
+ }
+ }
+ ctx.restore();
} else {
- const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
- const tmpCtx = tmpCanvas.context;
- putBinaryImageData(tmpCtx, imgData);
- imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
+ if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
+ ctx.fillText(character, x, y);
+ this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character));
+ }
+ if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
+ if (this.dependencyTracker) {
+ this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y, () => ctx.measureText(character)).recordDependencies(opIdx, Dependencies.stroke);
+ }
+ ctx.strokeText(character, x, y);
+ }
+ }
+ if (isAddToPathSet) {
+ const paths = this.pendingTextPaths ||= [];
+ paths.push({
+ transform: getCurrentTransform(ctx),
+ x,
+ y,
+ fontSize,
+ path
+ });
+ this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, font, fontSize, x, y);
}
- const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx));
- ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate);
- this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, ctx, 0, width, -height, 0).recordDependencies(opIdx, Dependencies.imageXObject).recordOperation(opIdx);
- drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height);
- this.compose();
- this.restore(opIdx);
}
- paintInlineImageXObjectGroup(opIdx, imgData, map) {
- if (!this.contentVisible) {
- return;
+ get isFontSubpixelAAEnabled() {
+ const {
+ context: ctx
+ } = this.cachedCanvases.getCanvas("isFontSubpixelAAEnabled", 10, 10);
+ ctx.scale(1.5, 1);
+ ctx.fillText("I", 0, 10);
+ const data = ctx.getImageData(0, 0, 10, 10).data;
+ let enabled = false;
+ for (let i = 3; i < data.length; i += 4) {
+ if (data[i] > 0 && data[i] < 255) {
+ enabled = true;
+ break;
+ }
+ }
+ return shadow(this, "isFontSubpixelAAEnabled", enabled);
+ }
+ showText(opIdx, glyphs) {
+ if (this.dependencyTracker) {
+ this.dependencyTracker.recordDependencies(opIdx, Dependencies.showText).resetBBox(opIdx);
+ if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
+ this.dependencyTracker.recordFutureForcedDependency("textClip", opIdx).inheritPendingDependenciesAsFutureForcedDependencies();
+ }
+ }
+ const current = this.current;
+ const font = current.font;
+ if (font.isType3Font) {
+ this.showType3Text(opIdx, glyphs);
+ this.dependencyTracker?.recordShowTextOperation(opIdx);
+ return undefined;
+ }
+ const fontSize = current.fontSize;
+ if (fontSize === 0) {
+ this.dependencyTracker?.recordOperation(opIdx);
+ return undefined;
}
const ctx = this.ctx;
- let imgToPaint;
- if (imgData.bitmap) {
- imgToPaint = imgData.bitmap;
+ const fontSizeScale = current.fontSizeScale;
+ const charSpacing = current.charSpacing;
+ const wordSpacing = current.wordSpacing;
+ const fontDirection = current.fontDirection;
+ const textHScale = current.textHScale * fontDirection;
+ const glyphsLength = glyphs.length;
+ const vertical = font.vertical;
+ const spacingDir = vertical ? 1 : -1;
+ const defaultVMetrics = font.defaultVMetrics;
+ const widthAdvanceScale = fontSize * current.fontMatrix[0];
+ const simpleFillText = current.textRenderingMode === TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
+ ctx.save();
+ if (current.textMatrix) {
+ ctx.transform(...current.textMatrix);
+ }
+ ctx.translate(current.x, current.y + current.textRise);
+ if (fontDirection > 0) {
+ ctx.scale(textHScale, -1);
} else {
- const w = imgData.width;
- const h = imgData.height;
- const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h);
- const tmpCtx = tmpCanvas.context;
- putBinaryImageData(tmpCtx, imgData);
- imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
+ ctx.scale(textHScale, 1);
}
- this.dependencyTracker?.resetBBox(opIdx);
- for (const entry of map) {
+ let patternFillTransform, patternStrokeTransform;
+ if (current.patternFill) {
ctx.save();
- ctx.transform(...entry.transform);
- ctx.scale(1, -1);
- drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1);
- this.dependencyTracker?.recordBBox(opIdx, ctx, 0, 1, -1, 0);
+ const pattern = current.fillColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx);
+ patternFillTransform = getCurrentTransform(ctx);
ctx.restore();
+ ctx.fillStyle = pattern;
}
- this.dependencyTracker?.recordOperation(opIdx);
- this.compose();
- }
- paintSolidColorImageMask(opIdx) {
- if (!this.contentVisible) {
- return;
+ if (current.patternStroke) {
+ ctx.save();
+ const pattern = current.strokeColor.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.STROKE, opIdx);
+ patternStrokeTransform = getCurrentTransform(ctx);
+ ctx.restore();
+ ctx.strokeStyle = pattern;
}
- this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, 0, 1, 0, 1).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx);
- this.ctx.fillRect(0, 0, 1, 1);
- this.compose();
- }
- markPoint(opIdx, tag) {}
- markPointProps(opIdx, tag, properties) {}
- beginMarkedContent(opIdx, tag) {
- this.dependencyTracker?.beginMarkedContent(opIdx);
- this.markedContentStack.push({
- visible: true
- });
- }
- beginMarkedContentProps(opIdx, tag, properties) {
- this.dependencyTracker?.beginMarkedContent(opIdx);
- if (tag === "OC") {
- this.markedContentStack.push({
- visible: this.optionalContentConfig.isVisible(properties)
- });
+ let lineWidth = current.lineWidth;
+ const scale = current.textMatrixScale;
+ if (scale === 0 || lineWidth === 0) {
+ const fillStrokeMode = current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;
+ if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) {
+ lineWidth = this.getSinglePixelWidth();
+ }
} else {
- this.markedContentStack.push({
- visible: true
- });
- }
- this.contentVisible = this.isContentVisible();
- }
- endMarkedContent(opIdx) {
- this.dependencyTracker?.endMarkedContent(opIdx);
- this.markedContentStack.pop();
- this.contentVisible = this.isContentVisible();
- }
- beginCompat(opIdx) {}
- endCompat(opIdx) {}
- consumePath(opIdx, path, clipBox) {
- const isEmpty = this.current.isEmptyClip();
- if (this.pendingClip) {
- this.current.updateClipFromPath();
+ lineWidth /= scale;
}
- if (!this.pendingClip) {
- this.compose(clipBox);
+ if (fontSizeScale !== 1.0) {
+ ctx.scale(fontSizeScale, fontSizeScale);
+ lineWidth /= fontSizeScale;
}
- const ctx = this.ctx;
- if (this.pendingClip) {
- if (!isEmpty) {
- if (this.pendingClip === EO_CLIP) {
- ctx.clip(path, "evenodd");
- } else {
- ctx.clip(path);
- }
+ ctx.lineWidth = lineWidth;
+ if (font.isInvalidPDFjsFont) {
+ const chars = [];
+ let width = 0;
+ for (const glyph of glyphs) {
+ chars.push(glyph.unicode);
+ width += glyph.width;
}
- this.pendingClip = null;
- this.dependencyTracker?.bboxToClipBoxDropOperation(opIdx).recordFutureForcedDependency("clipPath", opIdx);
- } else {
- this.dependencyTracker?.recordOperation(opIdx);
+ const joinedChars = chars.join("");
+ ctx.fillText(joinedChars, 0, 0);
+ if (this.dependencyTracker !== null) {
+ const measure = ctx.measureText(joinedChars);
+ this.dependencyTracker.recordBBox(opIdx, this.ctx, -measure.actualBoundingBoxLeft, measure.actualBoundingBoxRight, -measure.actualBoundingBoxAscent, measure.actualBoundingBoxDescent).recordShowTextOperation(opIdx);
+ }
+ current.x += width * widthAdvanceScale * textHScale;
+ ctx.restore();
+ this.compose();
+ return undefined;
}
- this.current.startNewPathAndClipBox(this.current.clipBox);
- }
- getSinglePixelWidth() {
- if (!this._cachedGetSinglePixelWidth) {
- const m = getCurrentTransform(this.ctx);
- if (m[1] === 0 && m[2] === 0) {
- this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3]));
+ let x = 0,
+ i;
+ for (i = 0; i < glyphsLength; ++i) {
+ const glyph = glyphs[i];
+ if (typeof glyph === "number") {
+ x += spacingDir * glyph * fontSize / 1000;
+ continue;
+ }
+ let restoreNeeded = false;
+ const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
+ const character = glyph.fontChar;
+ const accent = glyph.accent;
+ let scaledX, scaledY;
+ let width = glyph.width;
+ if (vertical) {
+ const vmetric = glyph.vmetric || defaultVMetrics;
+ const vx = -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale;
+ const vy = vmetric[2] * widthAdvanceScale;
+ width = vmetric ? -vmetric[0] : width;
+ scaledX = vx / fontSizeScale;
+ scaledY = (x + vy) / fontSizeScale;
} else {
- const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]);
- const normX = Math.hypot(m[0], m[2]);
- const normY = Math.hypot(m[1], m[3]);
- this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet;
+ scaledX = x / fontSizeScale;
+ scaledY = 0;
}
- }
- return this._cachedGetSinglePixelWidth;
- }
- getScaleForStroking() {
- if (this._cachedScaleForStroking[0] === -1) {
- const {
- lineWidth
- } = this.current;
- const {
- a,
- b,
- c,
- d
- } = this.ctx.getTransform();
- let scaleX, scaleY;
- if (b === 0 && c === 0) {
- const normX = Math.abs(a);
- const normY = Math.abs(d);
- if (normX === normY) {
- if (lineWidth === 0) {
- scaleX = scaleY = 1 / normX;
- } else {
- const scaledLineWidth = normX * lineWidth;
- scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1;
- }
- } else if (lineWidth === 0) {
- scaleX = 1 / normX;
- scaleY = 1 / normY;
- } else {
- const scaledXLineWidth = normX * lineWidth;
- const scaledYLineWidth = normY * lineWidth;
- scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1;
- scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1;
+ let measure;
+ if (font.remeasure && width > 0) {
+ measure = ctx.measureText(character);
+ const measuredWidth = measure.width * 1000 / fontSize * fontSizeScale;
+ if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
+ const characterScaleX = width / measuredWidth;
+ restoreNeeded = true;
+ ctx.save();
+ ctx.scale(characterScaleX, 1);
+ scaledX /= characterScaleX;
+ } else if (width !== measuredWidth) {
+ scaledX += (width - measuredWidth) / 2000 * fontSize / fontSizeScale;
}
- } else {
- const absDet = Math.abs(a * d - b * c);
- const normX = Math.hypot(a, b);
- const normY = Math.hypot(c, d);
- if (lineWidth === 0) {
- scaleX = normY / absDet;
- scaleY = normX / absDet;
+ }
+ if (this.contentVisible && (glyph.isInFont || font.missingFile)) {
+ if (simpleFillText && !accent) {
+ ctx.fillText(character, scaledX, scaledY);
+ this.dependencyTracker?.recordCharacterBBox(opIdx, ctx, measure ? {
+ bbox: null
+ } : font, fontSize / fontSizeScale, scaledX, scaledY, () => measure ?? ctx.measureText(character));
} else {
- const baseArea = lineWidth * absDet;
- scaleX = normY > baseArea ? normY / baseArea : 1;
- scaleY = normX > baseArea ? normX / baseArea : 1;
+ this.paintChar(opIdx, character, scaledX, scaledY, patternFillTransform, patternStrokeTransform);
+ if (accent) {
+ const scaledAccentX = scaledX + fontSize * accent.offset.x / fontSizeScale;
+ const scaledAccentY = scaledY - fontSize * accent.offset.y / fontSizeScale;
+ this.paintChar(opIdx, accent.fontChar, scaledAccentX, scaledAccentY, patternFillTransform, patternStrokeTransform);
+ }
}
}
- this._cachedScaleForStroking[0] = scaleX;
- this._cachedScaleForStroking[1] = scaleY;
- }
- return this._cachedScaleForStroking;
- }
- rescaleAndStroke(path, saveRestore) {
- const {
- ctx,
- current: {
- lineWidth
- }
- } = this;
- const [scaleX, scaleY] = this.getScaleForStroking();
- if (scaleX === scaleY) {
- ctx.lineWidth = (lineWidth || 1) * scaleX;
- ctx.stroke(path);
- return;
+ const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;
+ x += charWidth;
+ if (restoreNeeded) {
+ ctx.restore();
+ }
}
- const dashes = ctx.getLineDash();
- if (saveRestore) {
- ctx.save();
+ if (vertical) {
+ current.y -= x;
+ } else {
+ current.x += x * textHScale;
}
- ctx.scale(scaleX, scaleY);
- SCALE_MATRIX.a = 1 / scaleX;
- SCALE_MATRIX.d = 1 / scaleY;
- const newPath = new Path2D();
- newPath.addPath(path, SCALE_MATRIX);
- if (dashes.length > 0) {
- const scale = Math.max(scaleX, scaleY);
- ctx.setLineDash(dashes.map(x => x / scale));
- ctx.lineDashOffset /= scale;
+ ctx.restore();
+ this.compose();
+ this.dependencyTracker?.recordShowTextOperation(opIdx);
+ return undefined;
+ }
+ showType3Text(opIdx, glyphs) {
+ const ctx = this.ctx;
+ const current = this.current;
+ const font = current.font;
+ const fontSize = current.fontSize;
+ const fontDirection = current.fontDirection;
+ const spacingDir = font.vertical ? 1 : -1;
+ const charSpacing = current.charSpacing;
+ const wordSpacing = current.wordSpacing;
+ const textHScale = current.textHScale * fontDirection;
+ const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;
+ const glyphsLength = glyphs.length;
+ const isTextInvisible = current.textRenderingMode === TextRenderingMode.INVISIBLE;
+ let i, glyph, width, spacingLength;
+ if (isTextInvisible || fontSize === 0) {
+ return;
}
- ctx.lineWidth = lineWidth || 1;
- ctx.stroke(newPath);
- if (saveRestore) {
- ctx.restore();
+ this._cachedScaleForStroking[0] = -1;
+ this._cachedGetSinglePixelWidth = null;
+ ctx.save();
+ if (current.textMatrix) {
+ ctx.transform(...current.textMatrix);
}
- }
- isContentVisible() {
- for (let i = this.markedContentStack.length - 1; i >= 0; i--) {
- if (!this.markedContentStack[i].visible) {
- return false;
+ ctx.translate(current.x, current.y + current.textRise);
+ ctx.scale(textHScale, fontDirection);
+ const dependencyTracker = this.dependencyTracker;
+ this.dependencyTracker = dependencyTracker ? new CanvasNestedDependencyTracker(dependencyTracker, opIdx) : null;
+ for (i = 0; i < glyphsLength; ++i) {
+ glyph = glyphs[i];
+ if (typeof glyph === "number") {
+ spacingLength = spacingDir * glyph * fontSize / 1000;
+ this.ctx.translate(spacingLength, 0);
+ current.x += spacingLength * textHScale;
+ continue;
+ }
+ const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;
+ const operatorList = font.charProcOperatorList[glyph.operatorListId];
+ if (!operatorList) {
+ warn(`Type3 character "${glyph.operatorListId}" is not available.`);
+ } else if (this.contentVisible) {
+ this.save();
+ ctx.scale(fontSize, fontSize);
+ ctx.transform(...fontMatrix);
+ this.executeOperatorList(operatorList);
+ this.restore();
}
+ const p = [glyph.width, 0];
+ Util.applyTransform(p, fontMatrix);
+ width = p[0] * fontSize + spacing;
+ ctx.translate(width, 0);
+ current.x += width * textHScale;
}
- return true;
- }
-}
-for (const op in OPS) {
- if (CanvasGraphics.prototype[op] !== undefined) {
- CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
- }
-}
-
-;// ./src/display/canvas_factory.js
-
-class BaseCanvasFactory {
- #enableHWA = false;
- constructor({
- enableHWA = false
- }) {
- this.#enableHWA = enableHWA;
- }
- create(width, height) {
- if (width <= 0 || height <= 0) {
- throw new Error("Invalid canvas size");
+ ctx.restore();
+ if (dependencyTracker) {
+ this.dependencyTracker = dependencyTracker;
}
- const canvas = this._createCanvas(width, height);
- return {
- canvas,
- context: canvas.getContext("2d", {
- willReadFrequently: !this.#enableHWA
- })
- };
}
- reset(canvasAndContext, width, height) {
- if (!canvasAndContext.canvas) {
- throw new Error("Canvas is not specified");
- }
- if (width <= 0 || height <= 0) {
- throw new Error("Invalid canvas size");
- }
- canvasAndContext.canvas.width = width;
- canvasAndContext.canvas.height = height;
+ setCharWidth(opIdx, xWidth, yWidth) {}
+ setCharWidthAndBounds(opIdx, xWidth, yWidth, llx, lly, urx, ury) {
+ const clip = new Path2D();
+ clip.rect(llx, lly, urx - llx, ury - lly);
+ this.ctx.clip(clip);
+ this.dependencyTracker?.recordBBox(opIdx, this.ctx, llx, urx, lly, ury).recordClipBox(opIdx, this.ctx, llx, urx, lly, ury);
+ this.endPath(opIdx);
}
- destroy(canvasAndContext) {
- if (!canvasAndContext.canvas) {
- throw new Error("Canvas is not specified");
+ getColorN_Pattern(opIdx, IR) {
+ let pattern;
+ if (IR[0] === "TilingPattern") {
+ const baseTransform = this.baseTransform || getCurrentTransform(this.ctx);
+ const canvasGraphicsFactory = {
+ createCanvasGraphics: (ctx, renderingOpIdx) => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {
+ optionalContentConfig: this.optionalContentConfig,
+ markedContentStack: this.markedContentStack
+ }, undefined, undefined, this.dependencyTracker ? new CanvasNestedDependencyTracker(this.dependencyTracker, renderingOpIdx, true) : null)
+ };
+ pattern = new TilingPattern(IR, this.ctx, canvasGraphicsFactory, baseTransform);
+ } else {
+ pattern = this._getPattern(opIdx, IR[1], IR[2]);
}
- canvasAndContext.canvas.width = 0;
- canvasAndContext.canvas.height = 0;
- canvasAndContext.canvas = null;
- canvasAndContext.context = null;
+ return pattern;
}
- _createCanvas(width, height) {
- unreachable("Abstract method `_createCanvas` called.");
+ setStrokeColorN(opIdx, ...args) {
+ this.dependencyTracker?.recordSimpleData("strokeColor", opIdx);
+ this.current.strokeColor = this.getColorN_Pattern(opIdx, args);
+ this.current.patternStroke = true;
}
-}
-class DOMCanvasFactory extends BaseCanvasFactory {
- constructor({
- ownerDocument = globalThis.document,
- enableHWA = false
- }) {
- super({
- enableHWA
- });
- this._document = ownerDocument;
+ setFillColorN(opIdx, ...args) {
+ this.dependencyTracker?.recordSimpleData("fillColor", opIdx);
+ this.current.fillColor = this.getColorN_Pattern(opIdx, args);
+ this.current.patternFill = true;
}
- _createCanvas(width, height) {
- const canvas = this._document.createElement("canvas");
- canvas.width = width;
- canvas.height = height;
- return canvas;
+ setStrokeRGBColor(opIdx, color) {
+ this.dependencyTracker?.recordSimpleData("strokeColor", opIdx);
+ this.ctx.strokeStyle = this.current.strokeColor = color;
+ this.current.patternStroke = false;
}
-}
-
-;// ./src/display/stubs.js
-const DOMCMapReaderFactory = null;
-const DOMWasmFactory = null;
-const DOMStandardFontDataFactory = null;
-const NodeCanvasFactory = null;
-const NodeCMapReaderFactory = null;
-const NodeFilterFactory = null;
-const NodeWasmFactory = null;
-const NodeStandardFontDataFactory = null;
-const PDFFetchStream = null;
-const PDFNetworkStream = null;
-const PDFNodeStream = null;
-
-;// ./src/display/filter_factory.js
-
-
-class BaseFilterFactory {
- addFilter(maps) {
- return "none";
+ setStrokeTransparent(opIdx) {
+ this.dependencyTracker?.recordSimpleData("strokeColor", opIdx);
+ this.ctx.strokeStyle = this.current.strokeColor = "transparent";
+ this.current.patternStroke = false;
}
- addHCMFilter(fgColor, bgColor) {
- return "none";
+ setFillRGBColor(opIdx, color) {
+ this.dependencyTracker?.recordSimpleData("fillColor", opIdx);
+ this.ctx.fillStyle = this.current.fillColor = color;
+ this.current.patternFill = false;
}
- addAlphaFilter(map) {
- return "none";
+ setFillTransparent(opIdx) {
+ this.dependencyTracker?.recordSimpleData("fillColor", opIdx);
+ this.ctx.fillStyle = this.current.fillColor = "transparent";
+ this.current.patternFill = false;
}
- addLuminosityFilter(map) {
- return "none";
+ _getPattern(opIdx, objId, matrix = null) {
+ let pattern;
+ if (this.cachedPatterns.has(objId)) {
+ pattern = this.cachedPatterns.get(objId);
+ } else {
+ pattern = getShadingPattern(this.getObject(opIdx, objId));
+ this.cachedPatterns.set(objId, pattern);
+ }
+ if (matrix) {
+ pattern.matrix = matrix;
+ }
+ return pattern;
}
- addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
- return "none";
+ shadingFill(opIdx, objId) {
+ if (!this.contentVisible) {
+ return;
+ }
+ const ctx = this.ctx;
+ this.save(opIdx);
+ const pattern = this._getPattern(opIdx, objId);
+ ctx.fillStyle = pattern.getPattern(ctx, this, getCurrentTransformInverse(ctx), PathType.SHADING, opIdx);
+ const inv = getCurrentTransformInverse(ctx);
+ if (inv) {
+ const {
+ width,
+ height
+ } = ctx.canvas;
+ const minMax = MIN_MAX_INIT.slice();
+ Util.axialAlignedBoundingBox([0, 0, width, height], inv, minMax);
+ const [x0, y0, x1, y1] = minMax;
+ this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
+ } else {
+ this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
+ }
+ this.dependencyTracker?.resetBBox(opIdx).recordFullPageBBox(opIdx).recordDependencies(opIdx, Dependencies.transform).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx);
+ this.compose(this.current.getClippedPathBoundingBox());
+ this.restore(opIdx);
}
- destroy(keepHCM = false) {}
-}
-class DOMFilterFactory extends BaseFilterFactory {
- #baseUrl;
- #_cache;
- #_defs;
- #docId;
- #document;
- #_hcmCache;
- #id = 0;
- constructor({
- docId,
- ownerDocument = globalThis.document
- }) {
- super();
- this.#docId = docId;
- this.#document = ownerDocument;
+ beginInlineImage() {
+ unreachable("Should not call beginInlineImage");
}
- get #cache() {
- return this.#_cache ||= new Map();
+ beginImageData() {
+ unreachable("Should not call beginImageData");
}
- get #hcmCache() {
- return this.#_hcmCache ||= new Map();
+ paintFormXObjectBegin(opIdx, matrix, bbox) {
+ if (!this.contentVisible) {
+ return;
+ }
+ this.save(opIdx);
+ this.baseTransformStack.push(this.baseTransform);
+ if (matrix) {
+ this.transform(opIdx, ...matrix);
+ }
+ this.baseTransform = getCurrentTransform(this.ctx);
+ if (bbox) {
+ Util.axialAlignedBoundingBox(bbox, this.baseTransform, this.current.minMax);
+ const [x0, y0, x1, y1] = bbox;
+ const clip = new Path2D();
+ clip.rect(x0, y0, x1 - x0, y1 - y0);
+ this.ctx.clip(clip);
+ this.dependencyTracker?.recordClipBox(opIdx, this.ctx, x0, x1, y0, y1);
+ this.endPath(opIdx);
+ }
}
- get #defs() {
- if (!this.#_defs) {
- const div = this.#document.createElement("div");
- const {
- style
- } = div;
- style.visibility = "hidden";
- style.contain = "strict";
- style.width = style.height = 0;
- style.position = "absolute";
- style.top = style.left = 0;
- style.zIndex = -1;
- const svg = this.#document.createElementNS(SVG_NS, "svg");
- svg.setAttribute("width", 0);
- svg.setAttribute("height", 0);
- this.#_defs = this.#document.createElementNS(SVG_NS, "defs");
- div.append(svg);
- svg.append(this.#_defs);
- this.#document.body.append(div);
+ paintFormXObjectEnd(opIdx) {
+ if (!this.contentVisible) {
+ return;
}
- return this.#_defs;
+ this.restore(opIdx);
+ this.baseTransform = this.baseTransformStack.pop();
}
- #createTables(maps) {
- if (maps.length === 1) {
- const mapR = maps[0];
- const buffer = new Array(256);
- for (let i = 0; i < 256; i++) {
- buffer[i] = mapR[i] / 255;
- }
- const table = buffer.join(",");
- return [table, table, table];
+ beginGroup(opIdx, group) {
+ if (!this.contentVisible) {
+ return;
}
- const [mapR, mapG, mapB] = maps;
- const bufferR = new Array(256);
- const bufferG = new Array(256);
- const bufferB = new Array(256);
- for (let i = 0; i < 256; i++) {
- bufferR[i] = mapR[i] / 255;
- bufferG[i] = mapG[i] / 255;
- bufferB[i] = mapB[i] / 255;
+ this.save(opIdx);
+ if (this.inSMaskMode) {
+ this.endSMaskMode();
+ this.current.activeSMask = null;
}
- return [bufferR.join(","), bufferG.join(","), bufferB.join(",")];
+ const currentCtx = this.ctx;
+ if (!group.isolated) {
+ info("TODO: Support non-isolated groups.");
+ }
+ if (group.knockout) {
+ warn("Knockout groups not supported.");
+ }
+ const currentTransform = getCurrentTransform(currentCtx);
+ if (group.matrix) {
+ currentCtx.transform(...group.matrix);
+ }
+ if (!group.bbox) {
+ throw new Error("Bounding box is required.");
+ }
+ let bounds = MIN_MAX_INIT.slice();
+ Util.axialAlignedBoundingBox(group.bbox, getCurrentTransform(currentCtx), bounds);
+ const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];
+ bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
+ const offsetX = Math.floor(bounds[0]);
+ const offsetY = Math.floor(bounds[1]);
+ const drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
+ const drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
+ this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);
+ let cacheId = "groupAt" + this.groupLevel;
+ if (group.smask) {
+ cacheId += "_smask_" + this.smaskCounter++ % 2;
+ }
+ const scratchCanvas = this.cachedCanvases.getCanvas(cacheId, drawnWidth, drawnHeight);
+ const groupCtx = scratchCanvas.context;
+ groupCtx.translate(-offsetX, -offsetY);
+ groupCtx.transform(...currentTransform);
+ let clip = new Path2D();
+ const [x0, y0, x1, y1] = group.bbox;
+ clip.rect(x0, y0, x1 - x0, y1 - y0);
+ if (group.matrix) {
+ const path = new Path2D();
+ path.addPath(clip, new DOMMatrix(group.matrix));
+ clip = path;
+ }
+ groupCtx.clip(clip);
+ if (group.smask) {
+ this.smaskStack.push({
+ canvas: scratchCanvas.canvas,
+ context: groupCtx,
+ offsetX,
+ offsetY,
+ subtype: group.smask.subtype,
+ backdrop: group.smask.backdrop,
+ transferMap: group.smask.transferMap || null,
+ startTransformInverse: null
+ });
+ }
+ if (!group.smask || this.dependencyTracker) {
+ currentCtx.setTransform(1, 0, 0, 1, 0, 0);
+ currentCtx.translate(offsetX, offsetY);
+ currentCtx.save();
+ }
+ copyCtxState(currentCtx, groupCtx);
+ this.ctx = groupCtx;
+ this.dependencyTracker?.inheritSimpleDataAsFutureForcedDependencies(["fillAlpha", "strokeAlpha", "globalCompositeOperation"]).pushBaseTransform(currentCtx);
+ this.setGState(opIdx, [["BM", "source-over"], ["ca", 1], ["CA", 1]]);
+ this.groupStack.push(currentCtx);
+ this.groupLevel++;
}
- #createUrl(id) {
- if (this.#baseUrl === undefined) {
- this.#baseUrl = "";
- const url = this.#document.URL;
- if (url !== this.#document.baseURI) {
- if (isDataScheme(url)) {
- warn('#createUrl: ignore "data:"-URL for performance reasons.');
- } else {
- this.#baseUrl = updateUrlHash(url, "");
- }
+ endGroup(opIdx, group) {
+ if (!this.contentVisible) {
+ return;
+ }
+ this.groupLevel--;
+ const groupCtx = this.ctx;
+ const ctx = this.groupStack.pop();
+ this.ctx = ctx;
+ this.ctx.imageSmoothingEnabled = false;
+ this.dependencyTracker?.popBaseTransform();
+ if (group.smask) {
+ this.tempSMask = this.smaskStack.pop();
+ this.restore(opIdx);
+ if (this.dependencyTracker) {
+ this.ctx.restore();
}
+ } else {
+ this.ctx.restore();
+ const currentMtx = getCurrentTransform(this.ctx);
+ this.restore(opIdx);
+ this.ctx.save();
+ this.ctx.setTransform(...currentMtx);
+ const dirtyBox = MIN_MAX_INIT.slice();
+ Util.axialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx, dirtyBox);
+ this.ctx.drawImage(groupCtx.canvas, 0, 0);
+ this.ctx.restore();
+ this.compose(dirtyBox);
}
- return `url(${this.#baseUrl}#${id})`;
}
- addFilter(maps) {
- if (!maps) {
- return "none";
+ beginAnnotation(opIdx, id, rect, transform, matrix, hasOwnCanvas) {
+ this.#restoreInitialState();
+ resetCtxToDefault(this.ctx);
+ this.ctx.save();
+ this.save(opIdx);
+ if (this.baseTransform) {
+ this.ctx.setTransform(...this.baseTransform);
}
- let value = this.#cache.get(maps);
- if (value) {
- return value;
+ if (rect) {
+ const width = rect[2] - rect[0];
+ const height = rect[3] - rect[1];
+ if (hasOwnCanvas && this.annotationCanvasMap) {
+ transform = transform.slice();
+ transform[4] -= rect[0];
+ transform[5] -= rect[1];
+ rect = rect.slice();
+ rect[0] = rect[1] = 0;
+ rect[2] = width;
+ rect[3] = height;
+ Util.singularValueDecompose2dScale(getCurrentTransform(this.ctx), XY);
+ const {
+ viewportScale
+ } = this;
+ const canvasWidth = Math.ceil(width * this.outputScaleX * viewportScale);
+ const canvasHeight = Math.ceil(height * this.outputScaleY * viewportScale);
+ this.annotationCanvas = this.canvasFactory.create(canvasWidth, canvasHeight);
+ const {
+ canvas,
+ context
+ } = this.annotationCanvas;
+ this.annotationCanvasMap.set(id, canvas);
+ this.annotationCanvas.savedCtx = this.ctx;
+ this.ctx = context;
+ this.ctx.save();
+ this.ctx.setTransform(XY[0], 0, 0, -XY[1], 0, height * XY[1]);
+ resetCtxToDefault(this.ctx);
+ } else {
+ resetCtxToDefault(this.ctx);
+ this.endPath(opIdx);
+ const clip = new Path2D();
+ clip.rect(rect[0], rect[1], width, height);
+ this.ctx.clip(clip);
+ }
}
- const [tableR, tableG, tableB] = this.#createTables(maps);
- const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`;
- value = this.#cache.get(key);
- if (value) {
- this.#cache.set(maps, value);
- return value;
+ this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
+ this.transform(opIdx, ...transform);
+ this.transform(opIdx, ...matrix);
+ }
+ endAnnotation(opIdx) {
+ if (this.annotationCanvas) {
+ this.ctx.restore();
+ this.#drawFilter();
+ this.ctx = this.annotationCanvas.savedCtx;
+ delete this.annotationCanvas.savedCtx;
+ delete this.annotationCanvas;
}
- const id = `g_${this.#docId}_transfer_map_${this.#id++}`;
- const url = this.#createUrl(id);
- this.#cache.set(maps, url);
- this.#cache.set(key, url);
- const filter = this.#createFilter(id);
- this.#addTransferMapConversion(tableR, tableG, tableB, filter);
- return url;
}
- addHCMFilter(fgColor, bgColor) {
- const key = `${fgColor}-${bgColor}`;
- const filterName = "base";
- let info = this.#hcmCache.get(filterName);
- if (info?.key === key) {
- return info.url;
+ paintImageMaskXObject(opIdx, img) {
+ if (!this.contentVisible) {
+ return;
}
- if (info) {
- info.filter?.remove();
- info.key = key;
- info.url = "none";
- info.filter = null;
- } else {
- info = {
- key,
- url: "none",
- filter: null
- };
- this.#hcmCache.set(filterName, info);
+ const count = img.count;
+ img = this.getObject(opIdx, img.data, img);
+ img.count = count;
+ const ctx = this.ctx;
+ const mask = this._createMaskCanvas(opIdx, img);
+ const maskCanvas = mask.canvas;
+ ctx.save();
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
+ this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, mask.offsetX, mask.offsetX + maskCanvas.width, mask.offsetY, mask.offsetY + maskCanvas.height).recordOperation(opIdx);
+ ctx.restore();
+ this.compose();
+ }
+ paintImageMaskXObjectRepeat(opIdx, img, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
+ if (!this.contentVisible) {
+ return;
}
- if (!fgColor || !bgColor) {
- return info.url;
+ img = this.getObject(opIdx, img.data, img);
+ const ctx = this.ctx;
+ ctx.save();
+ const currentTransform = getCurrentTransform(ctx);
+ ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
+ const mask = this._createMaskCanvas(opIdx, img);
+ ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]);
+ this.dependencyTracker?.resetBBox(opIdx);
+ for (let i = 0, ii = positions.length; i < ii; i += 2) {
+ const trans = Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]);
+ ctx.drawImage(mask.canvas, trans[4], trans[5]);
+ this.dependencyTracker?.recordBBox(opIdx, this.ctx, trans[4], trans[4] + mask.canvas.width, trans[5], trans[5] + mask.canvas.height);
}
- const fgRGB = this.#getRGB(fgColor);
- fgColor = Util.makeHexColor(...fgRGB);
- const bgRGB = this.#getRGB(bgColor);
- bgColor = Util.makeHexColor(...bgRGB);
- this.#defs.style.color = "";
- if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) {
- return info.url;
+ ctx.restore();
+ this.compose();
+ this.dependencyTracker?.recordOperation(opIdx);
+ }
+ paintImageMaskXObjectGroup(opIdx, images) {
+ if (!this.contentVisible) {
+ return;
}
- const map = new Array(256);
- for (let i = 0; i <= 255; i++) {
- const x = i / 255;
- map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
+ const ctx = this.ctx;
+ const fillColor = this.current.fillColor;
+ const isPatternFill = this.current.patternFill;
+ this.dependencyTracker?.resetBBox(opIdx).recordDependencies(opIdx, Dependencies.transformAndFill);
+ for (const image of images) {
+ const {
+ data,
+ width,
+ height,
+ transform
+ } = image;
+ const maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
+ const maskCtx = maskCanvas.context;
+ maskCtx.save();
+ const img = this.getObject(opIdx, data, image);
+ putBinaryImageMask(maskCtx, img);
+ maskCtx.globalCompositeOperation = "source-in";
+ maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, getCurrentTransformInverse(ctx), PathType.FILL, opIdx) : fillColor;
+ maskCtx.fillRect(0, 0, width, height);
+ maskCtx.restore();
+ ctx.save();
+ ctx.transform(...transform);
+ ctx.scale(1, -1);
+ drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1);
+ this.dependencyTracker?.recordBBox(opIdx, ctx, 0, width, 0, height);
+ ctx.restore();
}
- const table = map.join(",");
- const id = `g_${this.#docId}_hcm_filter`;
- const filter = info.filter = this.#createFilter(id);
- this.#addTransferMapConversion(table, table, table, filter);
- this.#addGrayConversion(filter);
- const getSteps = (c, n) => {
- const start = fgRGB[c] / 255;
- const end = bgRGB[c] / 255;
- const arr = new Array(n + 1);
- for (let i = 0; i <= n; i++) {
- arr[i] = start + i / n * (end - start);
- }
- return arr.join(",");
- };
- this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter);
- info.url = this.#createUrl(id);
- return info.url;
+ this.compose();
+ this.dependencyTracker?.recordOperation(opIdx);
}
- addAlphaFilter(map) {
- let value = this.#cache.get(map);
- if (value) {
- return value;
+ paintImageXObject(opIdx, objId) {
+ if (!this.contentVisible) {
+ return;
}
- const [tableA] = this.#createTables([map]);
- const key = `alpha_${tableA}`;
- value = this.#cache.get(key);
- if (value) {
- this.#cache.set(map, value);
- return value;
+ const imgData = this.getObject(opIdx, objId);
+ if (!imgData) {
+ warn("Dependent image isn't ready yet");
+ return;
}
- const id = `g_${this.#docId}_alpha_map_${this.#id++}`;
- const url = this.#createUrl(id);
- this.#cache.set(map, url);
- this.#cache.set(key, url);
- const filter = this.#createFilter(id);
- this.#addTransferMapAlphaConversion(tableA, filter);
- return url;
+ this.paintInlineImageXObject(opIdx, imgData);
}
- addLuminosityFilter(map) {
- let value = this.#cache.get(map || "luminosity");
- if (value) {
- return value;
+ paintImageXObjectRepeat(opIdx, objId, scaleX, scaleY, positions) {
+ if (!this.contentVisible) {
+ return;
}
- let tableA, key;
- if (map) {
- [tableA] = this.#createTables([map]);
- key = `luminosity_${tableA}`;
- } else {
- key = "luminosity";
+ const imgData = this.getObject(opIdx, objId);
+ if (!imgData) {
+ warn("Dependent image isn't ready yet");
+ return;
}
- value = this.#cache.get(key);
- if (value) {
- this.#cache.set(map, value);
- return value;
+ const width = imgData.width;
+ const height = imgData.height;
+ const map = [];
+ for (let i = 0, ii = positions.length; i < ii; i += 2) {
+ map.push({
+ transform: [scaleX, 0, 0, scaleY, positions[i], positions[i + 1]],
+ x: 0,
+ y: 0,
+ w: width,
+ h: height
+ });
}
- const id = `g_${this.#docId}_luminosity_map_${this.#id++}`;
- const url = this.#createUrl(id);
- this.#cache.set(map, url);
- this.#cache.set(key, url);
- const filter = this.#createFilter(id);
- this.#addLuminosityConversion(filter);
- if (map) {
- this.#addTransferMapAlphaConversion(tableA, filter);
+ this.paintInlineImageXObjectGroup(opIdx, imgData, map);
+ }
+ applyTransferMapsToCanvas(ctx) {
+ if (this.current.transferMaps !== "none") {
+ ctx.filter = this.current.transferMaps;
+ ctx.drawImage(ctx.canvas, 0, 0);
+ ctx.filter = "none";
}
- return url;
+ return ctx.canvas;
}
- addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
- const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`;
- let info = this.#hcmCache.get(filterName);
- if (info?.key === key) {
- return info.url;
+ applyTransferMapsToBitmap(imgData) {
+ if (this.current.transferMaps === "none") {
+ return imgData.bitmap;
}
- if (info) {
- info.filter?.remove();
- info.key = key;
- info.url = "none";
- info.filter = null;
+ const {
+ bitmap,
+ width,
+ height
+ } = imgData;
+ const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
+ const tmpCtx = tmpCanvas.context;
+ tmpCtx.filter = this.current.transferMaps;
+ tmpCtx.drawImage(bitmap, 0, 0);
+ tmpCtx.filter = "none";
+ return tmpCanvas.canvas;
+ }
+ paintInlineImageXObject(opIdx, imgData) {
+ if (!this.contentVisible) {
+ return;
+ }
+ const width = imgData.width;
+ const height = imgData.height;
+ const ctx = this.ctx;
+ this.save(opIdx);
+ const {
+ filter
+ } = ctx;
+ if (filter !== "none" && filter !== "") {
+ ctx.filter = "none";
+ }
+ ctx.scale(1 / width, -1 / height);
+ let imgToPaint;
+ if (imgData.bitmap) {
+ imgToPaint = this.applyTransferMapsToBitmap(imgData);
+ } else if (typeof HTMLElement === "function" && imgData instanceof HTMLElement || !imgData.data) {
+ imgToPaint = imgData;
} else {
- info = {
- key,
- url: "none",
- filter: null
- };
- this.#hcmCache.set(filterName, info);
+ const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
+ const tmpCtx = tmpCanvas.context;
+ putBinaryImageData(tmpCtx, imgData);
+ imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
+ }
+ const scaled = this._scaleImage(imgToPaint, getCurrentTransformInverse(ctx));
+ ctx.imageSmoothingEnabled = getImageSmoothingEnabled(getCurrentTransform(ctx), imgData.interpolate);
+ this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, ctx, 0, width, -height, 0).recordDependencies(opIdx, Dependencies.imageXObject).recordOperation(opIdx);
+ drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height);
+ this.compose();
+ this.restore(opIdx);
+ }
+ paintInlineImageXObjectGroup(opIdx, imgData, map) {
+ if (!this.contentVisible) {
+ return;
}
- if (!fgColor || !bgColor) {
- return info.url;
+ const ctx = this.ctx;
+ let imgToPaint;
+ if (imgData.bitmap) {
+ imgToPaint = imgData.bitmap;
+ } else {
+ const w = imgData.width;
+ const h = imgData.height;
+ const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h);
+ const tmpCtx = tmpCanvas.context;
+ putBinaryImageData(tmpCtx, imgData);
+ imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
}
- const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this));
- let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]);
- let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]);
- let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this));
- if (bgGray < fgGray) {
- [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB];
+ this.dependencyTracker?.resetBBox(opIdx);
+ for (const entry of map) {
+ ctx.save();
+ ctx.transform(...entry.transform);
+ ctx.scale(1, -1);
+ drawImageAtIntegerCoords(ctx, imgToPaint, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1);
+ this.dependencyTracker?.recordBBox(opIdx, ctx, 0, 1, -1, 0);
+ ctx.restore();
}
- this.#defs.style.color = "";
- const getSteps = (fg, bg, n) => {
- const arr = new Array(256);
- const step = (bgGray - fgGray) / n;
- const newStart = fg / 255;
- const newStep = (bg - fg) / (255 * n);
- let prev = 0;
- for (let i = 0; i <= n; i++) {
- const k = Math.round(fgGray + i * step);
- const value = newStart + i * newStep;
- for (let j = prev; j <= k; j++) {
- arr[j] = value;
- }
- prev = k + 1;
- }
- for (let i = prev; i < 256; i++) {
- arr[i] = arr[prev - 1];
- }
- return arr.join(",");
- };
- const id = `g_${this.#docId}_hcm_${filterName}_filter`;
- const filter = info.filter = this.#createFilter(id);
- this.#addGrayConversion(filter);
- this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter);
- info.url = this.#createUrl(id);
- return info.url;
+ this.dependencyTracker?.recordOperation(opIdx);
+ this.compose();
}
- destroy(keepHCM = false) {
- if (keepHCM && this.#_hcmCache?.size) {
+ paintSolidColorImageMask(opIdx) {
+ if (!this.contentVisible) {
return;
}
- this.#_defs?.parentNode.parentNode.remove();
- this.#_defs = null;
- this.#_cache?.clear();
- this.#_cache = null;
- this.#_hcmCache?.clear();
- this.#_hcmCache = null;
- this.#id = 0;
- }
- #addLuminosityConversion(filter) {
- const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
- feColorMatrix.setAttribute("type", "matrix");
- feColorMatrix.setAttribute("values", "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0");
- filter.append(feColorMatrix);
- }
- #addGrayConversion(filter) {
- const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
- feColorMatrix.setAttribute("type", "matrix");
- feColorMatrix.setAttribute("values", "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0");
- filter.append(feColorMatrix);
- }
- #createFilter(id) {
- const filter = this.#document.createElementNS(SVG_NS, "filter");
- filter.setAttribute("color-interpolation-filters", "sRGB");
- filter.setAttribute("id", id);
- this.#defs.append(filter);
- return filter;
- }
- #appendFeFunc(feComponentTransfer, func, table) {
- const feFunc = this.#document.createElementNS(SVG_NS, func);
- feFunc.setAttribute("type", "discrete");
- feFunc.setAttribute("tableValues", table);
- feComponentTransfer.append(feFunc);
+ this.dependencyTracker?.resetBBox(opIdx).recordBBox(opIdx, this.ctx, 0, 1, 0, 1).recordDependencies(opIdx, Dependencies.fill).recordOperation(opIdx);
+ this.ctx.fillRect(0, 0, 1, 1);
+ this.compose();
}
- #addTransferMapConversion(rTable, gTable, bTable, filter) {
- const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
- filter.append(feComponentTransfer);
- this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable);
- this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable);
- this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable);
+ markPoint(opIdx, tag) {}
+ markPointProps(opIdx, tag, properties) {}
+ beginMarkedContent(opIdx, tag) {
+ this.dependencyTracker?.beginMarkedContent(opIdx);
+ this.markedContentStack.push({
+ visible: true
+ });
}
- #addTransferMapAlphaConversion(aTable, filter) {
- const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
- filter.append(feComponentTransfer);
- this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable);
+ beginMarkedContentProps(opIdx, tag, properties) {
+ this.dependencyTracker?.beginMarkedContent(opIdx);
+ if (tag === "OC") {
+ this.markedContentStack.push({
+ visible: this.optionalContentConfig.isVisible(properties)
+ });
+ } else {
+ this.markedContentStack.push({
+ visible: true
+ });
+ }
+ this.contentVisible = this.isContentVisible();
}
- #getRGB(color) {
- this.#defs.style.color = color;
- return getRGB(getComputedStyle(this.#defs).getPropertyValue("color"));
+ endMarkedContent(opIdx) {
+ this.dependencyTracker?.endMarkedContent(opIdx);
+ this.markedContentStack.pop();
+ this.contentVisible = this.isContentVisible();
}
-}
-
-;// ./src/shared/obj-bin-transform.js
-
-class CssFontInfo {
- #buffer;
- #view;
- #decoder;
- static strings = ["fontFamily", "fontWeight", "italicAngle"];
- static write(info) {
- const encoder = new TextEncoder();
- const encodedStrings = {};
- let stringsLength = 0;
- for (const prop of CssFontInfo.strings) {
- const encoded = encoder.encode(info[prop]);
- encodedStrings[prop] = encoded;
- stringsLength += 4 + encoded.length;
+ beginCompat(opIdx) {}
+ endCompat(opIdx) {}
+ consumePath(opIdx, path, clipBox) {
+ const isEmpty = this.current.isEmptyClip();
+ if (this.pendingClip) {
+ this.current.updateClipFromPath();
}
- const buffer = new ArrayBuffer(stringsLength);
- const data = new Uint8Array(buffer);
- const view = new DataView(buffer);
- let offset = 0;
- for (const prop of CssFontInfo.strings) {
- const encoded = encodedStrings[prop];
- const length = encoded.length;
- view.setUint32(offset, length);
- data.set(encoded, offset + 4);
- offset += 4 + length;
+ if (!this.pendingClip) {
+ this.compose(clipBox);
}
- assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow");
- return buffer;
+ const ctx = this.ctx;
+ if (this.pendingClip) {
+ if (!isEmpty) {
+ if (this.pendingClip === EO_CLIP) {
+ ctx.clip(path, "evenodd");
+ } else {
+ ctx.clip(path);
+ }
+ }
+ this.pendingClip = null;
+ this.dependencyTracker?.bboxToClipBoxDropOperation(opIdx).recordFutureForcedDependency("clipPath", opIdx);
+ } else {
+ this.dependencyTracker?.recordOperation(opIdx);
+ }
+ this.current.startNewPathAndClipBox(this.current.clipBox);
}
- constructor(buffer) {
- this.#buffer = buffer;
- this.#view = new DataView(this.#buffer);
- this.#decoder = new TextDecoder();
+ getSinglePixelWidth() {
+ if (!this._cachedGetSinglePixelWidth) {
+ const m = getCurrentTransform(this.ctx);
+ if (m[1] === 0 && m[2] === 0) {
+ this._cachedGetSinglePixelWidth = 1 / Math.min(Math.abs(m[0]), Math.abs(m[3]));
+ } else {
+ const absDet = Math.abs(m[0] * m[3] - m[2] * m[1]);
+ const normX = Math.hypot(m[0], m[2]);
+ const normY = Math.hypot(m[1], m[3]);
+ this._cachedGetSinglePixelWidth = Math.max(normX, normY) / absDet;
+ }
+ }
+ return this._cachedGetSinglePixelWidth;
}
- #readString(index) {
- assert(index < CssFontInfo.strings.length, "Invalid string index");
- let offset = 0;
- for (let i = 0; i < index; i++) {
- offset += this.#view.getUint32(offset) + 4;
+ getScaleForStroking() {
+ if (this._cachedScaleForStroking[0] === -1) {
+ const {
+ lineWidth
+ } = this.current;
+ const {
+ a,
+ b,
+ c,
+ d
+ } = this.ctx.getTransform();
+ let scaleX, scaleY;
+ if (b === 0 && c === 0) {
+ const normX = Math.abs(a);
+ const normY = Math.abs(d);
+ if (normX === normY) {
+ if (lineWidth === 0) {
+ scaleX = scaleY = 1 / normX;
+ } else {
+ const scaledLineWidth = normX * lineWidth;
+ scaleX = scaleY = scaledLineWidth < 1 ? 1 / scaledLineWidth : 1;
+ }
+ } else if (lineWidth === 0) {
+ scaleX = 1 / normX;
+ scaleY = 1 / normY;
+ } else {
+ const scaledXLineWidth = normX * lineWidth;
+ const scaledYLineWidth = normY * lineWidth;
+ scaleX = scaledXLineWidth < 1 ? 1 / scaledXLineWidth : 1;
+ scaleY = scaledYLineWidth < 1 ? 1 / scaledYLineWidth : 1;
+ }
+ } else {
+ const absDet = Math.abs(a * d - b * c);
+ const normX = Math.hypot(a, b);
+ const normY = Math.hypot(c, d);
+ if (lineWidth === 0) {
+ scaleX = normY / absDet;
+ scaleY = normX / absDet;
+ } else {
+ const baseArea = lineWidth * absDet;
+ scaleX = normY > baseArea ? normY / baseArea : 1;
+ scaleY = normX > baseArea ? normX / baseArea : 1;
+ }
+ }
+ this._cachedScaleForStroking[0] = scaleX;
+ this._cachedScaleForStroking[1] = scaleY;
}
- const length = this.#view.getUint32(offset);
- return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
- }
- get fontFamily() {
- return this.#readString(0);
- }
- get fontWeight() {
- return this.#readString(1);
- }
- get italicAngle() {
- return this.#readString(2);
+ return this._cachedScaleForStroking;
}
-}
-class SystemFontInfo {
- #buffer;
- #view;
- #decoder;
- static strings = ["css", "loadedName", "baseFontName", "src"];
- static write(info) {
- const encoder = new TextEncoder();
- const encodedStrings = {};
- let stringsLength = 0;
- for (const prop of SystemFontInfo.strings) {
- const encoded = encoder.encode(info[prop]);
- encodedStrings[prop] = encoded;
- stringsLength += 4 + encoded.length;
+ rescaleAndStroke(path, saveRestore) {
+ const {
+ ctx,
+ current: {
+ lineWidth
+ }
+ } = this;
+ const [scaleX, scaleY] = this.getScaleForStroking();
+ if (scaleX === scaleY) {
+ ctx.lineWidth = (lineWidth || 1) * scaleX;
+ ctx.stroke(path);
+ return;
}
- stringsLength += 4;
- let encodedStyleStyle,
- encodedStyleWeight,
- lengthEstimate = 1 + stringsLength;
- if (info.style) {
- encodedStyleStyle = encoder.encode(info.style.style);
- encodedStyleWeight = encoder.encode(info.style.weight);
- lengthEstimate += 4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length;
+ const dashes = ctx.getLineDash();
+ if (saveRestore) {
+ ctx.save();
}
- const buffer = new ArrayBuffer(lengthEstimate);
- const data = new Uint8Array(buffer);
- const view = new DataView(buffer);
- let offset = 0;
- view.setUint8(offset++, info.guessFallback ? 1 : 0);
- view.setUint32(offset, 0);
- offset += 4;
- stringsLength = 0;
- for (const prop of SystemFontInfo.strings) {
- const encoded = encodedStrings[prop];
- const length = encoded.length;
- stringsLength += 4 + length;
- view.setUint32(offset, length);
- data.set(encoded, offset + 4);
- offset += 4 + length;
+ ctx.scale(scaleX, scaleY);
+ SCALE_MATRIX.a = 1 / scaleX;
+ SCALE_MATRIX.d = 1 / scaleY;
+ const newPath = new Path2D();
+ newPath.addPath(path, SCALE_MATRIX);
+ if (dashes.length > 0) {
+ const scale = Math.max(scaleX, scaleY);
+ ctx.setLineDash(dashes.map(x => x / scale));
+ ctx.lineDashOffset /= scale;
}
- view.setUint32(offset - stringsLength - 4, stringsLength);
- if (info.style) {
- view.setUint32(offset, encodedStyleStyle.length);
- data.set(encodedStyleStyle, offset + 4);
- offset += 4 + encodedStyleStyle.length;
- view.setUint32(offset, encodedStyleWeight.length);
- data.set(encodedStyleWeight, offset + 4);
- offset += 4 + encodedStyleWeight.length;
+ ctx.lineWidth = lineWidth || 1;
+ ctx.stroke(newPath);
+ if (saveRestore) {
+ ctx.restore();
}
- assert(offset <= buffer.byteLength, "SubstitionInfo.write: Buffer overflow");
- return buffer.transferToFixedLength(offset);
- }
- constructor(buffer) {
- this.#buffer = buffer;
- this.#view = new DataView(this.#buffer);
- this.#decoder = new TextDecoder();
- }
- get guessFallback() {
- return this.#view.getUint8(0) !== 0;
}
- #readString(index) {
- assert(index < SystemFontInfo.strings.length, "Invalid string index");
- let offset = 5;
- for (let i = 0; i < index; i++) {
- offset += this.#view.getUint32(offset) + 4;
+ isContentVisible() {
+ for (let i = this.markedContentStack.length - 1; i >= 0; i--) {
+ if (!this.markedContentStack[i].visible) {
+ return false;
+ }
}
- const length = this.#view.getUint32(offset);
- return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
- }
- get css() {
- return this.#readString(0);
- }
- get loadedName() {
- return this.#readString(1);
- }
- get baseFontName() {
- return this.#readString(2);
- }
- get src() {
- return this.#readString(3);
+ return true;
}
- get style() {
- let offset = 1;
- offset += 4 + this.#view.getUint32(offset);
- const styleLength = this.#view.getUint32(offset);
- const style = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, styleLength));
- offset += 4 + styleLength;
- const weightLength = this.#view.getUint32(offset);
- const weight = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, weightLength));
- return {
- style,
- weight
- };
+}
+for (const op in OPS) {
+ if (CanvasGraphics.prototype[op] !== undefined) {
+ CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
}
}
-class FontInfo {
- static bools = ["black", "bold", "disableFontFace", "fontExtraProperties", "isInvalidPDFjsFont", "isType3Font", "italic", "missingFile", "remeasure", "vertical"];
- static numbers = ["ascent", "defaultWidth", "descent"];
- static strings = ["fallbackName", "loadedName", "mimetype", "name"];
- static #OFFSET_NUMBERS = Math.ceil(this.bools.length * 2 / 8);
- static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8;
- static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4;
- static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6;
- static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3;
- #buffer;
- #decoder;
- #view;
+
+;// ./src/display/canvas_factory.js
+
+class BaseCanvasFactory {
+ #enableHWA = false;
constructor({
- data,
- extra
+ enableHWA = false
}) {
- this.#buffer = data;
- this.#decoder = new TextDecoder();
- this.#view = new DataView(this.#buffer);
- if (extra) {
- Object.assign(this, extra);
- }
- }
- #readBoolean(index) {
- assert(index < FontInfo.bools.length, "Invalid boolean index");
- const byteOffset = Math.floor(index / 4);
- const bitOffset = index * 2 % 8;
- const value = this.#view.getUint8(byteOffset) >> bitOffset & 0x03;
- return value === 0x00 ? undefined : value === 0x02;
+ this.#enableHWA = enableHWA;
}
- get black() {
- return this.#readBoolean(0);
+ create(width, height) {
+ if (width <= 0 || height <= 0) {
+ throw new Error("Invalid canvas size");
+ }
+ const canvas = this._createCanvas(width, height);
+ return {
+ canvas,
+ context: canvas.getContext("2d", {
+ willReadFrequently: !this.#enableHWA
+ })
+ };
}
- get bold() {
- return this.#readBoolean(1);
+ reset(canvasAndContext, width, height) {
+ if (!canvasAndContext.canvas) {
+ throw new Error("Canvas is not specified");
+ }
+ if (width <= 0 || height <= 0) {
+ throw new Error("Invalid canvas size");
+ }
+ canvasAndContext.canvas.width = width;
+ canvasAndContext.canvas.height = height;
}
- get disableFontFace() {
- return this.#readBoolean(2);
+ destroy(canvasAndContext) {
+ if (!canvasAndContext.canvas) {
+ throw new Error("Canvas is not specified");
+ }
+ canvasAndContext.canvas.width = 0;
+ canvasAndContext.canvas.height = 0;
+ canvasAndContext.canvas = null;
+ canvasAndContext.context = null;
}
- get fontExtraProperties() {
- return this.#readBoolean(3);
+ _createCanvas(width, height) {
+ unreachable("Abstract method `_createCanvas` called.");
}
- get isInvalidPDFjsFont() {
- return this.#readBoolean(4);
+}
+class DOMCanvasFactory extends BaseCanvasFactory {
+ constructor({
+ ownerDocument = globalThis.document,
+ enableHWA = false
+ }) {
+ super({
+ enableHWA
+ });
+ this._document = ownerDocument;
}
- get isType3Font() {
- return this.#readBoolean(5);
+ _createCanvas(width, height) {
+ const canvas = this._document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+ return canvas;
}
- get italic() {
- return this.#readBoolean(6);
+}
+
+;// ./src/display/stubs.js
+const DOMCMapReaderFactory = null;
+const DOMWasmFactory = null;
+const DOMStandardFontDataFactory = null;
+const NodeCanvasFactory = null;
+const NodeCMapReaderFactory = null;
+const NodeFilterFactory = null;
+const NodeWasmFactory = null;
+const NodeStandardFontDataFactory = null;
+const PDFFetchStream = null;
+const PDFNetworkStream = null;
+const PDFNodeStream = null;
+
+;// ./src/display/filter_factory.js
+
+
+class BaseFilterFactory {
+ addFilter(maps) {
+ return "none";
}
- get missingFile() {
- return this.#readBoolean(7);
+ addHCMFilter(fgColor, bgColor) {
+ return "none";
}
- get remeasure() {
- return this.#readBoolean(8);
+ addAlphaFilter(map) {
+ return "none";
}
- get vertical() {
- return this.#readBoolean(9);
+ addLuminosityFilter(map) {
+ return "none";
}
- #readNumber(index) {
- assert(index < FontInfo.numbers.length, "Invalid number index");
- return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8);
+ addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
+ return "none";
}
- get ascent() {
- return this.#readNumber(0);
+ destroy(keepHCM = false) {}
+}
+class DOMFilterFactory extends BaseFilterFactory {
+ #baseUrl;
+ #_cache;
+ #_defs;
+ #docId;
+ #document;
+ #_hcmCache;
+ #id = 0;
+ constructor({
+ docId,
+ ownerDocument = globalThis.document
+ }) {
+ super();
+ this.#docId = docId;
+ this.#document = ownerDocument;
}
- get defaultWidth() {
- return this.#readNumber(1);
+ get #cache() {
+ return this.#_cache ||= new Map();
}
- get descent() {
- return this.#readNumber(2);
+ get #hcmCache() {
+ return this.#_hcmCache ||= new Map();
}
- get bbox() {
- let offset = FontInfo.#OFFSET_BBOX;
- const numCoords = this.#view.getUint8(offset);
- if (numCoords === 0) {
- return undefined;
- }
- offset += 1;
- const bbox = [];
- for (let i = 0; i < 4; i++) {
- bbox.push(this.#view.getInt16(offset, true));
- offset += 2;
+ get #defs() {
+ if (!this.#_defs) {
+ const div = this.#document.createElement("div");
+ const {
+ style
+ } = div;
+ style.visibility = "hidden";
+ style.contain = "strict";
+ style.width = style.height = 0;
+ style.position = "absolute";
+ style.top = style.left = 0;
+ style.zIndex = -1;
+ const svg = this.#document.createElementNS(SVG_NS, "svg");
+ svg.setAttribute("width", 0);
+ svg.setAttribute("height", 0);
+ this.#_defs = this.#document.createElementNS(SVG_NS, "defs");
+ div.append(svg);
+ svg.append(this.#_defs);
+ this.#document.body.append(div);
}
- return bbox;
+ return this.#_defs;
}
- get fontMatrix() {
- let offset = FontInfo.#OFFSET_FONT_MATRIX;
- const numPoints = this.#view.getUint8(offset);
- if (numPoints === 0) {
- return undefined;
+ #createTables(maps) {
+ if (maps.length === 1) {
+ const mapR = maps[0];
+ const buffer = new Array(256);
+ for (let i = 0; i < 256; i++) {
+ buffer[i] = mapR[i] / 255;
+ }
+ const table = buffer.join(",");
+ return [table, table, table];
}
- offset += 1;
- const fontMatrix = [];
- for (let i = 0; i < 6; i++) {
- fontMatrix.push(this.#view.getFloat64(offset, true));
- offset += 8;
+ const [mapR, mapG, mapB] = maps;
+ const bufferR = new Array(256);
+ const bufferG = new Array(256);
+ const bufferB = new Array(256);
+ for (let i = 0; i < 256; i++) {
+ bufferR[i] = mapR[i] / 255;
+ bufferG[i] = mapG[i] / 255;
+ bufferB[i] = mapB[i] / 255;
}
- return fontMatrix;
+ return [bufferR.join(","), bufferG.join(","), bufferB.join(",")];
}
- get defaultVMetrics() {
- let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS;
- const numMetrics = this.#view.getUint8(offset);
- if (numMetrics === 0) {
- return undefined;
- }
- offset += 1;
- const defaultVMetrics = [];
- for (let i = 0; i < 3; i++) {
- defaultVMetrics.push(this.#view.getInt16(offset, true));
- offset += 2;
+ #createUrl(id) {
+ if (this.#baseUrl === undefined) {
+ this.#baseUrl = "";
+ const url = this.#document.URL;
+ if (url !== this.#document.baseURI) {
+ if (isDataScheme(url)) {
+ warn('#createUrl: ignore "data:"-URL for performance reasons.');
+ } else {
+ this.#baseUrl = updateUrlHash(url, "");
+ }
+ }
}
- return defaultVMetrics;
+ return `url(${this.#baseUrl}#${id})`;
}
- #readString(index) {
- assert(index < FontInfo.strings.length, "Invalid string index");
- let offset = FontInfo.#OFFSET_STRINGS + 4;
- for (let i = 0; i < index; i++) {
- offset += this.#view.getUint32(offset) + 4;
+ addFilter(maps) {
+ if (!maps) {
+ return "none";
}
- const length = this.#view.getUint32(offset);
- const stringData = new Uint8Array(length);
- stringData.set(new Uint8Array(this.#buffer, offset + 4, length));
- return this.#decoder.decode(stringData);
- }
- get fallbackName() {
- return this.#readString(0);
- }
- get loadedName() {
- return this.#readString(1);
- }
- get mimetype() {
- return this.#readString(2);
- }
- get name() {
- return this.#readString(3);
- }
- get data() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + systemFontInfoLength;
- const cssFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + cssFontInfoLength;
- const length = this.#view.getUint32(offset);
- if (length === 0) {
- return undefined;
+ let value = this.#cache.get(maps);
+ if (value) {
+ return value;
}
- return new Uint8Array(this.#buffer, offset + 4, length);
- }
- clearData() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + systemFontInfoLength;
- const cssFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + cssFontInfoLength;
- const length = this.#view.getUint32(offset);
- const data = new Uint8Array(this.#buffer, offset + 4, length);
- data.fill(0);
- this.#view.setUint32(offset, 0);
- }
- get cssFontInfo() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + systemFontInfoLength;
- const cssFontInfoLength = this.#view.getUint32(offset);
- if (cssFontInfoLength === 0) {
- return null;
+ const [tableR, tableG, tableB] = this.#createTables(maps);
+ const key = maps.length === 1 ? tableR : `${tableR}${tableG}${tableB}`;
+ value = this.#cache.get(key);
+ if (value) {
+ this.#cache.set(maps, value);
+ return value;
}
- const cssFontInfoData = new Uint8Array(cssFontInfoLength);
- cssFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength));
- return new CssFontInfo(cssFontInfoData.buffer);
+ const id = `g_${this.#docId}_transfer_map_${this.#id++}`;
+ const url = this.#createUrl(id);
+ this.#cache.set(maps, url);
+ this.#cache.set(key, url);
+ const filter = this.#createFilter(id);
+ this.#addTransferMapConversion(tableR, tableG, tableB, filter);
+ return url;
}
- get systemFontInfo() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- if (systemFontInfoLength === 0) {
- return null;
+ addHCMFilter(fgColor, bgColor) {
+ const key = `${fgColor}-${bgColor}`;
+ const filterName = "base";
+ let info = this.#hcmCache.get(filterName);
+ if (info?.key === key) {
+ return info.url;
}
- const systemFontInfoData = new Uint8Array(systemFontInfoLength);
- systemFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength));
- return new SystemFontInfo(systemFontInfoData.buffer);
- }
- static write(font) {
- const systemFontInfoBuffer = font.systemFontInfo ? SystemFontInfo.write(font.systemFontInfo) : null;
- const cssFontInfoBuffer = font.cssFontInfo ? CssFontInfo.write(font.cssFontInfo) : null;
- const encoder = new TextEncoder();
- const encodedStrings = {};
- let stringsLength = 0;
- for (const prop of FontInfo.strings) {
- encodedStrings[prop] = encoder.encode(font[prop]);
- stringsLength += 4 + encodedStrings[prop].length;
+ if (info) {
+ info.filter?.remove();
+ info.key = key;
+ info.url = "none";
+ info.filter = null;
+ } else {
+ info = {
+ key,
+ url: "none",
+ filter: null
+ };
+ this.#hcmCache.set(filterName, info);
+ }
+ if (!fgColor || !bgColor) {
+ return info.url;
+ }
+ const fgRGB = this.#getRGB(fgColor);
+ fgColor = Util.makeHexColor(...fgRGB);
+ const bgRGB = this.#getRGB(bgColor);
+ bgColor = Util.makeHexColor(...bgRGB);
+ this.#defs.style.color = "";
+ if (fgColor === "#000000" && bgColor === "#ffffff" || fgColor === bgColor) {
+ return info.url;
+ }
+ const map = new Array(256);
+ for (let i = 0; i <= 255; i++) {
+ const x = i / 255;
+ map[i] = x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
}
- const lengthEstimate = FontInfo.#OFFSET_STRINGS + 4 + stringsLength + 4 + (systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) + 4 + (cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) + 4 + (font.data ? font.data.length : 0);
- const buffer = new ArrayBuffer(lengthEstimate);
- const data = new Uint8Array(buffer);
- const view = new DataView(buffer);
- let offset = 0;
- const numBools = FontInfo.bools.length;
- let boolByte = 0,
- boolBit = 0;
- for (let i = 0; i < numBools; i++) {
- const value = font[FontInfo.bools[i]];
- const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01;
- boolByte |= bits << boolBit;
- boolBit += 2;
- if (boolBit === 8 || i === numBools - 1) {
- view.setUint8(offset++, boolByte);
- boolByte = 0;
- boolBit = 0;
+ const table = map.join(",");
+ const id = `g_${this.#docId}_hcm_filter`;
+ const filter = info.filter = this.#createFilter(id);
+ this.#addTransferMapConversion(table, table, table, filter);
+ this.#addGrayConversion(filter);
+ const getSteps = (c, n) => {
+ const start = fgRGB[c] / 255;
+ const end = bgRGB[c] / 255;
+ const arr = new Array(n + 1);
+ for (let i = 0; i <= n; i++) {
+ arr[i] = start + i / n * (end - start);
}
+ return arr.join(",");
+ };
+ this.#addTransferMapConversion(getSteps(0, 5), getSteps(1, 5), getSteps(2, 5), filter);
+ info.url = this.#createUrl(id);
+ return info.url;
+ }
+ addAlphaFilter(map) {
+ let value = this.#cache.get(map);
+ if (value) {
+ return value;
}
- assert(offset === FontInfo.#OFFSET_NUMBERS, "FontInfo.write: Boolean properties offset mismatch");
- for (const prop of FontInfo.numbers) {
- view.setFloat64(offset, font[prop]);
- offset += 8;
+ const [tableA] = this.#createTables([map]);
+ const key = `alpha_${tableA}`;
+ value = this.#cache.get(key);
+ if (value) {
+ this.#cache.set(map, value);
+ return value;
}
- assert(offset === FontInfo.#OFFSET_BBOX, "FontInfo.write: Number properties offset mismatch");
- if (font.bbox) {
- view.setUint8(offset++, 4);
- for (const coord of font.bbox) {
- view.setInt16(offset, coord, true);
- offset += 2;
- }
- } else {
- view.setUint8(offset++, 0);
- offset += 2 * 4;
+ const id = `g_${this.#docId}_alpha_map_${this.#id++}`;
+ const url = this.#createUrl(id);
+ this.#cache.set(map, url);
+ this.#cache.set(key, url);
+ const filter = this.#createFilter(id);
+ this.#addTransferMapAlphaConversion(tableA, filter);
+ return url;
+ }
+ addLuminosityFilter(map) {
+ let value = this.#cache.get(map || "luminosity");
+ if (value) {
+ return value;
}
- assert(offset === FontInfo.#OFFSET_FONT_MATRIX, "FontInfo.write: BBox properties offset mismatch");
- if (font.fontMatrix) {
- view.setUint8(offset++, 6);
- for (const point of font.fontMatrix) {
- view.setFloat64(offset, point, true);
- offset += 8;
- }
+ let tableA, key;
+ if (map) {
+ [tableA] = this.#createTables([map]);
+ key = `luminosity_${tableA}`;
} else {
- view.setUint8(offset++, 0);
- offset += 8 * 6;
+ key = "luminosity";
}
- assert(offset === FontInfo.#OFFSET_DEFAULT_VMETRICS, "FontInfo.write: FontMatrix properties offset mismatch");
- if (font.defaultVMetrics) {
- view.setUint8(offset++, 1);
- for (const metric of font.defaultVMetrics) {
- view.setInt16(offset, metric, true);
- offset += 2;
- }
- } else {
- view.setUint8(offset++, 0);
- offset += 3 * 2;
+ value = this.#cache.get(key);
+ if (value) {
+ this.#cache.set(map, value);
+ return value;
}
- assert(offset === FontInfo.#OFFSET_STRINGS, "FontInfo.write: DefaultVMetrics properties offset mismatch");
- view.setUint32(FontInfo.#OFFSET_STRINGS, 0);
- offset += 4;
- for (const prop of FontInfo.strings) {
- const encoded = encodedStrings[prop];
- const length = encoded.length;
- view.setUint32(offset, length);
- data.set(encoded, offset + 4);
- offset += 4 + length;
+ const id = `g_${this.#docId}_luminosity_map_${this.#id++}`;
+ const url = this.#createUrl(id);
+ this.#cache.set(map, url);
+ this.#cache.set(key, url);
+ const filter = this.#createFilter(id);
+ this.#addLuminosityConversion(filter);
+ if (map) {
+ this.#addTransferMapAlphaConversion(tableA, filter);
}
- view.setUint32(FontInfo.#OFFSET_STRINGS, offset - FontInfo.#OFFSET_STRINGS - 4);
- if (!systemFontInfoBuffer) {
- view.setUint32(offset, 0);
- offset += 4;
- } else {
- const length = systemFontInfoBuffer.byteLength;
- view.setUint32(offset, length);
- assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at systemFontInfo");
- data.set(new Uint8Array(systemFontInfoBuffer), offset + 4);
- offset += 4 + length;
+ return url;
+ }
+ addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
+ const key = `${fgColor}-${bgColor}-${newFgColor}-${newBgColor}`;
+ let info = this.#hcmCache.get(filterName);
+ if (info?.key === key) {
+ return info.url;
}
- if (!cssFontInfoBuffer) {
- view.setUint32(offset, 0);
- offset += 4;
+ if (info) {
+ info.filter?.remove();
+ info.key = key;
+ info.url = "none";
+ info.filter = null;
} else {
- const length = cssFontInfoBuffer.byteLength;
- view.setUint32(offset, length);
- assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at cssFontInfo");
- data.set(new Uint8Array(cssFontInfoBuffer), offset + 4);
- offset += 4 + length;
+ info = {
+ key,
+ url: "none",
+ filter: null
+ };
+ this.#hcmCache.set(filterName, info);
}
- if (font.data === undefined) {
- view.setUint32(offset, 0);
- offset += 4;
- } else {
- view.setUint32(offset, font.data.length);
- data.set(font.data, offset + 4);
- offset += 4 + font.data.length;
+ if (!fgColor || !bgColor) {
+ return info.url;
}
- assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow");
- return buffer.transferToFixedLength(offset);
+ const [fgRGB, bgRGB] = [fgColor, bgColor].map(this.#getRGB.bind(this));
+ let fgGray = Math.round(0.2126 * fgRGB[0] + 0.7152 * fgRGB[1] + 0.0722 * fgRGB[2]);
+ let bgGray = Math.round(0.2126 * bgRGB[0] + 0.7152 * bgRGB[1] + 0.0722 * bgRGB[2]);
+ let [newFgRGB, newBgRGB] = [newFgColor, newBgColor].map(this.#getRGB.bind(this));
+ if (bgGray < fgGray) {
+ [fgGray, bgGray, newFgRGB, newBgRGB] = [bgGray, fgGray, newBgRGB, newFgRGB];
+ }
+ this.#defs.style.color = "";
+ const getSteps = (fg, bg, n) => {
+ const arr = new Array(256);
+ const step = (bgGray - fgGray) / n;
+ const newStart = fg / 255;
+ const newStep = (bg - fg) / (255 * n);
+ let prev = 0;
+ for (let i = 0; i <= n; i++) {
+ const k = Math.round(fgGray + i * step);
+ const value = newStart + i * newStep;
+ for (let j = prev; j <= k; j++) {
+ arr[j] = value;
+ }
+ prev = k + 1;
+ }
+ for (let i = prev; i < 256; i++) {
+ arr[i] = arr[prev - 1];
+ }
+ return arr.join(",");
+ };
+ const id = `g_${this.#docId}_hcm_${filterName}_filter`;
+ const filter = info.filter = this.#createFilter(id);
+ this.#addGrayConversion(filter);
+ this.#addTransferMapConversion(getSteps(newFgRGB[0], newBgRGB[0], 5), getSteps(newFgRGB[1], newBgRGB[1], 5), getSteps(newFgRGB[2], newBgRGB[2], 5), filter);
+ info.url = this.#createUrl(id);
+ return info.url;
+ }
+ destroy(keepHCM = false) {
+ if (keepHCM && this.#_hcmCache?.size) {
+ return;
+ }
+ this.#_defs?.parentNode.parentNode.remove();
+ this.#_defs = null;
+ this.#_cache?.clear();
+ this.#_cache = null;
+ this.#_hcmCache?.clear();
+ this.#_hcmCache = null;
+ this.#id = 0;
+ }
+ #addLuminosityConversion(filter) {
+ const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
+ feColorMatrix.setAttribute("type", "matrix");
+ feColorMatrix.setAttribute("values", "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3 0.59 0.11 0 0");
+ filter.append(feColorMatrix);
+ }
+ #addGrayConversion(filter) {
+ const feColorMatrix = this.#document.createElementNS(SVG_NS, "feColorMatrix");
+ feColorMatrix.setAttribute("type", "matrix");
+ feColorMatrix.setAttribute("values", "0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0");
+ filter.append(feColorMatrix);
+ }
+ #createFilter(id) {
+ const filter = this.#document.createElementNS(SVG_NS, "filter");
+ filter.setAttribute("color-interpolation-filters", "sRGB");
+ filter.setAttribute("id", id);
+ this.#defs.append(filter);
+ return filter;
+ }
+ #appendFeFunc(feComponentTransfer, func, table) {
+ const feFunc = this.#document.createElementNS(SVG_NS, func);
+ feFunc.setAttribute("type", "discrete");
+ feFunc.setAttribute("tableValues", table);
+ feComponentTransfer.append(feFunc);
+ }
+ #addTransferMapConversion(rTable, gTable, bTable, filter) {
+ const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
+ filter.append(feComponentTransfer);
+ this.#appendFeFunc(feComponentTransfer, "feFuncR", rTable);
+ this.#appendFeFunc(feComponentTransfer, "feFuncG", gTable);
+ this.#appendFeFunc(feComponentTransfer, "feFuncB", bTable);
+ }
+ #addTransferMapAlphaConversion(aTable, filter) {
+ const feComponentTransfer = this.#document.createElementNS(SVG_NS, "feComponentTransfer");
+ filter.append(feComponentTransfer);
+ this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable);
+ }
+ #getRGB(color) {
+ this.#defs.style.color = color;
+ return getRGB(getComputedStyle(this.#defs).getPropertyValue("color"));
}
}
@@ -12802,7 +13032,7 @@ function getDocument(src = {}) {
}
const docParams = {
docId,
- apiVersion: "5.4.329",
+ apiVersion: "5.4.370",
data,
password,
disableAutoFetch,
@@ -13987,9 +14217,12 @@ class WorkerTransport {
break;
case "FontPath":
case "Image":
- case "Pattern":
this.commonObjs.resolve(id, exportedData);
break;
+ case "Pattern":
+ const pattern = new PatternInfo(exportedData);
+ this.commonObjs.resolve(id, pattern.getIR());
+ break;
default:
throw new Error(`Got unknown common object type ${type}`);
}
@@ -14379,8 +14612,8 @@ class InternalRenderTask {
}
}
}
-const version = "5.4.329";
-const build = "3eca60735";
+const version = "5.4.370";
+const build = "f6317ddbb";
;// ./src/display/editor/color_picker.js
@@ -14636,11 +14869,11 @@ class BasicColorPicker {
const {
editorType,
colorType,
- colorValue
+ color
} = this.#editor;
const input = this.#input = document.createElement("input");
input.type = "color";
- input.value = colorValue || "#000000";
+ input.value = color || "#000000";
input.className = "basicColorPicker";
input.tabIndex = 0;
input.setAttribute("data-l10n-id", BasicColorPicker.#l10nColor[editorType]);
@@ -16962,6 +17195,9 @@ class PopupElement {
}
this.#firstElement.commentText = this.#commentText = text;
}
+ focus() {
+ this.#firstElement.container?.focus();
+ }
get parentBoundingClientRect() {
return this.#firstElement.layer.getBoundingClientRect();
}
@@ -18213,6 +18449,7 @@ class FreeTextEditor extends AnnotationEditor {
if (!this.annotationElementId) {
this._uiManager.a11yAlert("pdfjs-editor-freetext-added-alert");
}
+ this.canAddComment = false;
}
static initialize(l10n, uiManager) {
AnnotationEditor.initialize(l10n, uiManager);
diff --git a/toolkit/components/pdfjs/content/build/pdf.scripting.mjs b/toolkit/components/pdfjs/content/build/pdf.scripting.mjs
@@ -21,8 +21,8 @@
*/
/**
- * pdfjsVersion = 5.4.329
- * pdfjsBuild = 3eca60735
+ * pdfjsVersion = 5.4.370
+ * pdfjsBuild = f6317ddbb
*/
var __webpack_exports__ = {};
diff --git a/toolkit/components/pdfjs/content/build/pdf.worker.mjs b/toolkit/components/pdfjs/content/build/pdf.worker.mjs
@@ -21,8 +21,8 @@
*/
/**
- * pdfjsVersion = 5.4.329
- * pdfjsBuild = 3eca60735
+ * pdfjsVersion = 5.4.370
+ * pdfjsBuild = f6317ddbb
*/
/******/ // The require scope
/******/ var __webpack_require__ = {};
@@ -331,7 +331,8 @@ const DrawOPS = {
moveTo: 0,
lineTo: 1,
curveTo: 2,
- closePath: 3
+ quadraticCurveTo: 3,
+ closePath: 4
};
const PasswordResponses = {
NEED_PASSWORD: 1,
@@ -520,6 +521,9 @@ class FeatureTest {
static get isImageDecoderSupported() {
return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined");
}
+ static get isFloat16ArraySupported() {
+ return shadow(this, "isFloat16ArraySupported", typeof Float16Array !== "undefined");
+ }
static get platform() {
const {
platform,
@@ -1293,6 +1297,9 @@ class BaseStream {
getBaseStreams() {
return null;
}
+ getOriginalStream() {
+ return this.stream?.getOriginalStream() || this;
+ }
}
;// ./src/core/core_utils.js
@@ -3898,7 +3905,7 @@ class DecodeStream extends BaseStream {
return new Stream(this.buffer, start, length, dict);
}
getBaseStreams() {
- return this.str ? this.str.getBaseStreams() : null;
+ return this.stream ? this.stream.getBaseStreams() : null;
}
}
class StreamsSequenceStream extends DecodeStream {
@@ -6979,7 +6986,7 @@ class Ascii85Stream extends DecodeStream {
maybeLength *= 0.8;
}
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
this.input = new Uint8Array(5);
}
@@ -6987,7 +6994,7 @@ class Ascii85Stream extends DecodeStream {
const TILDA_CHAR = 0x7e;
const Z_LOWER_CHAR = 0x7a;
const EOF = -1;
- const str = this.str;
+ const str = this.stream;
let c = str.getByte();
while (isWhiteSpace(c)) {
c = str.getByte();
@@ -7045,13 +7052,13 @@ class AsciiHexStream extends DecodeStream {
maybeLength *= 0.5;
}
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
this.firstDigit = -1;
}
readBlock() {
const UPSTREAM_BLOCK_SIZE = 8000;
- const bytes = this.str.getBytes(UPSTREAM_BLOCK_SIZE);
+ const bytes = this.stream.getBytes(UPSTREAM_BLOCK_SIZE);
if (!bytes.length) {
this.eof = true;
return;
@@ -7605,7 +7612,7 @@ class CCITTFaxDecoder {
class CCITTFaxStream extends DecodeStream {
constructor(str, maybeLength, params) {
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
if (!(params instanceof Dict)) {
params = Dict.empty;
@@ -7650,7 +7657,7 @@ const fixedDistCodeTab = [new Int32Array([0x50000, 0x50010, 0x50008, 0x50018, 0x
class FlateStream extends DecodeStream {
constructor(str, maybeLength) {
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
const cmf = str.getByte();
const flg = str.getByte();
@@ -7680,8 +7687,8 @@ class FlateStream extends DecodeStream {
return data.subarray(0, length);
}
async asyncGetBytes() {
- this.str.reset();
- const bytes = this.str.getBytes();
+ this.stream.reset();
+ const bytes = this.stream.getBytes();
try {
const {
readable,
@@ -7707,7 +7714,7 @@ class FlateStream extends DecodeStream {
}
return data;
} catch {
- this.str = new Stream(bytes, 2, bytes.length, this.str.dict);
+ this.stream = new Stream(bytes, 2, bytes.length, this.stream.dict);
this.reset();
return null;
}
@@ -7716,7 +7723,7 @@ class FlateStream extends DecodeStream {
return true;
}
getBits(bits) {
- const str = this.str;
+ const str = this.stream;
let codeSize = this.codeSize;
let codeBuf = this.codeBuf;
let b;
@@ -7733,7 +7740,7 @@ class FlateStream extends DecodeStream {
return b;
}
getCode(table) {
- const str = this.str;
+ const str = this.stream;
const codes = table[0];
const maxLen = table[1];
let codeSize = this.codeSize;
@@ -7791,7 +7798,7 @@ class FlateStream extends DecodeStream {
}
readBlock() {
let buffer, hdr, len;
- const str = this.str;
+ const str = this.stream;
try {
hdr = this.getBits(3);
} catch (ex) {
@@ -10103,7 +10110,7 @@ class JpxStream extends DecodeStream {
class LZWStream extends DecodeStream {
constructor(str, maybeLength, earlyChange) {
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
this.cachedData = 0;
this.bitsCached = 0;
@@ -10128,7 +10135,7 @@ class LZWStream extends DecodeStream {
let bitsCached = this.bitsCached;
let cachedData = this.cachedData;
while (bitsCached < n) {
- const c = this.str.getByte();
+ const c = this.stream.getByte();
if (c === -1) {
this.eof = true;
return null;
@@ -10233,7 +10240,7 @@ class PredictorStream extends DecodeStream {
throw new FormatError(`Unsupported predictor: ${predictor}`);
}
this.readBlock = predictor === 2 ? this.readBlockTiff : this.readBlockPng;
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
const colors = this.colors = params.get("Colors") || 1;
const bits = this.bits = params.get("BPC", "BitsPerComponent") || 8;
@@ -10248,7 +10255,7 @@ class PredictorStream extends DecodeStream {
const buffer = this.ensureBuffer(bufferLength + rowBytes);
const bits = this.bits;
const colors = this.colors;
- const rawBytes = this.str.getBytes(rowBytes);
+ const rawBytes = this.stream.getBytes(rowBytes);
this.eof = !rawBytes.length;
if (this.eof) {
return;
@@ -10317,8 +10324,8 @@ class PredictorStream extends DecodeStream {
readBlockPng() {
const rowBytes = this.rowBytes;
const pixBytes = this.pixBytes;
- const predictor = this.str.getByte();
- const rawBytes = this.str.getBytes(rowBytes);
+ const predictor = this.stream.getByte();
+ const rawBytes = this.stream.getBytes(rowBytes);
this.eof = !rawBytes.length;
if (this.eof) {
return;
@@ -10407,11 +10414,11 @@ class PredictorStream extends DecodeStream {
class RunLengthStream extends DecodeStream {
constructor(str, maybeLength) {
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
}
readBlock() {
- const repeatHeader = this.str.getBytes(2);
+ const repeatHeader = this.stream.getBytes(2);
if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] === 128) {
this.eof = true;
return;
@@ -10423,7 +10430,7 @@ class RunLengthStream extends DecodeStream {
buffer = this.ensureBuffer(bufferLength + n + 1);
buffer[bufferLength++] = repeatHeader[1];
if (n > 0) {
- const source = this.str.getBytes(n);
+ const source = this.stream.getBytes(n);
buffer.set(source, bufferLength);
bufferLength += n;
}
@@ -19534,16 +19541,16 @@ function lookupCmap(ranges, unicode) {
function compileGlyf(code, cmds, font) {
function moveTo(x, y) {
if (firstPoint) {
- cmds.add("L", firstPoint);
+ cmds.add(DrawOPS.lineTo, firstPoint);
}
firstPoint = [x, y];
- cmds.add("M", [x, y]);
+ cmds.add(DrawOPS.moveTo, [x, y]);
}
function lineTo(x, y) {
- cmds.add("L", [x, y]);
+ cmds.add(DrawOPS.lineTo, [x, y]);
}
function quadraticCurveTo(xa, ya, x, y) {
- cmds.add("Q", [xa, ya, x, y]);
+ cmds.add(DrawOPS.quadraticCurveTo, [xa, ya, x, y]);
}
let i = 0;
const numberOfContours = readInt16(code, i);
@@ -19696,16 +19703,16 @@ function compileGlyf(code, cmds, font) {
function compileCharString(charStringCode, cmds, font, glyphId) {
function moveTo(x, y) {
if (firstPoint) {
- cmds.add("L", firstPoint);
+ cmds.add(DrawOPS.lineTo, firstPoint);
}
firstPoint = [x, y];
- cmds.add("M", [x, y]);
+ cmds.add(DrawOPS.moveTo, [x, y]);
}
function lineTo(x, y) {
- cmds.add("L", [x, y]);
+ cmds.add(DrawOPS.lineTo, [x, y]);
}
function bezierCurveTo(x1, y1, x2, y2, x, y) {
- cmds.add("C", [x1, y1, x2, y2, x, y]);
+ cmds.add(DrawOPS.curveTo, [x1, y1, x2, y2, x, y]);
}
const stack = [];
let x = 0,
@@ -20059,7 +20066,7 @@ class Commands {
for (let i = 0, ii = args.length; i < ii; i += 2) {
Util.applyTransform(args, currentTransform, i);
}
- this.cmds.push(`${cmd}${args.join(" ")}`);
+ this.cmds.push(cmd, ...args);
} else {
this.cmds.push(cmd);
}
@@ -20076,8 +20083,8 @@ class Commands {
restore() {
this.currentTransform = this.transformStack.pop() || [1, 0, 0, 1, 0, 0];
}
- getSVG() {
- return this.cmds.join("");
+ getPath() {
+ return new Float16Array(this.cmds);
}
}
class CompiledFont {
@@ -20126,8 +20133,8 @@ class CompiledFont {
const cmds = new Commands();
cmds.transform(fontMatrix.slice());
this.compileGlyphImpl(code, cmds, glyphId);
- cmds.add("Z");
- return cmds.getSVG();
+ cmds.add(DrawOPS.closePath);
+ return cmds.getPath();
}
compileGlyphImpl() {
unreachable("Children classes should implement this.");
@@ -27183,6 +27190,660 @@ class ErrorFont {
}
}
+;// ./src/shared/obj-bin-transform.js
+
+class CssFontInfo {
+ #buffer;
+ #view;
+ #decoder;
+ static strings = ["fontFamily", "fontWeight", "italicAngle"];
+ static write(info) {
+ const encoder = new TextEncoder();
+ const encodedStrings = {};
+ let stringsLength = 0;
+ for (const prop of CssFontInfo.strings) {
+ const encoded = encoder.encode(info[prop]);
+ encodedStrings[prop] = encoded;
+ stringsLength += 4 + encoded.length;
+ }
+ const buffer = new ArrayBuffer(stringsLength);
+ const data = new Uint8Array(buffer);
+ const view = new DataView(buffer);
+ let offset = 0;
+ for (const prop of CssFontInfo.strings) {
+ const encoded = encodedStrings[prop];
+ const length = encoded.length;
+ view.setUint32(offset, length);
+ data.set(encoded, offset + 4);
+ offset += 4 + length;
+ }
+ assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow");
+ return buffer;
+ }
+ constructor(buffer) {
+ this.#buffer = buffer;
+ this.#view = new DataView(this.#buffer);
+ this.#decoder = new TextDecoder();
+ }
+ #readString(index) {
+ assert(index < CssFontInfo.strings.length, "Invalid string index");
+ let offset = 0;
+ for (let i = 0; i < index; i++) {
+ offset += this.#view.getUint32(offset) + 4;
+ }
+ const length = this.#view.getUint32(offset);
+ return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
+ }
+ get fontFamily() {
+ return this.#readString(0);
+ }
+ get fontWeight() {
+ return this.#readString(1);
+ }
+ get italicAngle() {
+ return this.#readString(2);
+ }
+}
+class SystemFontInfo {
+ #buffer;
+ #view;
+ #decoder;
+ static strings = ["css", "loadedName", "baseFontName", "src"];
+ static write(info) {
+ const encoder = new TextEncoder();
+ const encodedStrings = {};
+ let stringsLength = 0;
+ for (const prop of SystemFontInfo.strings) {
+ const encoded = encoder.encode(info[prop]);
+ encodedStrings[prop] = encoded;
+ stringsLength += 4 + encoded.length;
+ }
+ stringsLength += 4;
+ let encodedStyleStyle,
+ encodedStyleWeight,
+ lengthEstimate = 1 + stringsLength;
+ if (info.style) {
+ encodedStyleStyle = encoder.encode(info.style.style);
+ encodedStyleWeight = encoder.encode(info.style.weight);
+ lengthEstimate += 4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length;
+ }
+ const buffer = new ArrayBuffer(lengthEstimate);
+ const data = new Uint8Array(buffer);
+ const view = new DataView(buffer);
+ let offset = 0;
+ view.setUint8(offset++, info.guessFallback ? 1 : 0);
+ view.setUint32(offset, 0);
+ offset += 4;
+ stringsLength = 0;
+ for (const prop of SystemFontInfo.strings) {
+ const encoded = encodedStrings[prop];
+ const length = encoded.length;
+ stringsLength += 4 + length;
+ view.setUint32(offset, length);
+ data.set(encoded, offset + 4);
+ offset += 4 + length;
+ }
+ view.setUint32(offset - stringsLength - 4, stringsLength);
+ if (info.style) {
+ view.setUint32(offset, encodedStyleStyle.length);
+ data.set(encodedStyleStyle, offset + 4);
+ offset += 4 + encodedStyleStyle.length;
+ view.setUint32(offset, encodedStyleWeight.length);
+ data.set(encodedStyleWeight, offset + 4);
+ offset += 4 + encodedStyleWeight.length;
+ }
+ assert(offset <= buffer.byteLength, "SubstitionInfo.write: Buffer overflow");
+ return buffer.transferToFixedLength(offset);
+ }
+ constructor(buffer) {
+ this.#buffer = buffer;
+ this.#view = new DataView(this.#buffer);
+ this.#decoder = new TextDecoder();
+ }
+ get guessFallback() {
+ return this.#view.getUint8(0) !== 0;
+ }
+ #readString(index) {
+ assert(index < SystemFontInfo.strings.length, "Invalid string index");
+ let offset = 5;
+ for (let i = 0; i < index; i++) {
+ offset += this.#view.getUint32(offset) + 4;
+ }
+ const length = this.#view.getUint32(offset);
+ return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
+ }
+ get css() {
+ return this.#readString(0);
+ }
+ get loadedName() {
+ return this.#readString(1);
+ }
+ get baseFontName() {
+ return this.#readString(2);
+ }
+ get src() {
+ return this.#readString(3);
+ }
+ get style() {
+ let offset = 1;
+ offset += 4 + this.#view.getUint32(offset);
+ const styleLength = this.#view.getUint32(offset);
+ const style = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, styleLength));
+ offset += 4 + styleLength;
+ const weightLength = this.#view.getUint32(offset);
+ const weight = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, weightLength));
+ return {
+ style,
+ weight
+ };
+ }
+}
+class FontInfo {
+ static bools = ["black", "bold", "disableFontFace", "fontExtraProperties", "isInvalidPDFjsFont", "isType3Font", "italic", "missingFile", "remeasure", "vertical"];
+ static numbers = ["ascent", "defaultWidth", "descent"];
+ static strings = ["fallbackName", "loadedName", "mimetype", "name"];
+ static #OFFSET_NUMBERS = Math.ceil(this.bools.length * 2 / 8);
+ static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8;
+ static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4;
+ static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6;
+ static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3;
+ #buffer;
+ #decoder;
+ #view;
+ constructor({
+ data,
+ extra
+ }) {
+ this.#buffer = data;
+ this.#decoder = new TextDecoder();
+ this.#view = new DataView(this.#buffer);
+ if (extra) {
+ Object.assign(this, extra);
+ }
+ }
+ #readBoolean(index) {
+ assert(index < FontInfo.bools.length, "Invalid boolean index");
+ const byteOffset = Math.floor(index / 4);
+ const bitOffset = index * 2 % 8;
+ const value = this.#view.getUint8(byteOffset) >> bitOffset & 0x03;
+ return value === 0x00 ? undefined : value === 0x02;
+ }
+ get black() {
+ return this.#readBoolean(0);
+ }
+ get bold() {
+ return this.#readBoolean(1);
+ }
+ get disableFontFace() {
+ return this.#readBoolean(2);
+ }
+ get fontExtraProperties() {
+ return this.#readBoolean(3);
+ }
+ get isInvalidPDFjsFont() {
+ return this.#readBoolean(4);
+ }
+ get isType3Font() {
+ return this.#readBoolean(5);
+ }
+ get italic() {
+ return this.#readBoolean(6);
+ }
+ get missingFile() {
+ return this.#readBoolean(7);
+ }
+ get remeasure() {
+ return this.#readBoolean(8);
+ }
+ get vertical() {
+ return this.#readBoolean(9);
+ }
+ #readNumber(index) {
+ assert(index < FontInfo.numbers.length, "Invalid number index");
+ return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8);
+ }
+ get ascent() {
+ return this.#readNumber(0);
+ }
+ get defaultWidth() {
+ return this.#readNumber(1);
+ }
+ get descent() {
+ return this.#readNumber(2);
+ }
+ get bbox() {
+ let offset = FontInfo.#OFFSET_BBOX;
+ const numCoords = this.#view.getUint8(offset);
+ if (numCoords === 0) {
+ return undefined;
+ }
+ offset += 1;
+ const bbox = [];
+ for (let i = 0; i < 4; i++) {
+ bbox.push(this.#view.getInt16(offset, true));
+ offset += 2;
+ }
+ return bbox;
+ }
+ get fontMatrix() {
+ let offset = FontInfo.#OFFSET_FONT_MATRIX;
+ const numPoints = this.#view.getUint8(offset);
+ if (numPoints === 0) {
+ return undefined;
+ }
+ offset += 1;
+ const fontMatrix = [];
+ for (let i = 0; i < 6; i++) {
+ fontMatrix.push(this.#view.getFloat64(offset, true));
+ offset += 8;
+ }
+ return fontMatrix;
+ }
+ get defaultVMetrics() {
+ let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS;
+ const numMetrics = this.#view.getUint8(offset);
+ if (numMetrics === 0) {
+ return undefined;
+ }
+ offset += 1;
+ const defaultVMetrics = [];
+ for (let i = 0; i < 3; i++) {
+ defaultVMetrics.push(this.#view.getInt16(offset, true));
+ offset += 2;
+ }
+ return defaultVMetrics;
+ }
+ #readString(index) {
+ assert(index < FontInfo.strings.length, "Invalid string index");
+ let offset = FontInfo.#OFFSET_STRINGS + 4;
+ for (let i = 0; i < index; i++) {
+ offset += this.#view.getUint32(offset) + 4;
+ }
+ const length = this.#view.getUint32(offset);
+ const stringData = new Uint8Array(length);
+ stringData.set(new Uint8Array(this.#buffer, offset + 4, length));
+ return this.#decoder.decode(stringData);
+ }
+ get fallbackName() {
+ return this.#readString(0);
+ }
+ get loadedName() {
+ return this.#readString(1);
+ }
+ get mimetype() {
+ return this.#readString(2);
+ }
+ get name() {
+ return this.#readString(3);
+ }
+ get data() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + systemFontInfoLength;
+ const cssFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + cssFontInfoLength;
+ const length = this.#view.getUint32(offset);
+ if (length === 0) {
+ return undefined;
+ }
+ return new Uint8Array(this.#buffer, offset + 4, length);
+ }
+ clearData() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + systemFontInfoLength;
+ const cssFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + cssFontInfoLength;
+ const length = this.#view.getUint32(offset);
+ const data = new Uint8Array(this.#buffer, offset + 4, length);
+ data.fill(0);
+ this.#view.setUint32(offset, 0);
+ }
+ get cssFontInfo() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ offset += 4 + systemFontInfoLength;
+ const cssFontInfoLength = this.#view.getUint32(offset);
+ if (cssFontInfoLength === 0) {
+ return null;
+ }
+ const cssFontInfoData = new Uint8Array(cssFontInfoLength);
+ cssFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength));
+ return new CssFontInfo(cssFontInfoData.buffer);
+ }
+ get systemFontInfo() {
+ let offset = FontInfo.#OFFSET_STRINGS;
+ const stringsLength = this.#view.getUint32(offset);
+ offset += 4 + stringsLength;
+ const systemFontInfoLength = this.#view.getUint32(offset);
+ if (systemFontInfoLength === 0) {
+ return null;
+ }
+ const systemFontInfoData = new Uint8Array(systemFontInfoLength);
+ systemFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength));
+ return new SystemFontInfo(systemFontInfoData.buffer);
+ }
+ static write(font) {
+ const systemFontInfoBuffer = font.systemFontInfo ? SystemFontInfo.write(font.systemFontInfo) : null;
+ const cssFontInfoBuffer = font.cssFontInfo ? CssFontInfo.write(font.cssFontInfo) : null;
+ const encoder = new TextEncoder();
+ const encodedStrings = {};
+ let stringsLength = 0;
+ for (const prop of FontInfo.strings) {
+ encodedStrings[prop] = encoder.encode(font[prop]);
+ stringsLength += 4 + encodedStrings[prop].length;
+ }
+ const lengthEstimate = FontInfo.#OFFSET_STRINGS + 4 + stringsLength + 4 + (systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) + 4 + (cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) + 4 + (font.data ? font.data.length : 0);
+ const buffer = new ArrayBuffer(lengthEstimate);
+ const data = new Uint8Array(buffer);
+ const view = new DataView(buffer);
+ let offset = 0;
+ const numBools = FontInfo.bools.length;
+ let boolByte = 0,
+ boolBit = 0;
+ for (let i = 0; i < numBools; i++) {
+ const value = font[FontInfo.bools[i]];
+ const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01;
+ boolByte |= bits << boolBit;
+ boolBit += 2;
+ if (boolBit === 8 || i === numBools - 1) {
+ view.setUint8(offset++, boolByte);
+ boolByte = 0;
+ boolBit = 0;
+ }
+ }
+ assert(offset === FontInfo.#OFFSET_NUMBERS, "FontInfo.write: Boolean properties offset mismatch");
+ for (const prop of FontInfo.numbers) {
+ view.setFloat64(offset, font[prop]);
+ offset += 8;
+ }
+ assert(offset === FontInfo.#OFFSET_BBOX, "FontInfo.write: Number properties offset mismatch");
+ if (font.bbox) {
+ view.setUint8(offset++, 4);
+ for (const coord of font.bbox) {
+ view.setInt16(offset, coord, true);
+ offset += 2;
+ }
+ } else {
+ view.setUint8(offset++, 0);
+ offset += 2 * 4;
+ }
+ assert(offset === FontInfo.#OFFSET_FONT_MATRIX, "FontInfo.write: BBox properties offset mismatch");
+ if (font.fontMatrix) {
+ view.setUint8(offset++, 6);
+ for (const point of font.fontMatrix) {
+ view.setFloat64(offset, point, true);
+ offset += 8;
+ }
+ } else {
+ view.setUint8(offset++, 0);
+ offset += 8 * 6;
+ }
+ assert(offset === FontInfo.#OFFSET_DEFAULT_VMETRICS, "FontInfo.write: FontMatrix properties offset mismatch");
+ if (font.defaultVMetrics) {
+ view.setUint8(offset++, 1);
+ for (const metric of font.defaultVMetrics) {
+ view.setInt16(offset, metric, true);
+ offset += 2;
+ }
+ } else {
+ view.setUint8(offset++, 0);
+ offset += 3 * 2;
+ }
+ assert(offset === FontInfo.#OFFSET_STRINGS, "FontInfo.write: DefaultVMetrics properties offset mismatch");
+ view.setUint32(FontInfo.#OFFSET_STRINGS, 0);
+ offset += 4;
+ for (const prop of FontInfo.strings) {
+ const encoded = encodedStrings[prop];
+ const length = encoded.length;
+ view.setUint32(offset, length);
+ data.set(encoded, offset + 4);
+ offset += 4 + length;
+ }
+ view.setUint32(FontInfo.#OFFSET_STRINGS, offset - FontInfo.#OFFSET_STRINGS - 4);
+ if (!systemFontInfoBuffer) {
+ view.setUint32(offset, 0);
+ offset += 4;
+ } else {
+ const length = systemFontInfoBuffer.byteLength;
+ view.setUint32(offset, length);
+ assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at systemFontInfo");
+ data.set(new Uint8Array(systemFontInfoBuffer), offset + 4);
+ offset += 4 + length;
+ }
+ if (!cssFontInfoBuffer) {
+ view.setUint32(offset, 0);
+ offset += 4;
+ } else {
+ const length = cssFontInfoBuffer.byteLength;
+ view.setUint32(offset, length);
+ assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at cssFontInfo");
+ data.set(new Uint8Array(cssFontInfoBuffer), offset + 4);
+ offset += 4 + length;
+ }
+ if (font.data === undefined) {
+ view.setUint32(offset, 0);
+ offset += 4;
+ } else {
+ view.setUint32(offset, font.data.length);
+ data.set(font.data, offset + 4);
+ offset += 4 + font.data.length;
+ }
+ assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow");
+ return buffer.transferToFixedLength(offset);
+ }
+}
+class PatternInfo {
+ static #KIND = 0;
+ static #HAS_BBOX = 1;
+ static #HAS_BACKGROUND = 2;
+ static #SHADING_TYPE = 3;
+ static #N_COORD = 4;
+ static #N_COLOR = 8;
+ static #N_STOP = 12;
+ static #N_FIGURES = 16;
+ constructor(buffer) {
+ this.buffer = buffer;
+ this.view = new DataView(buffer);
+ this.data = new Uint8Array(buffer);
+ }
+ static write(ir) {
+ let kind,
+ bbox = null,
+ coords = [],
+ colors = [],
+ colorStops = [],
+ figures = [],
+ shadingType = null,
+ background = null;
+ switch (ir[0]) {
+ case "RadialAxial":
+ kind = ir[1] === "axial" ? 1 : 2;
+ bbox = ir[2];
+ colorStops = ir[3];
+ if (kind === 1) {
+ coords.push(...ir[4], ...ir[5]);
+ } else {
+ coords.push(ir[4][0], ir[4][1], ir[6], ir[5][0], ir[5][1], ir[7]);
+ }
+ break;
+ case "Mesh":
+ kind = 3;
+ shadingType = ir[1];
+ coords = ir[2];
+ colors = ir[3];
+ figures = ir[4] || [];
+ bbox = ir[6];
+ background = ir[7];
+ break;
+ default:
+ throw new Error(`Unsupported pattern type: ${ir[0]}`);
+ }
+ const nCoord = Math.floor(coords.length / 2);
+ const nColor = Math.floor(colors.length / 3);
+ const nStop = colorStops.length;
+ const nFigures = figures.length;
+ let figuresSize = 0;
+ for (const figure of figures) {
+ figuresSize += 1;
+ figuresSize = Math.ceil(figuresSize / 4) * 4;
+ figuresSize += 4 + figure.coords.length * 4;
+ figuresSize += 4 + figure.colors.length * 4;
+ if (figure.verticesPerRow !== undefined) {
+ figuresSize += 4;
+ }
+ }
+ const byteLen = 20 + nCoord * 8 + nColor * 3 + nStop * 8 + (bbox ? 16 : 0) + (background ? 3 : 0) + figuresSize;
+ const buffer = new ArrayBuffer(byteLen);
+ const dataView = new DataView(buffer);
+ const u8data = new Uint8Array(buffer);
+ dataView.setUint8(PatternInfo.#KIND, kind);
+ dataView.setUint8(PatternInfo.#HAS_BBOX, bbox ? 1 : 0);
+ dataView.setUint8(PatternInfo.#HAS_BACKGROUND, background ? 1 : 0);
+ dataView.setUint8(PatternInfo.#SHADING_TYPE, shadingType);
+ dataView.setUint32(PatternInfo.#N_COORD, nCoord, true);
+ dataView.setUint32(PatternInfo.#N_COLOR, nColor, true);
+ dataView.setUint32(PatternInfo.#N_STOP, nStop, true);
+ dataView.setUint32(PatternInfo.#N_FIGURES, nFigures, true);
+ let offset = 20;
+ const coordsView = new Float32Array(buffer, offset, nCoord * 2);
+ coordsView.set(coords);
+ offset += nCoord * 8;
+ u8data.set(colors, offset);
+ offset += nColor * 3;
+ for (const [pos, hex] of colorStops) {
+ dataView.setFloat32(offset, pos, true);
+ offset += 4;
+ dataView.setUint32(offset, parseInt(hex.slice(1), 16), true);
+ offset += 4;
+ }
+ if (bbox) {
+ for (const v of bbox) {
+ dataView.setFloat32(offset, v, true);
+ offset += 4;
+ }
+ }
+ if (background) {
+ u8data.set(background, offset);
+ offset += 3;
+ }
+ for (let i = 0; i < figures.length; i++) {
+ const figure = figures[i];
+ dataView.setUint8(offset, figure.type);
+ offset += 1;
+ offset = Math.ceil(offset / 4) * 4;
+ dataView.setUint32(offset, figure.coords.length, true);
+ offset += 4;
+ const figureCoordsView = new Int32Array(buffer, offset, figure.coords.length);
+ figureCoordsView.set(figure.coords);
+ offset += figure.coords.length * 4;
+ dataView.setUint32(offset, figure.colors.length, true);
+ offset += 4;
+ const colorsView = new Int32Array(buffer, offset, figure.colors.length);
+ colorsView.set(figure.colors);
+ offset += figure.colors.length * 4;
+ if (figure.verticesPerRow !== undefined) {
+ dataView.setUint32(offset, figure.verticesPerRow, true);
+ offset += 4;
+ }
+ }
+ return buffer;
+ }
+ getIR() {
+ const dataView = this.view;
+ const kind = this.data[PatternInfo.#KIND];
+ const hasBBox = !!this.data[PatternInfo.#HAS_BBOX];
+ const hasBackground = !!this.data[PatternInfo.#HAS_BACKGROUND];
+ const nCoord = dataView.getUint32(PatternInfo.#N_COORD, true);
+ const nColor = dataView.getUint32(PatternInfo.#N_COLOR, true);
+ const nStop = dataView.getUint32(PatternInfo.#N_STOP, true);
+ const nFigures = dataView.getUint32(PatternInfo.#N_FIGURES, true);
+ let offset = 20;
+ const coords = new Float32Array(this.buffer, offset, nCoord * 2);
+ offset += nCoord * 8;
+ const colors = new Uint8Array(this.buffer, offset, nColor * 3);
+ offset += nColor * 3;
+ const stops = [];
+ for (let i = 0; i < nStop; ++i) {
+ const p = dataView.getFloat32(offset, true);
+ offset += 4;
+ const rgb = dataView.getUint32(offset, true);
+ offset += 4;
+ stops.push([p, `#${rgb.toString(16).padStart(6, "0")}`]);
+ }
+ let bbox = null;
+ if (hasBBox) {
+ bbox = [];
+ for (let i = 0; i < 4; ++i) {
+ bbox.push(dataView.getFloat32(offset, true));
+ offset += 4;
+ }
+ }
+ let background = null;
+ if (hasBackground) {
+ background = new Uint8Array(this.buffer, offset, 3);
+ offset += 3;
+ }
+ const figures = [];
+ for (let i = 0; i < nFigures; ++i) {
+ const type = dataView.getUint8(offset);
+ offset += 1;
+ offset = Math.ceil(offset / 4) * 4;
+ const coordsLength = dataView.getUint32(offset, true);
+ offset += 4;
+ const figureCoords = new Int32Array(this.buffer, offset, coordsLength);
+ offset += coordsLength * 4;
+ const colorsLength = dataView.getUint32(offset, true);
+ offset += 4;
+ const figureColors = new Int32Array(this.buffer, offset, colorsLength);
+ offset += colorsLength * 4;
+ const figure = {
+ type,
+ coords: figureCoords,
+ colors: figureColors
+ };
+ if (type === MeshFigureType.LATTICE) {
+ figure.verticesPerRow = dataView.getUint32(offset, true);
+ offset += 4;
+ }
+ figures.push(figure);
+ }
+ if (kind === 1) {
+ return ["RadialAxial", "axial", bbox, stops, Array.from(coords.slice(0, 2)), Array.from(coords.slice(2, 4)), null, null];
+ }
+ if (kind === 2) {
+ return ["RadialAxial", "radial", bbox, stops, [coords[0], coords[1]], [coords[3], coords[4]], coords[2], coords[5]];
+ }
+ if (kind === 3) {
+ const shadingType = this.data[PatternInfo.#SHADING_TYPE];
+ let bounds = null;
+ if (coords.length > 0) {
+ let minX = coords[0],
+ maxX = coords[0];
+ let minY = coords[1],
+ maxY = coords[1];
+ for (let i = 0; i < coords.length; i += 2) {
+ const x = coords[i],
+ y = coords[i + 1];
+ minX = minX > x ? x : minX;
+ minY = minY > y ? y : minY;
+ maxX = maxX < x ? x : maxX;
+ maxY = maxY < y ? y : maxY;
+ }
+ bounds = [minX, minY, maxX, maxY];
+ }
+ return ["Mesh", shadingType, coords, colors, figures, bounds, bbox, background];
+ }
+ throw new Error(`Unsupported pattern kind: ${kind}`);
+ }
+}
+
;// ./src/core/pattern.js
@@ -29911,456 +30572,6 @@ function bidi(str, startLevel = -1, vertical = false) {
return createBidiText(chars.join(""), isLTR);
}
-;// ./src/shared/obj-bin-transform.js
-
-class CssFontInfo {
- #buffer;
- #view;
- #decoder;
- static strings = ["fontFamily", "fontWeight", "italicAngle"];
- static write(info) {
- const encoder = new TextEncoder();
- const encodedStrings = {};
- let stringsLength = 0;
- for (const prop of CssFontInfo.strings) {
- const encoded = encoder.encode(info[prop]);
- encodedStrings[prop] = encoded;
- stringsLength += 4 + encoded.length;
- }
- const buffer = new ArrayBuffer(stringsLength);
- const data = new Uint8Array(buffer);
- const view = new DataView(buffer);
- let offset = 0;
- for (const prop of CssFontInfo.strings) {
- const encoded = encodedStrings[prop];
- const length = encoded.length;
- view.setUint32(offset, length);
- data.set(encoded, offset + 4);
- offset += 4 + length;
- }
- assert(offset === buffer.byteLength, "CssFontInfo.write: Buffer overflow");
- return buffer;
- }
- constructor(buffer) {
- this.#buffer = buffer;
- this.#view = new DataView(this.#buffer);
- this.#decoder = new TextDecoder();
- }
- #readString(index) {
- assert(index < CssFontInfo.strings.length, "Invalid string index");
- let offset = 0;
- for (let i = 0; i < index; i++) {
- offset += this.#view.getUint32(offset) + 4;
- }
- const length = this.#view.getUint32(offset);
- return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
- }
- get fontFamily() {
- return this.#readString(0);
- }
- get fontWeight() {
- return this.#readString(1);
- }
- get italicAngle() {
- return this.#readString(2);
- }
-}
-class SystemFontInfo {
- #buffer;
- #view;
- #decoder;
- static strings = ["css", "loadedName", "baseFontName", "src"];
- static write(info) {
- const encoder = new TextEncoder();
- const encodedStrings = {};
- let stringsLength = 0;
- for (const prop of SystemFontInfo.strings) {
- const encoded = encoder.encode(info[prop]);
- encodedStrings[prop] = encoded;
- stringsLength += 4 + encoded.length;
- }
- stringsLength += 4;
- let encodedStyleStyle,
- encodedStyleWeight,
- lengthEstimate = 1 + stringsLength;
- if (info.style) {
- encodedStyleStyle = encoder.encode(info.style.style);
- encodedStyleWeight = encoder.encode(info.style.weight);
- lengthEstimate += 4 + encodedStyleStyle.length + 4 + encodedStyleWeight.length;
- }
- const buffer = new ArrayBuffer(lengthEstimate);
- const data = new Uint8Array(buffer);
- const view = new DataView(buffer);
- let offset = 0;
- view.setUint8(offset++, info.guessFallback ? 1 : 0);
- view.setUint32(offset, 0);
- offset += 4;
- stringsLength = 0;
- for (const prop of SystemFontInfo.strings) {
- const encoded = encodedStrings[prop];
- const length = encoded.length;
- stringsLength += 4 + length;
- view.setUint32(offset, length);
- data.set(encoded, offset + 4);
- offset += 4 + length;
- }
- view.setUint32(offset - stringsLength - 4, stringsLength);
- if (info.style) {
- view.setUint32(offset, encodedStyleStyle.length);
- data.set(encodedStyleStyle, offset + 4);
- offset += 4 + encodedStyleStyle.length;
- view.setUint32(offset, encodedStyleWeight.length);
- data.set(encodedStyleWeight, offset + 4);
- offset += 4 + encodedStyleWeight.length;
- }
- assert(offset <= buffer.byteLength, "SubstitionInfo.write: Buffer overflow");
- return buffer.transferToFixedLength(offset);
- }
- constructor(buffer) {
- this.#buffer = buffer;
- this.#view = new DataView(this.#buffer);
- this.#decoder = new TextDecoder();
- }
- get guessFallback() {
- return this.#view.getUint8(0) !== 0;
- }
- #readString(index) {
- assert(index < SystemFontInfo.strings.length, "Invalid string index");
- let offset = 5;
- for (let i = 0; i < index; i++) {
- offset += this.#view.getUint32(offset) + 4;
- }
- const length = this.#view.getUint32(offset);
- return this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, length));
- }
- get css() {
- return this.#readString(0);
- }
- get loadedName() {
- return this.#readString(1);
- }
- get baseFontName() {
- return this.#readString(2);
- }
- get src() {
- return this.#readString(3);
- }
- get style() {
- let offset = 1;
- offset += 4 + this.#view.getUint32(offset);
- const styleLength = this.#view.getUint32(offset);
- const style = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, styleLength));
- offset += 4 + styleLength;
- const weightLength = this.#view.getUint32(offset);
- const weight = this.#decoder.decode(new Uint8Array(this.#buffer, offset + 4, weightLength));
- return {
- style,
- weight
- };
- }
-}
-class FontInfo {
- static bools = ["black", "bold", "disableFontFace", "fontExtraProperties", "isInvalidPDFjsFont", "isType3Font", "italic", "missingFile", "remeasure", "vertical"];
- static numbers = ["ascent", "defaultWidth", "descent"];
- static strings = ["fallbackName", "loadedName", "mimetype", "name"];
- static #OFFSET_NUMBERS = Math.ceil(this.bools.length * 2 / 8);
- static #OFFSET_BBOX = this.#OFFSET_NUMBERS + this.numbers.length * 8;
- static #OFFSET_FONT_MATRIX = this.#OFFSET_BBOX + 1 + 2 * 4;
- static #OFFSET_DEFAULT_VMETRICS = this.#OFFSET_FONT_MATRIX + 1 + 8 * 6;
- static #OFFSET_STRINGS = this.#OFFSET_DEFAULT_VMETRICS + 1 + 2 * 3;
- #buffer;
- #decoder;
- #view;
- constructor({
- data,
- extra
- }) {
- this.#buffer = data;
- this.#decoder = new TextDecoder();
- this.#view = new DataView(this.#buffer);
- if (extra) {
- Object.assign(this, extra);
- }
- }
- #readBoolean(index) {
- assert(index < FontInfo.bools.length, "Invalid boolean index");
- const byteOffset = Math.floor(index / 4);
- const bitOffset = index * 2 % 8;
- const value = this.#view.getUint8(byteOffset) >> bitOffset & 0x03;
- return value === 0x00 ? undefined : value === 0x02;
- }
- get black() {
- return this.#readBoolean(0);
- }
- get bold() {
- return this.#readBoolean(1);
- }
- get disableFontFace() {
- return this.#readBoolean(2);
- }
- get fontExtraProperties() {
- return this.#readBoolean(3);
- }
- get isInvalidPDFjsFont() {
- return this.#readBoolean(4);
- }
- get isType3Font() {
- return this.#readBoolean(5);
- }
- get italic() {
- return this.#readBoolean(6);
- }
- get missingFile() {
- return this.#readBoolean(7);
- }
- get remeasure() {
- return this.#readBoolean(8);
- }
- get vertical() {
- return this.#readBoolean(9);
- }
- #readNumber(index) {
- assert(index < FontInfo.numbers.length, "Invalid number index");
- return this.#view.getFloat64(FontInfo.#OFFSET_NUMBERS + index * 8);
- }
- get ascent() {
- return this.#readNumber(0);
- }
- get defaultWidth() {
- return this.#readNumber(1);
- }
- get descent() {
- return this.#readNumber(2);
- }
- get bbox() {
- let offset = FontInfo.#OFFSET_BBOX;
- const numCoords = this.#view.getUint8(offset);
- if (numCoords === 0) {
- return undefined;
- }
- offset += 1;
- const bbox = [];
- for (let i = 0; i < 4; i++) {
- bbox.push(this.#view.getInt16(offset, true));
- offset += 2;
- }
- return bbox;
- }
- get fontMatrix() {
- let offset = FontInfo.#OFFSET_FONT_MATRIX;
- const numPoints = this.#view.getUint8(offset);
- if (numPoints === 0) {
- return undefined;
- }
- offset += 1;
- const fontMatrix = [];
- for (let i = 0; i < 6; i++) {
- fontMatrix.push(this.#view.getFloat64(offset, true));
- offset += 8;
- }
- return fontMatrix;
- }
- get defaultVMetrics() {
- let offset = FontInfo.#OFFSET_DEFAULT_VMETRICS;
- const numMetrics = this.#view.getUint8(offset);
- if (numMetrics === 0) {
- return undefined;
- }
- offset += 1;
- const defaultVMetrics = [];
- for (let i = 0; i < 3; i++) {
- defaultVMetrics.push(this.#view.getInt16(offset, true));
- offset += 2;
- }
- return defaultVMetrics;
- }
- #readString(index) {
- assert(index < FontInfo.strings.length, "Invalid string index");
- let offset = FontInfo.#OFFSET_STRINGS + 4;
- for (let i = 0; i < index; i++) {
- offset += this.#view.getUint32(offset) + 4;
- }
- const length = this.#view.getUint32(offset);
- const stringData = new Uint8Array(length);
- stringData.set(new Uint8Array(this.#buffer, offset + 4, length));
- return this.#decoder.decode(stringData);
- }
- get fallbackName() {
- return this.#readString(0);
- }
- get loadedName() {
- return this.#readString(1);
- }
- get mimetype() {
- return this.#readString(2);
- }
- get name() {
- return this.#readString(3);
- }
- get data() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + systemFontInfoLength;
- const cssFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + cssFontInfoLength;
- const length = this.#view.getUint32(offset);
- if (length === 0) {
- return undefined;
- }
- return new Uint8Array(this.#buffer, offset + 4, length);
- }
- clearData() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + systemFontInfoLength;
- const cssFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + cssFontInfoLength;
- const length = this.#view.getUint32(offset);
- const data = new Uint8Array(this.#buffer, offset + 4, length);
- data.fill(0);
- this.#view.setUint32(offset, 0);
- }
- get cssFontInfo() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- offset += 4 + systemFontInfoLength;
- const cssFontInfoLength = this.#view.getUint32(offset);
- if (cssFontInfoLength === 0) {
- return null;
- }
- const cssFontInfoData = new Uint8Array(cssFontInfoLength);
- cssFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, cssFontInfoLength));
- return new CssFontInfo(cssFontInfoData.buffer);
- }
- get systemFontInfo() {
- let offset = FontInfo.#OFFSET_STRINGS;
- const stringsLength = this.#view.getUint32(offset);
- offset += 4 + stringsLength;
- const systemFontInfoLength = this.#view.getUint32(offset);
- if (systemFontInfoLength === 0) {
- return null;
- }
- const systemFontInfoData = new Uint8Array(systemFontInfoLength);
- systemFontInfoData.set(new Uint8Array(this.#buffer, offset + 4, systemFontInfoLength));
- return new SystemFontInfo(systemFontInfoData.buffer);
- }
- static write(font) {
- const systemFontInfoBuffer = font.systemFontInfo ? SystemFontInfo.write(font.systemFontInfo) : null;
- const cssFontInfoBuffer = font.cssFontInfo ? CssFontInfo.write(font.cssFontInfo) : null;
- const encoder = new TextEncoder();
- const encodedStrings = {};
- let stringsLength = 0;
- for (const prop of FontInfo.strings) {
- encodedStrings[prop] = encoder.encode(font[prop]);
- stringsLength += 4 + encodedStrings[prop].length;
- }
- const lengthEstimate = FontInfo.#OFFSET_STRINGS + 4 + stringsLength + 4 + (systemFontInfoBuffer ? systemFontInfoBuffer.byteLength : 0) + 4 + (cssFontInfoBuffer ? cssFontInfoBuffer.byteLength : 0) + 4 + (font.data ? font.data.length : 0);
- const buffer = new ArrayBuffer(lengthEstimate);
- const data = new Uint8Array(buffer);
- const view = new DataView(buffer);
- let offset = 0;
- const numBools = FontInfo.bools.length;
- let boolByte = 0,
- boolBit = 0;
- for (let i = 0; i < numBools; i++) {
- const value = font[FontInfo.bools[i]];
- const bits = value === undefined ? 0x00 : value ? 0x02 : 0x01;
- boolByte |= bits << boolBit;
- boolBit += 2;
- if (boolBit === 8 || i === numBools - 1) {
- view.setUint8(offset++, boolByte);
- boolByte = 0;
- boolBit = 0;
- }
- }
- assert(offset === FontInfo.#OFFSET_NUMBERS, "FontInfo.write: Boolean properties offset mismatch");
- for (const prop of FontInfo.numbers) {
- view.setFloat64(offset, font[prop]);
- offset += 8;
- }
- assert(offset === FontInfo.#OFFSET_BBOX, "FontInfo.write: Number properties offset mismatch");
- if (font.bbox) {
- view.setUint8(offset++, 4);
- for (const coord of font.bbox) {
- view.setInt16(offset, coord, true);
- offset += 2;
- }
- } else {
- view.setUint8(offset++, 0);
- offset += 2 * 4;
- }
- assert(offset === FontInfo.#OFFSET_FONT_MATRIX, "FontInfo.write: BBox properties offset mismatch");
- if (font.fontMatrix) {
- view.setUint8(offset++, 6);
- for (const point of font.fontMatrix) {
- view.setFloat64(offset, point, true);
- offset += 8;
- }
- } else {
- view.setUint8(offset++, 0);
- offset += 8 * 6;
- }
- assert(offset === FontInfo.#OFFSET_DEFAULT_VMETRICS, "FontInfo.write: FontMatrix properties offset mismatch");
- if (font.defaultVMetrics) {
- view.setUint8(offset++, 1);
- for (const metric of font.defaultVMetrics) {
- view.setInt16(offset, metric, true);
- offset += 2;
- }
- } else {
- view.setUint8(offset++, 0);
- offset += 3 * 2;
- }
- assert(offset === FontInfo.#OFFSET_STRINGS, "FontInfo.write: DefaultVMetrics properties offset mismatch");
- view.setUint32(FontInfo.#OFFSET_STRINGS, 0);
- offset += 4;
- for (const prop of FontInfo.strings) {
- const encoded = encodedStrings[prop];
- const length = encoded.length;
- view.setUint32(offset, length);
- data.set(encoded, offset + 4);
- offset += 4 + length;
- }
- view.setUint32(FontInfo.#OFFSET_STRINGS, offset - FontInfo.#OFFSET_STRINGS - 4);
- if (!systemFontInfoBuffer) {
- view.setUint32(offset, 0);
- offset += 4;
- } else {
- const length = systemFontInfoBuffer.byteLength;
- view.setUint32(offset, length);
- assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at systemFontInfo");
- data.set(new Uint8Array(systemFontInfoBuffer), offset + 4);
- offset += 4 + length;
- }
- if (!cssFontInfoBuffer) {
- view.setUint32(offset, 0);
- offset += 4;
- } else {
- const length = cssFontInfoBuffer.byteLength;
- view.setUint32(offset, length);
- assert(offset + 4 + length <= buffer.byteLength, "FontInfo.write: Buffer overflow at cssFontInfo");
- data.set(new Uint8Array(cssFontInfoBuffer), offset + 4);
- offset += 4 + length;
- }
- if (font.data === undefined) {
- view.setUint32(offset, 0);
- offset += 4;
- } else {
- view.setUint32(offset, font.data.length);
- data.set(font.data, offset + 4);
- offset += 4 + font.data.length;
- }
- assert(offset <= buffer.byteLength, "FontInfo.write: Buffer overflow");
- return buffer.transferToFixedLength(offset);
- }
-}
-
;// ./src/core/font_substitutions.js
@@ -32572,7 +32783,10 @@ class PartialEvaluator {
}
localShadingPatternCache.set(shading, id);
if (this.parsingType3Font) {
- this.handler.send("commonobj", [id, "Pattern", patternIR]);
+ const transfers = [];
+ const patternBuffer = PatternInfo.write(patternIR);
+ transfers.push(patternBuffer);
+ this.handler.send("commonobj", [id, "Pattern", patternBuffer], transfers);
} else {
this.handler.send("obj", [id, this.pageIndex, "Pattern", patternIR]);
}
@@ -34579,7 +34793,7 @@ class PartialEvaluator {
}
hash.update(`${firstChar}-${lastChar}`);
if (toUnicode instanceof BaseStream) {
- const stream = toUnicode.str || toUnicode;
+ const stream = toUnicode.stream || toUnicode;
const uint8array = stream.buffer ? new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) : new Uint8Array(stream.bytes.buffer, stream.start, stream.end - stream.start);
hash.update(uint8array);
} else if (toUnicode instanceof Name) {
@@ -54020,7 +54234,7 @@ const chunkSize = 512;
class DecryptStream extends DecodeStream {
constructor(str, maybeLength, decrypt) {
super(maybeLength);
- this.str = str;
+ this.stream = str;
this.dict = str.dict;
this.decrypt = decrypt;
this.nextChunk = null;
@@ -54031,14 +54245,14 @@ class DecryptStream extends DecodeStream {
if (this.initialized) {
chunk = this.nextChunk;
} else {
- chunk = this.str.getBytes(chunkSize);
+ chunk = this.stream.getBytes(chunkSize);
this.initialized = true;
}
if (!chunk?.length) {
this.eof = true;
return;
}
- this.nextChunk = this.str.getBytes(chunkSize);
+ this.nextChunk = this.stream.getBytes(chunkSize);
const hasMoreData = this.nextChunk?.length > 0;
const decrypt = this.decrypt;
chunk = decrypt(chunk, !hasMoreData);
@@ -58164,7 +58378,7 @@ class WorkerMessageHandler {
docId,
apiVersion
} = docParams;
- const workerVersion = "5.4.329";
+ const workerVersion = "5.4.370";
if (apiVersion !== workerVersion) {
throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
}
diff --git a/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs b/toolkit/components/pdfjs/content/web/viewer-geckoview.mjs
@@ -21,8 +21,8 @@
*/
/**
- * pdfjsVersion = 5.4.329
- * pdfjsBuild = 3eca60735
+ * pdfjsVersion = 5.4.370
+ * pdfjsBuild = f6317ddbb
*/
/******/ // The require scope
/******/ var __webpack_require__ = {};
@@ -3454,11 +3454,18 @@ class CommentPopup {
if (!correctPosition) {
this.#editor.commentPopupPosition = [x, y];
} else {
- const widthRatio = this._popupWidth / this.#editor.parentBoundingClientRect.width;
+ const parentRect = this.#editor.parentBoundingClientRect;
+ const widthRatio = this._popupWidth / parentRect.width;
if (this.#isLTR && x + widthRatio > 1 || !this.#isLTR && x - widthRatio >= 0) {
const buttonWidth = this.#editor.commentButtonWidth;
x -= widthRatio - buttonWidth;
}
+ const margin = 0.01;
+ if (this.#isLTR) {
+ x = Math.max(x, -parentRect.x / parentRect.width + margin);
+ } else {
+ x = Math.min(x, (window.innerWidth - parentRect.x) / parentRect.width - widthRatio - margin);
+ }
}
this.#posX = x;
this.#posY = y;
@@ -8213,7 +8220,7 @@ class PDFViewer {
#textLayerMode = TextLayerMode.ENABLE;
#viewerAlert = null;
constructor(options) {
- const viewerVersion = "5.4.329";
+ const viewerVersion = "5.4.370";
if (version !== viewerVersion) {
throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
}
diff --git a/toolkit/components/pdfjs/content/web/viewer.css b/toolkit/components/pdfjs/content/web/viewer.css
@@ -2717,6 +2717,9 @@
--button-comment-hover-bg:light-dark(#e0e0e6, #52525e);
--button-comment-hover-color:var(--button-comment-color);
+ --link-fg-color:light-dark(#0060df, #0df);
+ --link-hover-fg-color:light-dark(#0250bb, #80ebff);
+
@media screen and (forced-colors: active){
--comment-date-fg-color:CanvasText;
--comment-bg-color:Canvas;
@@ -2733,11 +2736,13 @@
--comment-indicator-selected-fg-color:SelectedItem;
--button-comment-bg:ButtonFace;
--button-comment-color:ButtonText;
- --button-comment-active-bg:ButtonText;
+ --button-comment-active-bg:Highlight;
--button-comment-active-color:HighlightText;
--button-comment-border:1px solid ButtonText;
--button-comment-hover-bg:Highlight;
--button-comment-hover-color:HighlightText;
+ --link-fg-color:LinkText;
+ --link-hover-fg-color:LinkText;
}
}
@@ -2792,8 +2797,8 @@
height:32px;
padding:8px;
border-radius:4px;
- border:none;
- background:none;
+ border:var(--button-comment-border);
+ background-color:var(--button-comment-bg);
cursor:pointer;
&::before{
@@ -2804,15 +2809,24 @@
mask-repeat:no-repeat;
mask-position:center;
mask-image:var(--comment-close-button-icon);
- background-color:var(--comment-fg-color);
+ background-color:var(--button-comment-color);
}
&:hover{
- background-color:var(--comment-hover-bg-color);
+ background-color:var(--button-comment-hover-bg);
+
+ &::before{
+ background-color:var(--button-comment-hover-color);
+ }
}
&:active{
- background-color:var(--comment-active-bg-color);
+ border:var(--button-comment-active-border);
+ background-color:var(--button-comment-active-bg);
+
+ &::before{
+ background-color:var(--button-comment-active-color);
+ }
}
&:focus-visible{
@@ -2941,10 +2955,15 @@
height:auto;
overflow-wrap:break-word;
margin-block-start:15px;
+ color:var(--link-fg-color);
&:focus-visible{
outline:var(--focus-ring-outline);
}
+
+ &:hover{
+ color:var(--link-hover-fg-color);
+ }
}
}
diff --git a/toolkit/components/pdfjs/content/web/viewer.mjs b/toolkit/components/pdfjs/content/web/viewer.mjs
@@ -21,8 +21,8 @@
*/
/**
- * pdfjsVersion = 5.4.329
- * pdfjsBuild = 3eca60735
+ * pdfjsVersion = 5.4.370
+ * pdfjsBuild = f6317ddbb
*/
/******/ // The require scope
/******/ var __webpack_require__ = {};
@@ -4301,11 +4301,18 @@ class CommentPopup {
if (!correctPosition) {
this.#editor.commentPopupPosition = [x, y];
} else {
- const widthRatio = this._popupWidth / this.#editor.parentBoundingClientRect.width;
+ const parentRect = this.#editor.parentBoundingClientRect;
+ const widthRatio = this._popupWidth / parentRect.width;
if (this.#isLTR && x + widthRatio > 1 || !this.#isLTR && x - widthRatio >= 0) {
const buttonWidth = this.#editor.commentButtonWidth;
x -= widthRatio - buttonWidth;
}
+ const margin = 0.01;
+ if (this.#isLTR) {
+ x = Math.max(x, -parentRect.x / parentRect.width + margin);
+ } else {
+ x = Math.min(x, (window.innerWidth - parentRect.x) / parentRect.width - widthRatio - margin);
+ }
}
this.#posX = x;
this.#posY = y;
@@ -11398,7 +11405,7 @@ class PDFViewer {
#textLayerMode = TextLayerMode.ENABLE;
#viewerAlert = null;
constructor(options) {
- const viewerVersion = "5.4.329";
+ const viewerVersion = "5.4.370";
if (version !== viewerVersion) {
throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
}
diff --git a/toolkit/components/pdfjs/moz.yaml b/toolkit/components/pdfjs/moz.yaml
@@ -20,8 +20,8 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
- release: 3eca60735ba27d0c6ec16b60b88dd6f1f5297e1c (2025-10-14T18:25:40Z).
- revision: 3eca60735ba27d0c6ec16b60b88dd6f1f5297e1c
+ release: f6317ddbbb847b9d6e70aa843f651a93e470257b (2025-10-23T07:01:50Z).
+ revision: f6317ddbbb847b9d6e70aa843f651a93e470257b
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/
diff --git a/toolkit/locales/en-US/toolkit/pdfviewer/viewer.ftl b/toolkit/locales/en-US/toolkit/pdfviewer/viewer.ftl
@@ -700,5 +700,5 @@ pdfjs-editor-edit-comment-dialog-cancel-button = Cancel
## Edit a comment button in the editor toolbar
-pdfjs-editor-edit-comment-button =
- .title = Edit comment
+pdfjs-editor-add-comment-button =
+ .title = Add comment