ngx-extended-pdf-viewer
Version:
Embedding PDF files in your Angular application. Highly configurable viewer including the toolbar, sidebar, and all the features you're used to.
41,013 lines • 1.29 MB
JavaScript
/**
* @licstart The following is the entire license notice for the
* JavaScript code in this page
*
* Copyright 2024 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @licend The above is the entire license notice for the
* JavaScript code in this page
*/
/******/ var __webpack_modules__ = ({
/***/ 641:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ NgxConsole: () => (/* binding */ NgxConsole)
/* harmony export */ });
class NgxConsole {
static ngxConsoleFilter = (_level, _message) => true;
static log(message, reason) {
if (NgxConsole.ngxConsoleFilter("log", message)) {
if (reason !== undefined) {
console.log("%s", message, reason);
} else {
console.log(message);
}
}
}
static error(message, reason) {
if (NgxConsole.ngxConsoleFilter("error", message)) {
if (reason !== undefined) {
console.error("%s", message, reason);
} else {
console.error(message);
}
}
}
static warn(message, reason) {
if (NgxConsole.ngxConsoleFilter("warn", message)) {
if (reason !== undefined) {
console.warn("%s", message, reason);
} else {
console.warn(message);
}
}
}
static debug(message, reason) {
if (NgxConsole.ngxConsoleFilter("debug", message)) {
if (reason !== undefined) {
console.warn("%s", message, reason);
} else {
console.warn(message);
}
}
}
get ngxConsoleFilter() {
return NgxConsole.ngxConsoleFilter;
}
set ngxConsoleFilter(filter) {
NgxConsole.ngxConsoleFilter = filter;
}
reset() {
NgxConsole.ngxConsoleFilter = (_level, _message) => true;
}
}
/***/ }),
/***/ 792:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AnnotationStorage: () => (/* binding */ AnnotationStorage),
/* harmony export */ PrintAnnotationStorage: () => (/* binding */ PrintAnnotationStorage),
/* harmony export */ SerializableEmpty: () => (/* binding */ SerializableEmpty)
/* harmony export */ });
/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(292);
/* harmony import */ var _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(310);
/* harmony import */ var _shared_murmurhash3_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(651);
const SerializableEmpty = Object.freeze({
map: null,
hash: "",
transfer: undefined
});
class AnnotationStorage {
#modified = false;
#modifiedIds = null;
#storage = new Map();
constructor() {
this.onSetModified = null;
this.onResetModified = null;
this.onAnnotationEditor = null;
}
getValue(key, defaultValue) {
const value = this.#storage.get(key);
if (value === undefined) {
return defaultValue;
}
return Object.assign(defaultValue, value);
}
getRawValue(key) {
return this.#storage.get(key);
}
remove(key) {
this.#storage.delete(key);
if (this.#storage.size === 0) {
this.resetModified();
}
if (typeof this.onAnnotationEditor === "function") {
for (const value of this.#storage.values()) {
if (value instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor) {
return;
}
}
this.onAnnotationEditor(null);
}
}
setValue(key, value) {
const obj = this.#storage.get(key);
let modified = false;
if (obj !== undefined) {
for (const [entry, val] of Object.entries(value)) {
if (obj[entry] !== val) {
modified = true;
obj[entry] = val;
}
}
} else {
modified = true;
this.#storage.set(key, value);
}
if (modified) {
this.#setModified();
}
if (value instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor && typeof this.onAnnotationEditor === "function") {
this.onAnnotationEditor(value.constructor._type);
}
}
has(key) {
return this.#storage.has(key);
}
getAll() {
return this.#storage.size > 0 ? (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.objectFromMap)(this.#storage) : null;
}
setAll(obj) {
for (const [key, val] of Object.entries(obj)) {
this.setValue(key, val);
}
}
get size() {
return this.#storage.size;
}
#setModified() {
if (!this.#modified) {
this.#modified = true;
if (typeof this.onSetModified === "function") {
this.onSetModified();
}
}
}
resetModified() {
if (this.#modified) {
this.#modified = false;
if (typeof this.onResetModified === "function") {
this.onResetModified();
}
}
}
get print() {
return new PrintAnnotationStorage(this);
}
get serializable() {
if (this.#storage.size === 0) {
return SerializableEmpty;
}
const map = new Map(),
hash = new _shared_murmurhash3_js__WEBPACK_IMPORTED_MODULE_2__.MurmurHash3_64(),
transfer = [];
const context = Object.create(null);
let hasBitmap = false;
for (const [key, val] of this.#storage) {
const serialized = val instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor ? val.serialize(false, context) : val;
if (serialized) {
map.set(key, serialized);
hash.update(`${key}:${JSON.stringify(serialized)}`);
hasBitmap ||= !!serialized.bitmap;
}
}
if (hasBitmap) {
for (const value of map.values()) {
if (value.bitmap) {
transfer.push(value.bitmap);
}
}
}
return map.size > 0 ? {
map,
hash: hash.hexdigest(),
transfer
} : SerializableEmpty;
}
get editorStats() {
let stats = null;
const typeToEditor = new Map();
for (const value of this.#storage.values()) {
if (!(value instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor)) {
continue;
}
const editorStats = value.telemetryFinalData;
if (!editorStats) {
continue;
}
const {
type
} = editorStats;
if (!typeToEditor.has(type)) {
typeToEditor.set(type, Object.getPrototypeOf(value).constructor);
}
stats ||= Object.create(null);
const map = stats[type] ||= new Map();
for (const [key, val] of Object.entries(editorStats)) {
if (key === "type") {
continue;
}
let counters = map.get(key);
if (!counters) {
counters = new Map();
map.set(key, counters);
}
const count = counters.get(val) ?? 0;
counters.set(val, count + 1);
}
}
for (const [type, editor] of typeToEditor) {
stats[type] = editor.computeTelemetryFinalData(stats[type]);
}
return stats;
}
resetModifiedIds() {
this.#modifiedIds = null;
}
get modifiedIds() {
if (this.#modifiedIds) {
return this.#modifiedIds;
}
const ids = [];
for (const value of this.#storage.values()) {
if (!(value instanceof _editor_editor_js__WEBPACK_IMPORTED_MODULE_1__.AnnotationEditor) || !value.annotationElementId || !value.serialize()) {
continue;
}
ids.push(value.annotationElementId);
}
return this.#modifiedIds = {
ids: new Set(ids),
hash: ids.join(",")
};
}
}
class PrintAnnotationStorage extends AnnotationStorage {
#serializable;
constructor(parent) {
super();
const {
map,
hash,
transfer
} = parent.serializable;
const clone = structuredClone(map, transfer ? {
transfer
} : null);
this.#serializable = {
map: clone,
hash,
transfer
};
}
get print() {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.unreachable)("Should not call PrintAnnotationStorage.print");
}
get serializable() {
return this.#serializable;
}
get modifiedIds() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "modifiedIds", {
ids: new Set(),
hash: ""
});
}
}
/***/ }),
/***/ 925:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
PDFDataRangeTransport: () => (/* binding */ PDFDataRangeTransport),
PDFWorker: () => (/* binding */ PDFWorker),
build: () => (/* binding */ build),
getDocument: () => (/* binding */ getDocument),
version: () => (/* binding */ version)
});
// UNUSED EXPORTS: DefaultCMapReaderFactory, DefaultCanvasFactory, DefaultFilterFactory, DefaultStandardFontDataFactory, LoopbackPort, PDFDocumentLoadingTask, PDFDocumentProxy, PDFPageProxy, RenderTask
// EXTERNAL MODULE: ./src/shared/util.js
var util = __webpack_require__(292);
// EXTERNAL MODULE: ./src/display/annotation_storage.js
var annotation_storage = __webpack_require__(792);
;// ./src/display/font_loader.js
class FontLoader {
#systemFonts = new Set();
constructor({
ownerDocument = globalThis.document,
styleElement = null
}) {
this._document = ownerDocument;
this.nativeFontFaces = new Set();
this.styleElement = null;
this.loadingRequests = [];
this.loadTestFontId = 0;
}
addNativeFontFace(nativeFontFace) {
this.nativeFontFaces.add(nativeFontFace);
this._document.fonts.add(nativeFontFace);
}
removeNativeFontFace(nativeFontFace) {
this.nativeFontFaces.delete(nativeFontFace);
this._document.fonts.delete(nativeFontFace);
}
insertRule(rule) {
if (!this.styleElement) {
this.styleElement = this._document.createElement("style");
this._document.documentElement.getElementsByTagName("head")[0].append(this.styleElement);
}
const styleSheet = this.styleElement.sheet;
styleSheet.insertRule(rule, styleSheet.cssRules.length);
}
clear() {
for (const nativeFontFace of this.nativeFontFaces) {
this._document.fonts.delete(nativeFontFace);
}
this.nativeFontFaces.clear();
this.#systemFonts.clear();
if (this.styleElement) {
this.styleElement.remove();
this.styleElement = null;
}
}
async loadSystemFont({
systemFontInfo: info,
_inspectFont
}) {
if (!info || this.#systemFonts.has(info.loadedName)) {
return;
}
(0,util.assert)(!this.disableFontFace, "loadSystemFont shouldn't be called when `disableFontFace` is set.");
if (this.isFontLoadingAPISupported) {
const {
loadedName,
src,
style
} = info;
const fontFace = new FontFace(loadedName, src, style);
this.addNativeFontFace(fontFace);
try {
await fontFace.load();
this.#systemFonts.add(loadedName);
_inspectFont?.(info);
} catch {
(0,util.warn)(`Cannot load system font: ${info.baseFontName}, installing it could help to improve PDF rendering.`);
this.removeNativeFontFace(fontFace);
}
return;
}
(0,util.unreachable)("Not implemented: loadSystemFont without the Font Loading API.");
}
async bind(font) {
if (font.attached || font.missingFile && !font.systemFontInfo) {
return;
}
font.attached = true;
if (font.systemFontInfo) {
await this.loadSystemFont(font);
return;
}
if (this.isFontLoadingAPISupported) {
const nativeFontFace = font.createNativeFontFace();
if (nativeFontFace) {
this.addNativeFontFace(nativeFontFace);
try {
await nativeFontFace.loaded;
} catch (ex) {
(0,util.warn)(`Failed to load font '${nativeFontFace.family}': '${ex}'.`);
font.disableFontFace = true;
throw ex;
}
}
return;
}
const rule = font.createFontFaceRule();
if (rule) {
this.insertRule(rule);
if (this.isSyncFontLoadingSupported) {
return;
}
await new Promise(resolve => {
const request = this._queueLoadingCallback(resolve);
this._prepareFontLoadEvent(font, request);
});
}
}
get isFontLoadingAPISupported() {
const hasFonts = !!this._document?.fonts;
return (0,util.shadow)(this, "isFontLoadingAPISupported", hasFonts);
}
get isSyncFontLoadingSupported() {
let supported = false;
if (util.isNodeJS) {
supported = true;
} else if (typeof navigator !== "undefined" && typeof navigator?.userAgent === "string" && /Mozilla\/5.0.*?rv:\d+.*? Gecko/.test(navigator.userAgent)) {
supported = true;
}
return (0,util.shadow)(this, "isSyncFontLoadingSupported", supported);
}
_queueLoadingCallback(callback) {
function completeRequest() {
(0,util.assert)(!request.done, "completeRequest() cannot be called twice.");
request.done = true;
while (loadingRequests.length > 0 && loadingRequests[0].done) {
const otherRequest = loadingRequests.shift();
setTimeout(otherRequest.callback, 0);
}
}
const {
loadingRequests
} = this;
const request = {
done: false,
complete: completeRequest,
callback
};
loadingRequests.push(request);
return request;
}
get _loadTestFont() {
const testFont = atob("T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQA" + "FQAABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAA" + "ALwAAAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgA" + "AAAGbmFtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1" + "AAsD6AAAAADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD" + "6AAAAAAD6AABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACM" + "AooCvAAAAeAAMQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4D" + "IP84AFoDIQAAAAAAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAA" + "AAEAAQAAAAEAAAAAAAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUA" + "AQAAAAEAAAAAAAYAAQAAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgAB" + "AAMAAQQJAAMAAgABAAMAAQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABY" + "AAAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAA" + "AC7////TAAEAAAAAAAABBgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAA" + "AAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgcA/gXBIwMAYuL+nz5tQXkD5j3CBLnEQAC" + "AQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYAAABAQAADwACAQEEE/t3" + "Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQAAAAAAAABAAAAAMmJbzEAAAAAzgTj" + "FQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAgABAAAAAAAAAAAD6AAAAAAAAA==");
return (0,util.shadow)(this, "_loadTestFont", testFont);
}
_prepareFontLoadEvent(font, request) {
function int32(data, offset) {
return data.charCodeAt(offset) << 24 | data.charCodeAt(offset + 1) << 16 | data.charCodeAt(offset + 2) << 8 | data.charCodeAt(offset + 3) & 0xff;
}
function spliceString(s, offset, remove, insert) {
const chunk1 = s.substring(0, offset);
const chunk2 = s.substring(offset + remove);
return chunk1 + insert + chunk2;
}
let i, ii;
const canvas = this._document.createElement("canvas");
canvas.width = 1;
canvas.height = 1;
const ctx = canvas.getContext("2d");
let called = 0;
function isFontReady(name, callback) {
if (++called > 30) {
(0,util.warn)("Load test font never loaded.");
callback();
return;
}
ctx.font = "30px " + name;
ctx.fillText(".", 0, 20);
const imageData = ctx.getImageData(0, 0, 1, 1);
if (imageData.data[3] > 0) {
callback();
return;
}
setTimeout(isFontReady.bind(null, name, callback));
}
const loadTestFontId = `lt${Date.now()}${this.loadTestFontId++}`;
let data = this._loadTestFont;
const COMMENT_OFFSET = 976;
data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId);
const CFF_CHECKSUM_OFFSET = 16;
const XXXX_VALUE = 0x58585858;
let checksum = int32(data, CFF_CHECKSUM_OFFSET);
for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {
checksum = checksum - XXXX_VALUE + int32(loadTestFontId, i) | 0;
}
if (i < loadTestFontId.length) {
checksum = checksum - XXXX_VALUE + int32(loadTestFontId + "XXX", i) | 0;
}
data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, (0,util.string32)(checksum));
const url = `url(data:font/opentype;base64,${btoa(data)});`;
const rule = `@font-face {font-family:"${loadTestFontId}";src:${url}}`;
this.insertRule(rule);
const div = this._document.createElement("div");
div.style.visibility = "hidden";
div.style.width = div.style.height = "10px";
div.style.position = "absolute";
div.style.top = div.style.left = "0px";
for (const name of [font.loadedName, loadTestFontId]) {
const span = this._document.createElement("span");
span.textContent = "Hi";
span.style.fontFamily = name;
div.append(span);
}
this._document.body.append(div);
isFontReady(loadTestFontId, () => {
div.remove();
request.complete();
});
}
}
class FontFaceObject {
constructor(translatedData, {
disableFontFace = false,
fontExtraProperties = false,
inspectFont = null
}) {
this.compiledGlyphs = Object.create(null);
for (const i in translatedData) {
this[i] = translatedData[i];
}
this.disableFontFace = disableFontFace === true;
this.fontExtraProperties = fontExtraProperties === true;
this._inspectFont = inspectFont;
}
createNativeFontFace() {
if (!this.data || this.disableFontFace) {
return null;
}
let nativeFontFace;
if (!this.cssFontInfo) {
nativeFontFace = new FontFace(this.loadedName, this.data, {});
} else {
const css = {
weight: this.cssFontInfo.fontWeight
};
if (this.cssFontInfo.italicAngle) {
css.style = `oblique ${this.cssFontInfo.italicAngle}deg`;
}
nativeFontFace = new FontFace(this.cssFontInfo.fontFamily, this.data, css);
}
this._inspectFont?.(this);
return nativeFontFace;
}
createFontFaceRule() {
if (!this.data || this.disableFontFace) {
return null;
}
const url = `url(data:${this.mimetype};base64,${(0,util.toBase64Util)(this.data)});`;
let rule;
if (!this.cssFontInfo) {
rule = `@font-face {font-family:"${this.loadedName}";src:${url}}`;
} else {
let css = `font-weight: ${this.cssFontInfo.fontWeight};`;
if (this.cssFontInfo.italicAngle) {
css += `font-style: oblique ${this.cssFontInfo.italicAngle}deg;`;
}
rule = `@font-face {font-family:"${this.cssFontInfo.fontFamily}";${css}src:${url}}`;
}
this._inspectFont?.(this, url);
return rule;
}
getPathGenerator(objs, character) {
if (this.compiledGlyphs[character] !== undefined) {
return this.compiledGlyphs[character];
}
const objId = this.loadedName + "_path_" + character;
let cmds;
try {
cmds = objs.get(objId);
} catch (ex) {
(0,util.warn)(`getPathGenerator - ignoring character: "${ex}".`);
}
const path = new Path2D(cmds || "");
if (!this.fontExtraProperties) {
objs.delete(objId);
}
return this.compiledGlyphs[character] = path;
}
}
// EXTERNAL MODULE: ./src/display/display_utils.js
var display_utils = __webpack_require__(419);
;// ./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 util.AbortException || ex instanceof util.InvalidPDFException || ex instanceof util.MissingPDFException || ex instanceof util.PasswordException || ex instanceof util.UnexpectedResponseException || ex instanceof util.UnknownErrorException) {
return ex;
}
if (!(ex instanceof Error || typeof ex === "object" && ex !== null)) {
(0,util.unreachable)('wrapReason: Expected "reason" to be a (possibly cloned) Error.');
}
switch (ex.name) {
case "AbortException":
return new util.AbortException(ex.message);
case "InvalidPDFException":
return new util.InvalidPDFException(ex.message);
case "MissingPDFException":
return new util.MissingPDFException(ex.message);
case "PasswordException":
return new util.PasswordException(ex.message, ex.code);
case "UnexpectedResponseException":
return new util.UnexpectedResponseException(ex.message, ex.status);
case "UnknownErrorException":
return new util.UnknownErrorException(ex.message, ex.details);
}
return new util.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
});
}
#onMessage({
data
}) {
if (data.targetName !== this.sourceName) {
return;
}
if (data.stream) {
this.#processStreamMessage(data);
return;
}
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;
}
const action = this.actionHandler[data.action];
if (!action) {
throw new Error(`Unknown action from worker: ${data.action}`);
}
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;
}
if (data.streamId) {
this.#createStreamSink(data);
return;
}
action(data.data);
}
on(actionName, handler) {
const ah = this.actionHandler;
if (ah[actionName]) {
throw new Error(`There is already an actionName called "${actionName}"`);
}
ah[actionName] = handler;
}
send(actionName, data, transfers) {
this.comObj.postMessage({
sourceName: this.sourceName,
targetName: this.targetName,
action: actionName,
data
}, transfers);
}
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;
}
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 => {
(0,util.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) {
(0,util.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:
(0,util.assert)(streamController, "enqueue should have stream controller");
if (streamController.isClosed) {
break;
}
streamController.controller.enqueue(data.chunk);
break;
case StreamKind.CLOSE:
(0,util.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:
(0,util.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_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");
}
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;
}
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;
}
_createCanvas(width, height) {
(0,util.unreachable)("Abstract method `_createCanvas` called.");
}
}
class DOMCanvasFactory extends BaseCanvasFactory {
constructor({
ownerDocument = globalThis.document,
enableHWA = false
}) {
super({
enableHWA
});
this._document = ownerDocument;
}
_createCanvas(width, height) {
const canvas = this._document.createElement("canvas");
canvas.width = width;
canvas.height = height;
return canvas;
}
}
;// ./src/display/cmap_reader_factory.js
class BaseCMapReaderFactory {
constructor({
baseUrl = null,
isCompressed = true
}) {
this.baseUrl = baseUrl;
this.isCompressed = isCompressed;
}
async fetch({
name
}) {
if (!this.baseUrl) {
throw new Error("Ensure that the `cMapUrl` and `cMapPacked` API parameters are provided.");
}
if (!name) {
throw new Error("CMap name must be specified.");
}
const url = this.baseUrl + name + (this.isCompressed ? ".bcmap" : "");
return this._fetch(url).then(cMapData => ({
cMapData,
isCompressed: this.isCompressed
})).catch(reason => {
throw new Error(`Unable to load ${this.isCompressed ? "binary " : ""}CMap at: ${url}`);
});
}
async _fetch(url) {
(0,util.unreachable)("Abstract method `_fetch` called.");
}
}
class DOMCMapReaderFactory extends BaseCMapReaderFactory {
async _fetch(url) {
const data = await (0,display_utils.fetchData)(url, this.isCompressed ? "arraybuffer" : "text");
return data instanceof ArrayBuffer ? new Uint8Array(data) : (0,util.stringToBytes)(data);
}
}
;// ./src/display/filter_factory.js
class BaseFilterFactory {
addFilter(maps) {
return "none";
}
addHCMFilter(fgColor, bgColor) {
return "none";
}
addAlphaFilter(map) {
return "none";
}
addLuminosityFilter(map) {
return "none";
}
addHighlightHCMFilter(filterName, fgColor, bgColor, newFgColor, newBgColor) {
return "none";
}
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 #cache() {
return this.#_cache ||= new Map();
}
get #hcmCache() {
return this.#_hcmCache ||= new Map();
}
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(display_utils.SVG_NS, "svg");
svg.setAttribute("width", 0);
svg.setAttribute("height", 0);
this.#_defs = this.#document.createElementNS(display_utils.SVG_NS, "defs");
div.append(svg);
svg.append(this.#_defs);
this.#document.body.append(div);
}
return this.#_defs;
}
#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];
}
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 [bufferR.join(","), bufferG.join(","), bufferB.join(",")];
}
#createUrl(id) {
if (this.#baseUrl === undefined) {
this.#baseUrl = "";
const url = this.#document.URL;
if (url !== this.#document.baseURI) {
if ((0,display_utils.isDataScheme)(url)) {
(0,util.warn)('#createUrl: ignore "data:"-URL for performance reasons.');
} else {
this.#baseUrl = url.split("#", 1)[0];
}
}
}
return `url(${this.#baseUrl}#${id})`;
}
addFilter(maps) {
if (!maps) {
return "none";
}
let value = this.#cache.get(maps);
if (value) {
return value;
}
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 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;
}
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.Util.makeHexColor(...fgRGB);
const bgRGB = this.#getRGB(bgColor);
bgColor = util.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 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;
}
const [tableA] = this.#createTables([map]);
const key = `alpha_${tableA}`;
value = this.#cache.get(key);
if (value) {
this.#cache.set(map, value);
return value;
}
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;
}
let tableA, key;
if (map) {
[tableA] = this.#createTables([map]);
key = `luminosity_${tableA}`;
} else {
key = "luminosity";
}
value = this.#cache.get(key);
if (value) {
this.#cache.set(map, value);
return value;
}
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);
}
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 (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, 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(display_utils.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(display_utils.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(display_utils.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(display_utils.SVG_NS, func);
feFunc.setAttribute("type", "discrete");
feFunc.setAttribute("tableValues", table);
feComponentTransfer.append(feFunc);
}
#addTransferMapConversion(rTable, gTable, bTable, filter) {
const feComponentTransfer = this.#document.createElementNS(display_utils.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(display_utils.SVG_NS, "feComponentTransfer");
filter.append(feComponentTransfer);
this.#appendFeFunc(feComponentTransfer, "feFuncA", aTable);
}
#getRGB(color) {
this.#defs.style.color = color;
return (0,display_utils.getRGB)(getComputedStyle(this.#defs).getPropertyValue("color"));
}
}
;// ./src/display/standard_fontdata_factory.js
class BaseStandardFontDataFactory {
constructor({
baseUrl = null
}) {
this.baseUrl = baseUrl;
}
async fetch({
filename
}) {
if (!this.baseUrl) {
throw new Error("Ensure that the `standardFontDataUrl` API parameter is provided.");
}
if (!filename) {
throw new Error("Font filename must be specified.");
}
const url = `${this.baseUrl}${filename}`;
return this._fetch(url).catch(reason => {
throw new Error(`Unable to load font data at: ${url}`);
});
}
async _fetch(url) {
(0,util.unreachable)("Abstract method `_fetch` called.");
}
}
class DOMStandardFontDataFactory extends BaseStandardFontDataFactory {
async _fetch(url) {
const data = await (0,display_utils.fetchData)(url, "arraybuffer");
return new Uint8Array(data);
}
}
;// ./src/display/node_utils.js
async function fetchData(url) {
const fs = process.getBuiltinModule("fs");
const data = await fs.promises.readFile(url);
return new Uint8Array(data);
}
class NodeFilterFactory extends BaseFilterFactory {}
class NodeCanvasFactory extends BaseCanvasFactory {
_createCanvas(width, height) {
const require = process.getBuiltinModule("module").createRequire(import.meta.url);
const canvas = require("@napi-rs/canvas");
return canvas.createCanvas(width, height);
}
}
class NodeCMapReaderFactory extends BaseCMapReaderFactory {
async _fetch(url) {
return fetchData(url);
}
}
class NodeStandardFontDataFactory extends BaseStandardFontDataFactory {
async _fetch(url) {
return fetchData(url);
}
}
;// ./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);
}
class BaseShadingPattern {
getPattern() {
(0,util.unreachable)("Abstract method `getPattern` called.");
}
}
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;
}
_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;
}
getPattern(ctx, owner, inverse, pathType) {
let pattern;
if (pathType === PathType.STROKE || pathType === PathType.FILL) {
const ownerBBox = owner.current.getClippedPathBoundingBox(pathType, (0,display_utils.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.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;
}
}
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;
}
let k;
if (y < y1) {
k = 0;
} else if (y > y3) {
k = 1;
} else {
k = (y1 - y) / (y1 - y3);
}
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;
}
}
}
function drawFigure(data, figure, context) {
const ps = figure.coords;
const cs = figure.colors;
let i, ii;
switch (figure.type) {
case "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 "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");
}
}
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[7];
this._background = IR[8];
this.matrix = null;
}
_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;
}
}
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
};
}
getPattern(ctx, owner, inverse, pathType) {
applyBoundingBox(ctx, this._bbox);
let scale;
if (pathType === PathType.SHADING) {
scale = util.Util.singularValueDecompose2dScale((0,display_utils.getCurrentTransform)(ctx));
} else {
scale = util.Util.singularValueDecompose2dScale(owner.baseTransform);
if (this.matrix) {
const matrixScale = util.Util.singularValueDecompose2dScale(this.matrix);
scale = [scale[0] * matrixScale[0], scale[1] * matrixScale[1]];
}
}
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");
}
}
class DummyShadingPattern extends BaseShadingPattern {
getPattern() {
return "hotpink";
}
}
function getShadingPattern(IR) {
switch (IR[0]) {
case "RadialAxial":
return new RadialAxialShadingPattern(IR);
case "Mesh":
return new MeshShadingPattern(IR);
case "Dummy":
return new DummyShadingPattern();
}
throw new Error(`Unknown IR type: ${IR[0]}`);
}
const PaintType = {
COLORED: 1,
UNCOLORED: 2
};
class TilingPattern {
static MAX_PATTERN_SIZE = 3000;
constructor(IR, color, ctx, canvasGraphicsFactory, baseTransform) {
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.color = color;
this.ctx = ctx;
this.canvasGraphicsFactory = canvasGraphicsFactory;
this.baseTransform = baseTransform;
}
createPatternCanvas(owner) {
const {
bbox,
operatorList,
paintType,
tilingType,
color,
canvasGraphicsFactory
} = this;
let {
xstep,
ystep
} = this;
xstep = Math.abs(xstep);
ystep = Math.abs(ystep);
(0,util.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 matrixScale = util.Util.singularValueDecompose2dScale(this.matrix);
const curMatrixScale = util.Util.singularValueDecompose2dScale(this.baseTransform);
const combinedScaleX = matrixScale[0] * curMatrixScale[0];
const combinedScaleY = matrixScale[1] * curMatrixScale[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);
graphics.groupLevel = owner.groupLevel;
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
tmpCtx.translate(-dimx.scale * x0, -dimy.scale * y0);
graphics.transform(dimx.scale, 0, 0, dimy.scale, 0, 0);
tmpCtx.save();
this.clipBbox(graphics, x0, y0, x1, y1);
graphics.baseTransform = (0,display_utils.getCurrentTransform)(graphics.ctx);
graphics.executeOperatorList(operatorList);
graphics.endDrawing();
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
};
}
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;
}
return {
scale,
size
};
}
clipBbox(graphics, x0, y0, x1, y1) {
const bboxWidth = x1 - x0;
const bboxHeight = y1 - y0;
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
graphics.current.updateRectMinMax((0,display_utils.getCurrentTransform)(graphics.ctx), [x0, y0, x1, y1]);
graphics.clip();
graphics.endPath();
}
setFillAndStrokeStyleToContext(graphics, paintType, color) {
const context = graphics.ctx,
current = graphics.current;
switch (paintType) {
case PaintType.COLORED:
const ctx = this.ctx;
context.fillStyle = ctx.fillStyle;
context.strokeStyle = ctx.strokeStyle;
current.fillColor = ctx.fillStyle;
current.strokeColor = ctx.strokeStyle;
break;
case PaintType.UNCOLORED:
const cssColor = util.Util.makeHexColor(color[0], color[1], color[2]);
context.fillStyle = cssColor;
context.strokeStyle = cssColor;
current.fillColor = cssColor;
current.strokeColor = cssColor;
break;
default:
throw new util.FormatError(`Unsupported paint type: ${paintType}`);
}
}
getPattern(ctx, owner, inverse, pathType) {
let matrix = inverse;
if (pathType !== PathType.SHADING) {
matrix = util.Util.transform(matrix, owner.baseTransform);
if (this.matrix) {
matrix = util.Util.transform(matrix, this.matrix);
}
}
const temporaryPatternCanvas = this.createPatternCanvas(owner);
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 (widthRemainder === 0) {
continue;
}
const elem = srcPos < srcLength ? src[srcPos++] : 255;
for (let j = 0; j < widthRemainder; j++) {
dest[destPos++] = elem & 1 << 7 - j ? oneMapping : zeroMapping;
}
}
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;
}
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;
}
} 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;
}
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 {
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;
}
} else {
for (let i = 0, ii = src.length; i < ii; i++) {
dest[i] = src[i] * 0x1010100 | 0x000000ff;
}
}
}
;// ./src/display/canvas.js
const MIN_FONT_SIZE = 16;
const MAX_FONT_SIZE = 100;
const EXECUTION_TIME = 15;
const EXECUTION_STEPS = 10;
const MAX_SIZE_TO_COMPILE = 1000;
const FULL_CHUNK_HEIGHT = 16;
function mirrorContextOperations(ctx, destCtx) {
if (ctx._removeMirroring) {
throw new Error("Context is already forwarding operations.");
}
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 ctxSave() {
destCtx.save();
this.__originalSave();
};
ctx.restore = function ctxRestore() {
destCtx.restore();
this.__originalRestore();
};
ctx.translate = function ctxTranslate(x, y) {
destCtx.translate(x, y);
this.__originalTranslate(x, y);
};
ctx.scale = function ctxScale(x, y) {
destCtx.scale(x, y);
this.__originalScale(x, y);
};
ctx.transform = function ctxTransform(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 ctxSetTransform(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 ctxResetTransform() {
destCtx.resetTransform();
this.__originalResetTransform();
};
ctx.rotate = function ctxRotate(angle) {
destCtx.rotate(angle);
this.__originalRotate(angle);
};
ctx.clip = function ctxRotate(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;
}
return canvasEntry;
}
delete(id) {
delete this.cache[id];
}
clear() {
for (const id in this.cache) {
const canvasEntry = this.cache[id];
this.canvasFactory.destroy(canvasEntry);
delete this.cache[id];
}
}
}
function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) {
const [a, b, c, d, tx, ty] = (0,display_utils.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];
}
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];
}
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];
}
function compileType3Glyph(imgData) {
const {
width,
height
} = imgData;
if (width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) {
return null;
}
const POINT_TO_PROCESS_LIMIT = 1000;
const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]);
const width1 = width + 1;
let points = new Uint8Array(width1 * (height + 1));
let i, j, j0;
const lineSize = width + 7 & ~7;
let data = new Uint8Array(lineSize * height),
pos = 0;
for (const elem of imgData.data) {
let mask = 128;
while (mask > 0) {
data[pos++] = elem & mask ? 0 : 255;
mask >>= 1;
}
}
let count = 0;
pos = 0;
if (data[pos] !== 0) {
points[0] = 1;
++count;
}
for (j = 1; j < width; j++) {
if (data[pos] !== data[pos + 1]) {
points[j] = data[pos] ? 2 : 1;
++count;
}
pos++;
}
if (data[pos] !== 0) {
points[j] = 2;
++count;
}
for (i = 1; i < height; i++) {
pos = i * lineSize;
j0 = i * width1;
if (data[pos - lineSize] !== data[pos]) {
points[j0] = data[pos] ? 1 : 8;
++count;
}
let sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0);
for (j = 1; j < width; j++) {
sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + (data[pos - lineSize + 1] ? 8 : 0);
if (POINT_TYPES[sum]) {
points[j0 + j] = POINT_TYPES[sum];
++count;
}
pos++;
}
if (data[pos - lineSize] !== data[pos]) {
points[j0 + j] = data[pos] ? 2 : 4;
++count;
}
if (count > POINT_TO_PROCESS_LIMIT) {
return null;
}
}
pos = lineSize * (height - 1);
j0 = i * width1;
if (data[pos] !== 0) {
points[j0] = 8;
++count;
}
for (j = 1; j < width; j++) {
if (data[pos] !== data[pos + 1]) {
points[j0 + j] = data[pos] ? 4 : 8;
++count;
}
pos++;
}
if (data[pos] !== 0) {
points[j0 + j] = 4;
++count;
}
if (count > POINT_TO_PROCESS_LIMIT) {
return null;
}
const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]);
const path = new Path2D();
for (i = 0; count && i <= height; i++) {
let p = i * width1;
const end = p + width;
while (p < end && !points[p]) {
p++;
}
if (p === end) {
continue;
}
path.moveTo(p % width1, i);
const p0 = p;
let type = points[p];
do {
const step = steps[type];
do {
p += step;
} while (!points[p]);
const pp = points[p];
if (pp !== 5 && pp !== 10) {
type = pp;
points[p] = 0;
} else {
type = pp & 0x33 * type >> 4;
points[p] &= type >> 2 | type << 2;
}
path.lineTo(p % width1, p / width1 | 0);
if (!points[p]) {
--count;
}
} while (p0 !== p);
--i;
}
data = null;
points = null;
const drawOutline = function (c) {
c.save();
c.scale(1 / width, -1 / height);
c.translate(0, -height);
c.fill(path);
c.beginPath();
c.restore();
};
return drawOutline;
}
class CanvasExtraState {
constructor(width, height) {
this.alphaIsShape = false;
this.fontSize = 0;
this.fontSizeScale = 1;
this.textMatrix = util.IDENTITY_MATRIX;
this.textMatrixScale = 1;
this.fontMatrix = util.FONT_IDENTITY_MATRIX;
this.leading = 0;
this.x = 0;
this.y = 0;
this.lineX = 0;
this.lineY = 0;
this.charSpacing = 0;
this.wordSpacing = 0;
this.textHScale = 1;
this.textRenderingMode = util.TextRenderingMode.FILL;
this.textRise = 0;
this.fillColor = "#000000";
this.strokeColor = "#000000";
this.patternFill = false;
this.patternStroke = false;
this.fillAlpha = 1;
this.strokeAlpha = 1;
this.lineWidth = 1;
this.activeSMask = null;
this.transferMaps = "none";
this.startNewPathAndClipBox([0, 0, width, height]);
}
clone() {
const clone = Object.create(this);
clone.clipBox = this.clipBox.slice();
return clone;
}
setCurrentPoint(x, y) {
this.x = x;
this.y = y;
}
updatePathMinMax(transform, x, y) {
[x, y] = util.Util.applyTransform([x, y], transform);
this.minX = Math.min(this.minX, x);
this.minY = Math.min(this.minY, y);
this.maxX = Math.max(this.maxX, x);
this.maxY = Math.max(this.maxY, y);
}
updateRectMinMax(transform, rect) {
const p1 = util.Util.applyTransform(rect, transform);
const p2 = util.Util.applyTransform(rect.slice(2), transform);
const p3 = util.Util.applyTransform([rect[0], rect[3]], transform);
const p4 = util.Util.applyTransform([rect[2], rect[1]], transform);
this.minX = Math.min(this.minX, p1[0], p2[0], p3[0], p4[0]);
this.minY = Math.min(this.minY, p1[1], p2[1], p3[1], p4[1]);
this.maxX = Math.max(this.maxX, p1[0], p2[0], p3[0], p4[0]);
this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]);
}
updateScalingPathMinMax(transform, minMax) {
util.Util.scaleMinMax(transform, minMax);
this.minX = Math.min(this.minX, minMax[0]);
this.minY = Math.min(this.minY, minMax[1]);
this.maxX = Math.max(this.maxX, minMax[2]);
this.maxY = Math.max(this.maxY, minMax[3]);
}
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
const box = util.Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax);
if (minMax) {
return;
}
this.updateRectMinMax(transform, box);
}
getPathBoundingBox(pathType = PathType.FILL, transform = null) {
const box = [this.minX, this.minY, this.maxX, this.maxY];
if (pathType === PathType.STROKE) {
if (!transform) {
(0,util.unreachable)("Stroke bounding box must include transform.");
}
const scale = util.Util.singularValueDecompose2dScale(transform);
const xStrokePad = scale[0] * this.lineWidth / 2;
const yStrokePad = scale[1] * this.lineWidth / 2;
box[0] -= xStrokePad;
box[1] -= yStrokePad;
box[2] += xStrokePad;
box[3] += yStrokePad;
}
return box;
}
updateClipFromPath() {
const intersect = util.Util.intersect(this.clipBox, this.getPathBoundingBox());
this.startNewPathAndClipBox(intersect || [0, 0, 0, 0]);
}
isEmptyClip() {
return this.minX === Infinity;
}
startNewPathAndClipBox(box) {
this.clipBox = box;
this.minX = Infinity;
this.minY = Infinity;
this.maxX = 0;
this.maxY = 0;
}
getClippedPathBoundingBox(pathType = PathType.FILL, transform = null) {
return util.Util.intersect(this.clipBox, this.getPathBoundingBox(pathType, transform));
}
}
function putBinaryImageData(ctx, imgData) {
if (imgData instanceof ImageData) {
ctx.putImageData(imgData, 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,
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);
}
} 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;
}
if (i < totalChunks) {
elemsInThisChunk = width * partialChunkHeight * 4;
dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk));
ctx.putImageData(chunkImgData, 0, j);
}
} 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);
}
} 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];
}
}
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;
}
if (!util.isNodeJS) {
const {
filter
} = ctx;
if (filter !== "none" && filter !== "") {
ctx.filter = "none";
}
}
}
function getImageSmoothingEnabled(transform, interpolate) {
if (interpolate) {
return true;
}
const scale = util.Util.singularValueDecompose2dScale(transform);
scale[0] = Math.fround(scale[0]);
scale[1] = Math.fround(scale[1]);
const actualScale = Math.fround((globalThis.devicePixelRatio || 1) * display_utils.PixelsPerInch.PDF_TO_CSS_UNITS);
return scale[0] <= actualScale && scale[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) {
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.processingType3 = null;
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();
}
getObject(data, fallback = null) {
if (typeof data === "string") {
return data.startsWith("g_") ? this.commonObjs.get(data) : this.objs.get(data);
}
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(...(0,display_utils.getCurrentTransform)(this.compositeCtx));
}
this.ctx.save();
resetCtxToDefault(this.ctx);
if (transform) {
this.ctx.transform(...transform);
this.outputScaleX = transform[0];
this.outputScaleY = transform[0];
}
this.ctx.transform(...viewport.transform);
this.viewportScale = viewport.scale;
this.baseTransform = (0,display_utils.getCurrentTransform)(this.ctx);
}
executeOperatorList(operatorList, executionStartIdx, continueCallback, stepper) {
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;
while (true) {
if (stepper !== undefined && i === stepper.nextBreakPoint) {
stepper.breakIt(i, continueCallback);
return i;
}
fnId = fnArray[i];
if (fnId !== util.OPS.dependency) {
this[fnId].apply(this, argsArray[i]);
} else {
for (const depObjId of argsArray[i]) {
const objsPool = depObjId.startsWith("g_") ? commonObjs : objs;
if (!objsPool.has(depObjId)) {
objsPool.get(depObjId, continueCallback);
return i;
}
}
}
i++;
if (i === argsArrayLen) {
return i;
}
if (chunkOperations && ++steps > EXECUTION_STEPS) {
if (Date.now() > endTime) {
continueCallback();
return i;
}
steps = 0;
}
}
}
#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;
}
}
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;
}
}
}
_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
};
}
_createMaskCanvas(img) {
const ctx = this.ctx;
const {
width,
height
} = img;
const fillColor = this.current.fillColor;
const isPatternFill = this.current.patternFill;
const currentTransform = (0,display_utils.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]);
return {
canvas: cachedImage,
offsetX,
offsetY
};
}
scaled = cachedImage;
}
if (!scaled) {
maskCanvas = this.cachedCanvases.getCanvas("maskCanvas", width, height);
putBinaryImageMask(maskCanvas.context, img);
}
let maskToCanvas = util.Util.transform(currentTransform, [1 / width, 0, 0, -1 / height, 0, 0]);
maskToCanvas = util.Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);
const [minX, minY, maxX, maxY] = util.Util.getAxialAlignedBoundingBox([0, 0, width, height], maskToCanvas);
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, (0,display_utils.getCurrentTransformInverse)(fillCtx));
scaled = scaled.img;
if (cache && isPatternFill) {
cache.set(cacheKey, scaled);
}
}
fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled((0,display_utils.getCurrentTransform)(fillCtx), img.interpolate);
drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height);
fillCtx.globalCompositeOperation = "source-in";
const inverse = util.Util.transform((0,display_utils.getCurrentTransformInverse)(fillCtx), [1, 0, 0, 1, -offsetX, -offsetY]);
fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, PathType.FILL) : fillColor;
fillCtx.fillRect(0, 0, width, height);
if (cache && !isPatternFill) {
this.cachedCanvases.delete("fillCanvas");
cache.set(cacheKey, fillCanvas.canvas);
}
return {
canvas: fillCanvas.canvas,
offsetX: Math.round(offsetX),
offsetY: Math.round(offsetY)
};
}
setLineWidth(width) {
if (width !== this.current.lineWidth) {
this._cachedScaleForStroking[0] = -1;
}
this.current.lineWidth = width;
this.ctx.lineWidth = width;
}
setLineCap(style) {
this.ctx.lineCap = LINE_CAP_STYLES[style];
}
setLineJoin(style) {
this.ctx.lineJoin = LINE_JOIN_STYLES[style];
}
setMiterLimit(limit) {
this.ctx.miterLimit = limit;
}
setDash(dashArray, dashPhase) {
const ctx = this.ctx;
if (ctx.setLineDash !== undefined) {
ctx.setLineDash(dashArray);
ctx.lineDashOffset = dashPhase;
}
}
setRenderingIntent(intent) {}
setFlatness(flatness) {}
setGState(states) {
for (const [key, value] of states) {
switch (key) {
case "LW":
this.setLineWidth(value);
break;
case "LC":
this.setLineCap(value);
break;
case "LJ":
this.setLineJoin(value);
break;
case "ML":
this.setMiterLimit(value);
break;
case "D":
this.setDash(value[0], value[1]);
break;
case "RI":
this.setRenderingIntent(value);
break;
case "FL":
this.setFlatness(value);
break;
case "Font":
this.setFont(value[0], value[1]);
break;
case "CA":
this.current.strokeAlpha = value;
break;
case "ca":
this.current.fillAlpha = value;
this.ctx.globalAlpha = value;
break;
case "BM":
this.ctx.globalCompositeOperation = value;
break;
case "SMask":
this.current.activeSMask = value ? this.tempSMask : null;
this.tempSMask = null;
this.checkSMaskState();
break;
case "TR":
this.ctx.filter = this.current.transferMaps = this.filterFactory.addFilter(value);
break;
}
}
}
get inSMaskMode() {
return !!this.suspendedCtx;
}
checkSMaskState() {
const inSMaskMode = this.inSMaskMode;
if (this.current.activeSMask && !inSMaskMode) {
this.beginSMaskMode();
} else if (!this.current.activeSMask && inSMaskMode) {
this.endSMaskMode();
}
}
beginSMaskMode() {
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;
this.ctx = scratchCanvas.context;
const ctx = this.ctx;
ctx.setTransform(...(0,display_utils.getCurrentTransform)(this.suspendedCtx));
copyCtxState(this.suspendedCtx, ctx);
mirrorContextOperations(ctx, this.suspendedCtx);
this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
}
endSMaskMode() {
if (!this.inSMaskMode) {
throw new Error("endSMaskMode called while not in smask mode");
}
this.ctx._removeMirroring();
copyCtxState(this.ctx, this.suspendedCtx);
this.ctx = this.suspendedCtx;
this.suspendedCtx = null;
}
compose(dirtyBox) {
if (!this.current.activeSMask) {
return;
}
if (!dirtyBox) {
dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];
} 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]);
}
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();
}
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();
}
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) {
const backdropRGB = util.Util.makeHexColor(...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 = backdropRGB;
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 = backdropRGB;
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);
}
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() {
if (this.inSMaskMode) {
copyCtxState(this.ctx, this.suspendedCtx);
this.suspendedCtx.save();
} else {
this.ctx.save();
}
const old = this.current;
this.stateStack.push(old);
this.current = old.clone();
}
restore() {
if (this.stateStack.length === 0 && this.inSMaskMode) {
this.endSMaskMode();
}
if (this.stateStack.length !== 0) {
this.current = this.stateStack.pop();
if (this.inSMaskMode) {
this.suspendedCtx.restore();
copyCtxState(this.suspendedCtx, this.ctx);
} else {
this.ctx.restore();
}
this.checkSMaskState();
this.pendingClip = null;
this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null;
}
}
transform(a, b, c, d, e, f) {
this.ctx.transform(a, b, c, d, e, f);
this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null;
}
constructPath(ops, args, minMax) {
const ctx = this.ctx;
const current = this.current;
let x = current.x,
y = current.y;
let startX, startY;
const currentTransform = (0,display_utils.getCurrentTransform)(ctx);
const isScalingMatrix = currentTransform[0] === 0 && currentTransform[3] === 0 || currentTransform[1] === 0 && currentTransform[2] === 0;
const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;
for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
switch (ops[i] | 0) {
case util.OPS.rectangle:
x = args[j++];
y = args[j++];
const width = args[j++];
const height = args[j++];
const xw = x + width;
const yh = y + height;
ctx.moveTo(x, y);
if (width === 0 || height === 0) {
ctx.lineTo(xw, yh);
} else {
ctx.lineTo(xw, y);
ctx.lineTo(xw, yh);
ctx.lineTo(x, yh);
}
if (!isScalingMatrix) {
current.updateRectMinMax(currentTransform, [x, y, xw, yh]);
}
ctx.closePath();
break;
case util.OPS.moveTo:
x = args[j++];
y = args[j++];
ctx.moveTo(x, y);
if (!isScalingMatrix) {
current.updatePathMinMax(currentTransform, x, y);
}
break;
case util.OPS.lineTo:
x = args[j++];
y = args[j++];
ctx.lineTo(x, y);
if (!isScalingMatrix) {
current.updatePathMinMax(currentTransform, x, y);
}
break;
case util.OPS.curveTo:
startX = x;
startY = y;
x = args[j + 4];
y = args[j + 5];
ctx.bezierCurveTo(args[j], args[j + 1], args[j + 2], args[j + 3], x, y);
current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], args[j + 2], args[j + 3], x, y, minMaxForBezier);
j += 6;
break;
case util.OPS.curveTo2:
startX = x;
startY = y;
ctx.bezierCurveTo(x, y, args[j], args[j + 1], args[j + 2], args[j + 3]);
current.updateCurvePathMinMax(currentTransform, startX, startY, x, y, args[j], args[j + 1], args[j + 2], args[j + 3], minMaxForBezier);
x = args[j + 2];
y = args[j + 3];
j += 4;
break;
case util.OPS.curveTo3:
startX = x;
startY = y;
x = args[j + 2];
y = args[j + 3];
ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
current.updateCurvePathMinMax(currentTransform, startX, startY, args[j], args[j + 1], x, y, x, y, minMaxForBezier);
j += 4;
break;
case util.OPS.closePath:
ctx.closePath();
break;
}
}
if (isScalingMatrix) {
current.updateScalingPathMinMax(currentTransform, minMaxForBezier);
}
current.setCurrentPoint(x, y);
}
closePath() {
this.ctx.closePath();
}
stroke(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) {
ctx.save();
ctx.strokeStyle = strokeColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.STROKE);
this.rescaleAndStroke(false);
ctx.restore();
} else {
this.rescaleAndStroke(true);
}
}
if (consumePath) {
this.consumePath(this.current.getClippedPathBoundingBox());
}
ctx.globalAlpha = this.current.fillAlpha;
}
closeStroke() {
this.closePath();
this.stroke();
}
fill(consumePath = true) {
const ctx = this.ctx;
const fillColor = this.current.fillColor;
const isPatternFill = this.current.patternFill;
let needRestore = false;
if (isPatternFill) {
ctx.save();
ctx.fillStyle = fillColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.FILL);
needRestore = true;
}
const intersect = this.current.getClippedPathBoundingBox();
if (this.contentVisible && intersect !== null) {
if (this.pendingEOFill) {
ctx.fill("evenodd");
this.pendingEOFill = false;
} else {
ctx.fill();
}
}
if (needRestore) {
ctx.restore();
}
if (consumePath) {
this.consumePath(intersect);
}
}
eoFill() {
this.pendingEOFill = true;
this.fill();
}
fillStroke() {
this.fill(false);
this.stroke(false);
this.consumePath();
}
eoFillStroke() {
this.pendingEOFill = true;
this.fillStroke();
}
closeFillStroke() {
this.closePath();
this.fillStroke();
}
closeEOFillStroke() {
this.pendingEOFill = true;
this.closePath();
this.fillStroke();
}
endPath() {
this.consumePath();
}
clip() {
this.pendingClip = NORMAL_CLIP;
}
eoClip() {
this.pendingClip = EO_CLIP;
}
beginText() {
this.current.textMatrix = util.IDENTITY_MATRIX;
this.current.textMatrixScale = 1;
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
}
endText() {
const paths = this.pendingTextPaths;
const ctx = this.ctx;
if (paths === undefined) {
ctx.beginPath();
return;
}
const newPath = new Path2D();
const invTransf = ctx.getTransform().invertSelf();
for (const {
transform,
x,
y,
fontSize,
path
} of paths) {
newPath.addPath(path, new DOMMatrix(transform).preMultiplySelf(invTransf).translate(x, y).scale(fontSize, -fontSize));
}
ctx.clip(newPath);
ctx.beginPath();
delete this.pendingTextPaths;
}
setCharSpacing(spacing) {
this.current.charSpacing = spacing;
}
setWordSpacing(spacing) {
this.current.wordSpacing = spacing;
}
setHScale(scale) {
this.current.textHScale = scale / 100;
}
setLeading(leading) {
this.current.leading = -leading;
}
setFont(fontRefName, size) {
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 || util.FONT_IDENTITY_MATRIX;
if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {
(0,util.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}`;
}
setTextRenderingMode(mode) {
this.current.textRenderingMode = mode;
}
setTextRise(rise) {
this.current.textRise = rise;
}
moveText(x, y) {
this.current.x = this.current.lineX += x;
this.current.y = this.current.lineY += y;
}
setLeadingMoveText(x, y) {
this.setLeading(-y);
this.moveText(x, y);
}
setTextMatrix(a, b, c, d, e, f) {
this.current.textMatrix = [a, b, c, d, e, f];
this.current.textMatrixScale = Math.hypot(a, b);
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
}
nextLine() {
this.moveText(0, this.current.leading);
}
#getScaledPath(path, currentTransform, transform) {
const newPath = new Path2D();
newPath.addPath(path, new DOMMatrix(transform).invertSelf().multiplySelf(currentTransform));
return newPath;
}
paintChar(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 & util.TextRenderingMode.FILL_STROKE_MASK;
const isAddToPathSet = !!(textRenderingMode & util.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) {
path = font.getPathGenerator(this.commonObjs, character);
}
if (font.disableFontFace || patternFill || patternStroke) {
ctx.save();
ctx.translate(x, y);
ctx.scale(fontSize, -fontSize);
if (fillStrokeMode === util.TextRenderingMode.FILL || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {
if (patternFillTransform) {
const currentTransform = ctx.getTransform();
ctx.setTransform(...patternFillTransform);
ctx.fill(this.#getScaledPath(path, currentTransform, patternFillTransform));
} else {
ctx.fill(path);
}
}
if (fillStrokeMode === util.TextRenderingMode.STROKE || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {
if (patternStrokeTransform) {
const currentTransform = ctx.getTransform();
ctx.setTransform(...patternStrokeTransform);
ctx.stroke(this.#getScaledPath(path, currentTransform, patternStrokeTransform));
} else {
ctx.lineWidth /= fontSize;
ctx.stroke(path);
}
}
ctx.restore();
} else {
if (fillStrokeMode === util.TextRenderingMode.FILL || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {
ctx.fillText(character, x, y);
}
if (fillStrokeMode === util.TextRenderingMode.STROKE || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {
ctx.strokeText(character, x, y);
}
}
if (isAddToPathSet) {
const paths = this.pendingTextPaths ||= [];
paths.push({
transform: (0,display_utils.getCurrentTransform)(ctx),
x,
y,
fontSize,
path
});
}
}
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 (0,util.shadow)(this, "isFontSubpixelAAEnabled", enabled);
}
showText(glyphs) {
const current = this.current;
const font = current.font;
if (font.isType3Font) {
return this.showType3Text(glyphs);
}
const fontSize = current.fontSize;
if (fontSize === 0) {
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 === util.TextRenderingMode.FILL && !font.disableFontFace && !current.patternFill;
ctx.save();
ctx.transform(...current.textMatrix);
ctx.translate(current.x, current.y + current.textRise);
if (fontDirection > 0) {
ctx.scale(textHScale, -1);
} else {
ctx.scale(textHScale, 1);
}
let patternFillTransform, patternStrokeTransform;
if (current.patternFill) {
ctx.save();
const pattern = current.fillColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.FILL);
patternFillTransform = (0,display_utils.getCurrentTransform)(ctx);
ctx.restore();
ctx.fillStyle = pattern;
}
if (current.patternStroke) {
ctx.save();
const pattern = current.strokeColor.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.STROKE);
patternStrokeTransform = (0,display_utils.getCurrentTransform)(ctx);
ctx.restore();
ctx.strokeStyle = pattern;
}
let lineWidth = current.lineWidth;
const scale = current.textMatrixScale;
if (scale === 0 || lineWidth === 0) {
const fillStrokeMode = current.textRenderingMode & util.TextRenderingMode.FILL_STROKE_MASK;
if (fillStrokeMode === util.TextRenderingMode.STROKE || fillStrokeMode === util.TextRenderingMode.FILL_STROKE) {
lineWidth = this.getSinglePixelWidth();
}
} else {
lineWidth /= scale;
}
if (fontSizeScale !== 1.0) {
ctx.scale(fontSizeScale, fontSizeScale);
lineWidth /= fontSizeScale;
}
ctx.lineWidth = lineWidth;
if (font.isInvalidPDFjsFont) {
const chars = [];
let width = 0;
for (const glyph of glyphs) {
chars.push(glyph.unicode);
width += glyph.width;
}
ctx.fillText(chars.join(""), 0, 0);
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;
}
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;
}
if (font.remeasure && width > 0) {
const measuredWidth = ctx.measureText(character).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 (this.contentVisible && (glyph.isInFont || font.missingFile)) {
if (simpleFillText && !accent) {
ctx.fillText(character, scaledX, scaledY);
} else {
this.paintChar(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(accent.fontChar, scaledAccentX, scaledAccentY, patternFillTransform, patternStrokeTransform);
}
}
}
const charWidth = vertical ? width * widthAdvanceScale - spacing * fontDirection : width * widthAdvanceScale + spacing * fontDirection;
x += charWidth;
if (restoreNeeded) {
ctx.restore();
}
}
if (vertical) {
current.y -= x;
} else {
current.x += x * textHScale;
}
ctx.restore();
this.compose();
return undefined;
}
showType3Text(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 || util.FONT_IDENTITY_MATRIX;
const glyphsLength = glyphs.length;
const isTextInvisible = current.textRenderingMode === util.TextRenderingMode.INVISIBLE;
let i, glyph, width, spacingLength;
if (isTextInvisible || fontSize === 0) {
return;
}
this._cachedScaleForStroking[0] = -1;
this._cachedGetSinglePixelWidth = null;
ctx.save();
ctx.transform(...current.textMatrix);
ctx.translate(current.x, current.y);
ctx.scale(textHScale, fontDirection);
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) {
(0,util.warn)(`Type3 character "${glyph.operatorListId}" is not available.`);
continue;
}
if (this.contentVisible) {
this.processingType3 = glyph;
this.save();
ctx.scale(fontSize, fontSize);
ctx.transform(...fontMatrix);
this.executeOperatorList(operatorList);
this.restore();
}
const transformed = util.Util.applyTransform([glyph.width, 0], fontMatrix);
width = transformed[0] * fontSize + spacing;
ctx.translate(width, 0);
current.x += width * textHScale;
}
ctx.restore();
this.processingType3 = null;
}
setCharWidth(xWidth, yWidth) {}
setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) {
this.ctx.rect(llx, lly, urx - llx, ury - lly);
this.ctx.clip();
this.endPath();
}
getColorN_Pattern(IR) {
let pattern;
if (IR[0] === "TilingPattern") {
const color = IR[1];
const baseTransform = this.baseTransform || (0,display_utils.getCurrentTransform)(this.ctx);
const canvasGraphicsFactory = {
createCanvasGraphics: ctx => new CanvasGraphics(ctx, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {
optionalContentConfig: this.optionalContentConfig,
markedContentStack: this.markedContentStack
})
};
pattern = new TilingPattern(IR, color, this.ctx, canvasGraphicsFactory, baseTransform);
} else {
pattern = this._getPattern(IR[1], IR[2]);
}
return pattern;
}
setStrokeColorN() {
this.current.strokeColor = this.getColorN_Pattern(arguments);
this.current.patternStroke = true;
}
setFillColorN() {
this.current.fillColor = this.getColorN_Pattern(arguments);
this.current.patternFill = true;
}
setStrokeRGBColor(r, g, b) {
this.ctx.strokeStyle = this.current.strokeColor = util.Util.makeHexColor(r, g, b);
this.current.patternStroke = false;
}
setStrokeTransparent() {
this.ctx.strokeStyle = this.current.strokeColor = "transparent";
this.current.patternStroke = false;
}
setFillRGBColor(r, g, b) {
this.ctx.fillStyle = this.current.fillColor = util.Util.makeHexColor(r, g, b);
this.current.patternFill = false;
}
setFillTransparent() {
this.ctx.fillStyle = this.current.fillColor = "transparent";
this.current.patternFill = false;
}
_getPattern(objId, matrix = null) {
let pattern;
if (this.cachedPatterns.has(objId)) {
pattern = this.cachedPatterns.get(objId);
} else {
pattern = getShadingPattern(this.getObject(objId));
this.cachedPatterns.set(objId, pattern);
}
if (matrix) {
pattern.matrix = matrix;
}
return pattern;
}
shadingFill(objId) {
if (!this.contentVisible) {
return;
}
const ctx = this.ctx;
this.save();
const pattern = this._getPattern(objId);
ctx.fillStyle = pattern.getPattern(ctx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.SHADING);
const inv = (0,display_utils.getCurrentTransformInverse)(ctx);
if (inv) {
const {
width,
height
} = ctx.canvas;
const [x0, y0, x1, y1] = util.Util.getAxialAlignedBoundingBox([0, 0, width, height], inv);
this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
} else {
this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
}
this.compose(this.current.getClippedPathBoundingBox());
this.restore();
}
beginInlineImage() {
(0,util.unreachable)("Should not call beginInlineImage");
}
beginImageData() {
(0,util.unreachable)("Should not call beginImageData");
}
paintFormXObjectBegin(matrix, bbox) {
if (!this.contentVisible) {
return;
}
this.save();
this.baseTransformStack.push(this.baseTransform);
if (matrix) {
this.transform(...matrix);
}
this.baseTransform = (0,display_utils.getCurrentTransform)(this.ctx);
if (bbox) {
const width = bbox[2] - bbox[0];
const height = bbox[3] - bbox[1];
this.ctx.rect(bbox[0], bbox[1], width, height);
this.current.updateRectMinMax((0,display_utils.getCurrentTransform)(this.ctx), bbox);
this.clip();
this.endPath();
}
}
paintFormXObjectEnd() {
if (!this.contentVisible) {
return;
}
this.restore();
this.baseTransform = this.baseTransformStack.pop();
}
beginGroup(group) {
if (!this.contentVisible) {
return;
}
this.save();
if (this.inSMaskMode) {
this.endSMaskMode();
this.current.activeSMask = null;
}
const currentCtx = this.ctx;
if (!group.isolated) {
(0,util.info)("TODO: Support non-isolated groups.");
}
if (group.knockout) {
(0,util.warn)("Knockout groups not supported.");
}
const currentTransform = (0,display_utils.getCurrentTransform)(currentCtx);
if (group.matrix) {
currentCtx.transform(...group.matrix);
}
if (!group.bbox) {
throw new Error("Bounding box is required.");
}
let bounds = util.Util.getAxialAlignedBoundingBox(group.bbox, (0,display_utils.getCurrentTransform)(currentCtx));
const canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];
bounds = util.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);
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
});
} else {
currentCtx.setTransform(1, 0, 0, 1, 0, 0);
currentCtx.translate(offsetX, offsetY);
currentCtx.save();
}
copyCtxState(currentCtx, groupCtx);
this.ctx = groupCtx;
this.setGState([["BM", "source-over"], ["ca", 1], ["CA", 1]]);
this.groupStack.push(currentCtx);
this.groupLevel++;
}
endGroup(group) {
if (!this.contentVisible) {
return;
}
this.groupLevel--;
const groupCtx = this.ctx;
const ctx = this.groupStack.pop();
this.ctx = ctx;
this.ctx.imageSmoothingEnabled = false;
if (group.smask) {
this.tempSMask = this.smaskStack.pop();
this.restore();
} else {
this.ctx.restore();
const currentMtx = (0,display_utils.getCurrentTransform)(this.ctx);
this.restore();
this.ctx.save();
this.ctx.setTransform(...currentMtx);
const dirtyBox = util.Util.getAxialAlignedBoundingBox([0, 0, groupCtx.canvas.width, groupCtx.canvas.height], currentMtx);
this.ctx.drawImage(groupCtx.canvas, 0, 0);
this.ctx.restore();
this.compose(dirtyBox);
}
}
beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) {
this.#restoreInitialState();
resetCtxToDefault(this.ctx);
this.ctx.save();
this.save();
if (this.baseTransform) {
this.ctx.setTransform(...this.baseTransform);
}
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;
const [scaleX, scaleY] = util.Util.singularValueDecompose2dScale((0,display_utils.getCurrentTransform)(this.ctx));
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(scaleX, 0, 0, -scaleY, 0, height * scaleY);
resetCtxToDefault(this.ctx);
} else {
resetCtxToDefault(this.ctx);
this.endPath();
this.ctx.rect(rect[0], rect[1], width, height);
this.ctx.clip();
this.ctx.beginPath();
}
}
this.current = new CanvasExtraState(this.ctx.canvas.width, this.ctx.canvas.height);
this.transform(...transform);
this.transform(...matrix);
}
endAnnotation() {
if (this.annotationCanvas) {
this.ctx.restore();
this.#drawFilter();
this.ctx = this.annotationCanvas.savedCtx;
delete this.annotationCanvas.savedCtx;
delete this.annotationCanvas;
}
}
paintImageMaskXObject(img) {
if (!this.contentVisible) {
return;
}
const count = img.count;
img = this.getObject(img.data, img);
img.count = count;
const ctx = this.ctx;
const glyph = this.processingType3;
if (glyph) {
if (glyph.compiled === undefined) {
glyph.compiled = compileType3Glyph(img);
}
if (glyph.compiled) {
glyph.compiled(ctx);
return;
}
}
const mask = this._createMaskCanvas(img);
const maskCanvas = mask.canvas;
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.drawImage(maskCanvas, mask.offsetX, mask.offsetY);
ctx.restore();
this.compose();
}
paintImageMaskXObjectRepeat(img, scaleX, skewX = 0, skewY = 0, scaleY, positions) {
if (!this.contentVisible) {
return;
}
img = this.getObject(img.data, img);
const ctx = this.ctx;
ctx.save();
const currentTransform = (0,display_utils.getCurrentTransform)(ctx);
ctx.transform(scaleX, skewX, skewY, scaleY, 0, 0);
const mask = this._createMaskCanvas(img);
ctx.setTransform(1, 0, 0, 1, mask.offsetX - currentTransform[4], mask.offsetY - currentTransform[5]);
for (let i = 0, ii = positions.length; i < ii; i += 2) {
const trans = util.Util.transform(currentTransform, [scaleX, skewX, skewY, scaleY, positions[i], positions[i + 1]]);
const [x, y] = util.Util.applyTransform([0, 0], trans);
ctx.drawImage(mask.canvas, x, y);
}
ctx.restore();
this.compose();
}
paintImageMaskXObjectGroup(images) {
if (!this.contentVisible) {
return;
}
const ctx = this.ctx;
const fillColor = this.current.fillColor;
const isPatternFill = this.current.patternFill;
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(data, image);
putBinaryImageMask(maskCtx, img);
maskCtx.globalCompositeOperation = "source-in";
maskCtx.fillStyle = isPatternFill ? fillColor.getPattern(maskCtx, this, (0,display_utils.getCurrentTransformInverse)(ctx), PathType.FILL) : 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);
ctx.restore();
}
this.compose();
}
paintImageXObject(objId) {
if (!this.contentVisible) {
return;
}
const imgData = this.getObject(objId);
if (!imgData) {
(0,util.warn)("Dependent image isn't ready yet");
return;
}
this.paintInlineImageXObject(imgData);
}
paintImageXObjectRepeat(objId, scaleX, scaleY, positions) {
if (!this.contentVisible) {
return;
}
const imgData = this.getObject(objId);
if (!imgData) {
(0,util.warn)("Dependent image isn't ready yet");
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
});
}
this.paintInlineImageXObjectGroup(imgData, map);
}
applyTransferMapsToCanvas(ctx) {
if (this.current.transferMaps !== "none") {
ctx.filter = this.current.transferMaps;
ctx.drawImage(ctx.canvas, 0, 0);
ctx.filter = "none";
}
return ctx.canvas;
}
applyTransferMapsToBitmap(imgData) {
if (this.current.transferMaps === "none") {
return imgData.bitmap;
}
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(imgData) {
if (!this.contentVisible) {
return;
}
const width = imgData.width;
const height = imgData.height;
const ctx = this.ctx;
this.save();
if (!util.isNodeJS) {
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 {
const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", width, height);
const tmpCtx = tmpCanvas.context;
putBinaryImageData(tmpCtx, imgData);
imgToPaint = this.applyTransferMapsToCanvas(tmpCtx);
}
const scaled = this._scaleImage(imgToPaint, (0,display_utils.getCurrentTransformInverse)(ctx));
ctx.imageSmoothingEnabled = getImageSmoothingEnabled((0,display_utils.getCurrentTransform)(ctx), imgData.interpolate);
drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height);
this.compose();
this.restore();
}
paintInlineImageXObjectGroup(imgData, map) {
if (!this.contentVisible) {
return;
}
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);
}
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);
ctx.restore();
}
this.compose();
}
paintSolidColorImageMask() {
if (!this.contentVisible) {
return;
}
this.ctx.fillRect(0, 0, 1, 1);
this.compose();
}
markPoint(tag) {}
markPointProps(tag, properties) {}
beginMarkedContent(tag) {
this.markedContentStack.push({
visible: true
});
}
beginMarkedContentProps(tag, properties) {
if (tag === "OC") {
this.markedContentStack.push({
visible: this.optionalContentConfig.isVisible(properties)
});
} else {
this.markedContentStack.push({
visible: true
});
}
this.contentVisible = this.isContentVisible();
}
endMarkedContent() {
this.markedContentStack.pop();
this.contentVisible = this.isContentVisible();
}
beginCompat() {}
endCompat() {}
consumePath(clipBox) {
const isEmpty = this.current.isEmptyClip();
if (this.pendingClip) {
this.current.updateClipFromPath();
}
if (!this.pendingClip) {
this.compose(clipBox);
}
const ctx = this.ctx;
if (this.pendingClip) {
if (!isEmpty) {
if (this.pendingClip === EO_CLIP) {
ctx.clip("evenodd");
} else {
ctx.clip();
}
}
this.pendingClip = null;
}
this.current.startNewPathAndClipBox(this.current.clipBox);
ctx.beginPath();
}
getSinglePixelWidth() {
if (!this._cachedGetSinglePixelWidth) {
const m = (0,display_utils.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;
}
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;
}
return this._cachedScaleForStroking;
}
rescaleAndStroke(saveRestore) {
const {
ctx
} = this;
const {
lineWidth
} = this.current;
const [scaleX, scaleY] = this.getScaleForStroking();
ctx.lineWidth = lineWidth || 1;
if (scaleX === 1 && scaleY === 1) {
ctx.stroke();
return;
}
const dashes = ctx.getLineDash();
if (saveRestore) {
ctx.save();
}
ctx.scale(scaleX, scaleY);
if (dashes.length > 0) {
const scale = Math.max(scaleX, scaleY);
ctx.setLineDash(dashes.map(x => x / scale));
ctx.lineDashOffset /= scale;
}
ctx.stroke();
if (saveRestore) {
ctx.restore();
}
}
isContentVisible() {
for (let i = this.markedContentStack.length - 1; i >= 0; i--) {
if (!this.markedContentStack[i].visible) {
return false;
}
}
return true;
}
}
for (const op in util.OPS) {
if (CanvasGraphics.prototype[op] !== undefined) {
CanvasGraphics.prototype[util.OPS[op]] = CanvasGraphics.prototype[op];
}
}
// EXTERNAL MODULE: ./src/display/worker_options.js
var worker_options = __webpack_require__(164);
;// ./src/display/metadata.js
class Metadata {
#metadataMap;
#data;
constructor({
parsedData,
rawData
}) {
this.#metadataMap = parsedData;
this.#data = rawData;
}
getRaw() {
return this.#data;
}
get(name) {
return this.#metadataMap.get(name) ?? null;
}
getAll() {
return (0,util.objectFromMap)(this.#metadataMap);
}
has(name) {
return this.#metadataMap.has(name);
}
}
// EXTERNAL MODULE: ./src/shared/murmurhash3.js
var murmurhash3 = __webpack_require__(651);
;// ./src/display/optional_content_config.js
const INTERNAL = Symbol("INTERNAL");
class OptionalContentGroup {
#isDisplay = false;
#isPrint = false;
#userSet = false;
#visible = true;
constructor(renderingIntent, {
name,
intent,
usage,
rbGroups
}) {
this.#isDisplay = !!(renderingIntent & util.RenderingIntentFlag.DISPLAY);
this.#isPrint = !!(renderingIntent & util.RenderingIntentFlag.PRINT);
this.name = name;
this.intent = intent;
this.usage = usage;
this.rbGroups = rbGroups;
}
get visible() {
if (this.#userSet) {
return this.#visible;
}
if (!this.#visible) {
return false;
}
const {
print,
view
} = this.usage;
if (this.#isDisplay) {
return view?.viewState !== "OFF";
} else if (this.#isPrint) {
return print?.printState !== "OFF";
}
return true;
}
_setVisible(internal, visible, userSet = false) {
if (internal !== INTERNAL) {
(0,util.unreachable)("Internal method `_setVisible` called.");
}
this.#userSet = userSet;
this.#visible = visible;
}
}
class OptionalContentConfig {
#cachedGetHash = null;
#groups = new Map();
#initialHash = null;
#order = null;
constructor(data, renderingIntent = util.RenderingIntentFlag.DISPLAY) {
this.renderingIntent = renderingIntent;
this.name = null;
this.creator = null;
if (data === null) {
return;
}
this.name = data.name;
this.creator = data.creator;
this.#order = data.order;
for (const group of data.groups) {
this.#groups.set(group.id, new OptionalContentGroup(renderingIntent, group));
}
if (data.baseState === "OFF") {
for (const group of this.#groups.values()) {
group._setVisible(INTERNAL, false);
}
}
for (const on of data.on) {
this.#groups.get(on)._setVisible(INTERNAL, true);
}
for (const off of data.off) {
this.#groups.get(off)._setVisible(INTERNAL, false);
}
this.#initialHash = this.getHash();
}
#evaluateVisibilityExpression(array) {
const length = array.length;
if (length < 2) {
return true;
}
const operator = array[0];
for (let i = 1; i < length; i++) {
const element = array[i];
let state;
if (Array.isArray(element)) {
state = this.#evaluateVisibilityExpression(element);
} else if (this.#groups.has(element)) {
state = this.#groups.get(element).visible;
} else {
(0,util.warn)(`Optional content group not found: ${element}`);
return true;
}
switch (operator) {
case "And":
if (!state) {
return false;
}
break;
case "Or":
if (state) {
return true;
}
break;
case "Not":
return !state;
default:
return true;
}
}
return operator === "And";
}
isVisible(group) {
if (this.#groups.size === 0) {
return true;
}
if (!group) {
(0,util.info)("Optional content group not defined.");
return true;
}
if (group.type === "OCG") {
if (!this.#groups.has(group.id)) {
(0,util.warn)(`Optional content group not found: ${group.id}`);
return true;
}
return this.#groups.get(group.id).visible;
} else if (group.type === "OCMD") {
if (group.expression) {
return this.#evaluateVisibilityExpression(group.expression);
}
if (!group.policy || group.policy === "AnyOn") {
for (const id of group.ids) {
if (!this.#groups.has(id)) {
(0,util.warn)(`Optional content group not found: ${id}`);
return true;
}
if (this.#groups.get(id).visible) {
return true;
}
}
return false;
} else if (group.policy === "AllOn") {
for (const id of group.ids) {
if (!this.#groups.has(id)) {
(0,util.warn)(`Optional content group not found: ${id}`);
return true;
}
if (!this.#groups.get(id).visible) {
return false;
}
}
return true;
} else if (group.policy === "AnyOff") {
for (const id of group.ids) {
if (!this.#groups.has(id)) {
(0,util.warn)(`Optional content group not found: ${id}`);
return true;
}
if (!this.#groups.get(id).visible) {
return true;
}
}
return false;
} else if (group.policy === "AllOff") {
for (const id of group.ids) {
if (!this.#groups.has(id)) {
(0,util.warn)(`Optional content group not found: ${id}`);
return true;
}
if (this.#groups.get(id).visible) {
return false;
}
}
return true;
}
(0,util.warn)(`Unknown optional content policy ${group.policy}.`);
return true;
}
(0,util.warn)(`Unknown group type ${group.type}.`);
return true;
}
setVisibility(id, visible = true, preserveRB = true) {
const group = this.#groups.get(id);
if (!group) {
(0,util.warn)(`Optional content group not found: ${id}`);
return;
}
if (preserveRB && visible && group.rbGroups.length) {
for (const rbGroup of group.rbGroups) {
for (const otherId of rbGroup) {
if (otherId !== id) {
this.#groups.get(otherId)?._setVisible(INTERNAL, false, true);
}
}
}
}
group._setVisible(INTERNAL, !!visible, true);
this.#cachedGetHash = null;
}
setOCGState({
state,
preserveRB
}) {
let operator;
for (const elem of state) {
switch (elem) {
case "ON":
case "OFF":
case "Toggle":
operator = elem;
continue;
}
const group = this.#groups.get(elem);
if (!group) {
continue;
}
switch (operator) {
case "ON":
this.setVisibility(elem, true, preserveRB);
break;
case "OFF":
this.setVisibility(elem, false, preserveRB);
break;
case "Toggle":
this.setVisibility(elem, !group.visible, preserveRB);
break;
}
}
this.#cachedGetHash = null;
}
get hasInitialVisibility() {
return this.#initialHash === null || this.getHash() === this.#initialHash;
}
getOrder() {
if (!this.#groups.size) {
return null;
}
if (this.#order) {
return this.#order.slice();
}
return [...this.#groups.keys()];
}
getGroups() {
return this.#groups.size > 0 ? (0,util.objectFromMap)(this.#groups) : null;
}
getGroup(id) {
return this.#groups.get(id) || null;
}
getHash() {
if (this.#cachedGetHash !== null) {
return this.#cachedGetHash;
}
const hash = new murmurhash3.MurmurHash3_64();
for (const [id, group] of this.#groups) {
hash.update(`${id}:${group.visible}`);
}
return this.#cachedGetHash = hash.hexdigest();
}
}
;// ./src/display/transport_stream.js
class PDFDataTransportStream {
constructor(pdfDataRangeTransport, {
disableRange = false,
disableStream = false
}) {
(0,util.assert)(pdfDataRangeTransport, 'PDFDataTransportStream - missing required "pdfDataRangeTransport" argument.');
const {
length,
initialData,
progressiveDone,
contentDispositionFilename
} = pdfDataRangeTransport;
this._queuedChunks = [];
this._progressiveDone = progressiveDone;
this._contentDispositionFilename = contentDispositionFilename;
if (initialData?.length > 0) {
const buffer = initialData instanceof Uint8Array && initialData.byteLength === initialData.buffer.byteLength ? initialData.buffer : new Uint8Array(initialData).buffer;
this._queuedChunks.push(buffer);
}
this._pdfDataRangeTransport = pdfDataRangeTransport;
this._isStreamingSupported = !disableStream;
this._isRangeSupported = !disableRange;
this._contentLength = length;
this._fullRequestReader = null;
this._rangeReaders = [];
pdfDataRangeTransport.addRangeListener((begin, chunk) => {
this._onReceiveData({
begin,
chunk
});
});
pdfDataRangeTransport.addProgressListener((loaded, total) => {
this._onProgress({
loaded,
total
});
});
pdfDataRangeTransport.addProgressiveReadListener(chunk => {
this._onReceiveData({
chunk
});
});
pdfDataRangeTransport.addProgressiveDoneListener(() => {
this._onProgressiveDone();
});
pdfDataRangeTransport.transportReady();
}
_onReceiveData({
begin,
chunk
}) {
const buffer = chunk instanceof Uint8Array && chunk.byteLength === chunk.buffer.byteLength ? chunk.buffer : new Uint8Array(chunk).buffer;
if (begin === undefined) {
if (this._fullRequestReader) {
this._fullRequestReader._enqueue(buffer);
} else {
this._queuedChunks.push(buffer);
}
} else {
const found = this._rangeReaders.some(function (rangeReader) {
if (rangeReader._begin !== begin) {
return false;
}
rangeReader._enqueue(buffer);
return true;
});
(0,util.assert)(found, "_onReceiveData - no `PDFDataTransportStreamRangeReader` instance found.");
}
}
get _progressiveDataLength() {
return this._fullRequestReader?._loaded ?? 0;
}
_onProgress(evt) {
if (evt.total === undefined) {
this._rangeReaders[0]?.onProgress?.({
loaded: evt.loaded
});
} else {
this._fullRequestReader?.onProgress?.({
loaded: evt.loaded,
total: evt.total
});
}
}
_onProgressiveDone() {
this._fullRequestReader?.progressiveDone();
this._progressiveDone = true;
}
_removeRangeReader(reader) {
const i = this._rangeReaders.indexOf(reader);
if (i >= 0) {
this._rangeReaders.splice(i, 1);
}
}
getFullReader() {
(0,util.assert)(!this._fullRequestReader, "PDFDataTransportStream.getFullReader can only be called once.");
const queuedChunks = this._queuedChunks;
this._queuedChunks = null;
return new PDFDataTransportStreamReader(this, queuedChunks, this._progressiveDone, this._contentDispositionFilename);
}
getRangeReader(begin, end) {
if (end <= this._progressiveDataLength) {
return null;
}
const reader = new PDFDataTransportStreamRangeReader(this, begin, end);
this._pdfDataRangeTransport.requestDataRange(begin, end);
this._rangeReaders.push(reader);
return reader;
}
cancelAllRequests(reason) {
this._fullRequestReader?.cancel(reason);
for (const reader of this._rangeReaders.slice(0)) {
reader.cancel(reason);
}
this._pdfDataRangeTransport.abort();
}
}
class PDFDataTransportStreamReader {
constructor(stream, queuedChunks, progressiveDone = false, contentDispositionFilename = null) {
this._stream = stream;
this._done = progressiveDone || false;
this._filename = (0,display_utils.isPdfFile)(contentDispositionFilename) ? contentDispositionFilename : null;
this._queuedChunks = queuedChunks || [];
this._loaded = 0;
for (const chunk of this._queuedChunks) {
this._loaded += chunk.byteLength;
}
this._requests = [];
this._headersReady = Promise.resolve();
stream._fullRequestReader = this;
this.onProgress = null;
}
_enqueue(chunk) {
if (this._done) {
return;
}
if (this._requests.length > 0) {
const requestCapability = this._requests.shift();
requestCapability.resolve({
value: chunk,
done: false
});
} else {
this._queuedChunks.push(chunk);
}
this._loaded += chunk.byteLength;
}
get headersReady() {
return this._headersReady;
}
get filename() {
return this._filename;
}
get isRangeSupported() {
return this._stream._isRangeSupported;
}
get isStreamingSupported() {
return this._stream._isStreamingSupported;
}
get contentLength() {
return this._stream._contentLength;
}
async read() {
if (this._queuedChunks.length > 0) {
const chunk = this._queuedChunks.shift();
return {
value: chunk,
done: false
};
}
if (this._done) {
return {
value: undefined,
done: true
};
}
const requestCapability = Promise.withResolvers();
this._requests.push(requestCapability);
return requestCapability.promise;
}
cancel(reason) {
this._done = true;
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
}
progressiveDone() {
if (this._done) {
return;
}
this._done = true;
}
}
class PDFDataTransportStreamRangeReader {
constructor(stream, begin, end) {
this._stream = stream;
this._begin = begin;
this._end = end;
this._queuedChunk = null;
this._requests = [];
this._done = false;
this.onProgress = null;
}
_enqueue(chunk) {
if (this._done) {
return;
}
if (this._requests.length === 0) {
this._queuedChunk = chunk;
} else {
const requestsCapability = this._requests.shift();
requestsCapability.resolve({
value: chunk,
done: false
});
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
}
this._done = true;
this._stream._removeRangeReader(this);
}
get isStreamingSupported() {
return false;
}
async read() {
if (this._queuedChunk) {
const chunk = this._queuedChunk;
this._queuedChunk = null;
return {
value: chunk,
done: false
};
}
if (this._done) {
return {
value: undefined,
done: true
};
}
const requestCapability = Promise.withResolvers();
this._requests.push(requestCapability);
return requestCapability.promise;
}
cancel(reason) {
this._done = true;
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
this._stream._removeRangeReader(this);
}
}
;// ./src/display/content_disposition.js
function getFilenameFromContentDispositionHeader(contentDisposition) {
let needsEncodingFixup = true;
let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition);
if (tmp) {
tmp = tmp[1];
let filename = rfc2616unquote(tmp);
filename = unescape(filename);
filename = rfc5987decode(filename);
filename = rfc2047decode(filename);
return fixupEncoding(filename);
}
tmp = rfc2231getparam(contentDisposition);
if (tmp) {
const filename = rfc2047decode(tmp);
return fixupEncoding(filename);
}
tmp = toParamRegExp("filename", "i").exec(contentDisposition);
if (tmp) {
tmp = tmp[1];
let filename = rfc2616unquote(tmp);
filename = rfc2047decode(filename);
return fixupEncoding(filename);
}
function toParamRegExp(attributePattern, flags) {
return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags);
}
function textdecode(encoding, value) {
if (encoding) {
if (!/^[\x00-\xFF]+$/.test(value)) {
return value;
}
try {
const decoder = new TextDecoder(encoding, {
fatal: true
});
const buffer = (0,util.stringToBytes)(value);
value = decoder.decode(buffer);
needsEncodingFixup = false;
} catch {}
}
return value;
}
function fixupEncoding(value) {
if (needsEncodingFixup && /[\x80-\xff]/.test(value)) {
value = textdecode("utf-8", value);
if (needsEncodingFixup) {
value = textdecode("iso-8859-1", value);
}
}
return value;
}
function rfc2231getparam(contentDispositionStr) {
const matches = [];
let match;
const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig");
while ((match = iter.exec(contentDispositionStr)) !== null) {
let [, n, quot, part] = match;
n = parseInt(n, 10);
if (n in matches) {
if (n === 0) {
break;
}
continue;
}
matches[n] = [quot, part];
}
const parts = [];
for (let n = 0; n < matches.length; ++n) {
if (!(n in matches)) {
break;
}
let [quot, part] = matches[n];
part = rfc2616unquote(part);
if (quot) {
part = unescape(part);
if (n === 0) {
part = rfc5987decode(part);
}
}
parts.push(part);
}
return parts.join("");
}
function rfc2616unquote(value) {
if (value.startsWith('"')) {
const parts = value.slice(1).split('\\"');
for (let i = 0; i < parts.length; ++i) {
const quotindex = parts[i].indexOf('"');
if (quotindex !== -1) {
parts[i] = parts[i].slice(0, quotindex);
parts.length = i + 1;
}
parts[i] = parts[i].replaceAll(/\\(.)/g, "$1");
}
value = parts.join('"');
}
return value;
}
function rfc5987decode(extvalue) {
const encodingend = extvalue.indexOf("'");
if (encodingend === -1) {
return extvalue;
}
const encoding = extvalue.slice(0, encodingend);
const langvalue = extvalue.slice(encodingend + 1);
const value = langvalue.replace(/^[^']*'/, "");
return textdecode(encoding, value);
}
function rfc2047decode(value) {
if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) {
return value;
}
return value.replaceAll(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) {
if (encoding === "q" || encoding === "Q") {
text = text.replaceAll("_", " ");
text = text.replaceAll(/=([0-9a-fA-F]{2})/g, function (match, hex) {
return String.fromCharCode(parseInt(hex, 16));
});
return textdecode(charset, text);
}
try {
text = atob(text);
} catch {}
return textdecode(charset, text);
});
}
return "";
}
;// ./src/display/network_utils.js
function createHeaders(isHttp, httpHeaders) {
const headers = new Headers();
if (!isHttp || !httpHeaders || typeof httpHeaders !== "object") {
return headers;
}
for (const key in httpHeaders) {
const val = httpHeaders[key];
if (val !== undefined) {
headers.append(key, val);
}
}
return headers;
}
function getResponseOrigin(url) {
try {
return new URL(url).origin;
} catch {}
return null;
}
function validateRangeRequestCapabilities({
responseHeaders,
isHttp,
rangeChunkSize,
disableRange
}) {
const returnValues = {
allowRangeRequests: false,
suggestedLength: undefined
};
const length = parseInt(responseHeaders.get("Content-Length"), 10);
if (!Number.isInteger(length)) {
return returnValues;
}
returnValues.suggestedLength = length;
if (length <= 2 * rangeChunkSize) {
return returnValues;
}
if (disableRange || !isHttp) {
return returnValues;
}
if (responseHeaders.get("Accept-Ranges") !== "bytes") {
return returnValues;
}
const contentEncoding = responseHeaders.get("Content-Encoding") || "identity";
if (contentEncoding !== "identity") {
return returnValues;
}
returnValues.allowRangeRequests = true;
return returnValues;
}
function extractFilenameFromHeader(responseHeaders) {
const contentDisposition = responseHeaders.get("Content-Disposition");
if (contentDisposition) {
let filename = getFilenameFromContentDispositionHeader(contentDisposition);
if (filename.includes("%")) {
try {
filename = decodeURIComponent(filename);
} catch {}
}
if ((0,display_utils.isPdfFile)(filename)) {
return filename;
}
}
return null;
}
function createResponseStatusError(status, url) {
if (status === 404 || status === 0 && url.startsWith("file:")) {
return new util.MissingPDFException('Missing PDF "' + url + '".');
}
return new util.UnexpectedResponseException(`Unexpected server response (${status}) while retrieving PDF "${url}".`, status);
}
function validateResponseStatus(status) {
return status === 200 || status === 206;
}
;// ./src/display/fetch_stream.js
function createFetchOptions(headers, withCredentials, abortController) {
return {
method: "GET",
headers,
signal: abortController.signal,
mode: "cors",
credentials: withCredentials ? "include" : "same-origin",
redirect: "follow"
};
}
function getArrayBuffer(val) {
if (val instanceof Uint8Array) {
return val.buffer;
}
if (val instanceof ArrayBuffer) {
return val;
}
(0,util.warn)(`getArrayBuffer - unexpected data format: ${val}`);
return new Uint8Array(val).buffer;
}
class PDFFetchStream {
_responseOrigin = null;
constructor(source) {
this.source = source;
this.isHttp = /^https?:/i.test(source.url);
this.headers = createHeaders(this.isHttp, source.httpHeaders);
this._fullRequestReader = null;
this._rangeRequestReaders = [];
}
get _progressiveDataLength() {
return this._fullRequestReader?._loaded ?? 0;
}
getFullReader() {
(0,util.assert)(!this._fullRequestReader, "PDFFetchStream.getFullReader can only be called once.");
this._fullRequestReader = new PDFFetchStreamReader(this);
return this._fullRequestReader;
}
getRangeReader(begin, end) {
if (end <= this._progressiveDataLength) {
return null;
}
const reader = new PDFFetchStreamRangeReader(this, begin, end);
this._rangeRequestReaders.push(reader);
return reader;
}
cancelAllRequests(reason) {
this._fullRequestReader?.cancel(reason);
for (const reader of this._rangeRequestReaders.slice(0)) {
reader.cancel(reason);
}
}
}
class PDFFetchStreamReader {
constructor(stream) {
this._stream = stream;
this._reader = null;
this._loaded = 0;
this._filename = null;
const source = stream.source;
this._withCredentials = source.withCredentials || false;
this._contentLength = source.length;
this._headersCapability = Promise.withResolvers();
this._disableRange = source.disableRange || false;
this._rangeChunkSize = source.rangeChunkSize;
if (!this._rangeChunkSize && !this._disableRange) {
this._disableRange = true;
}
this._abortController = new AbortController();
this._isStreamingSupported = !source.disableStream;
this._isRangeSupported = !source.disableRange;
const headers = new Headers(stream.headers);
const url = source.url;
fetch(url, createFetchOptions(headers, this._withCredentials, this._abortController)).then(response => {
stream._responseOrigin = getResponseOrigin(response.url);
if (!validateResponseStatus(response.status)) {
throw createResponseStatusError(response.status, url);
}
this._reader = response.body.getReader();
this._headersCapability.resolve();
const responseHeaders = response.headers;
const {
allowRangeRequests,
suggestedLength
} = validateRangeRequestCapabilities({
responseHeaders,
isHttp: stream.isHttp,
rangeChunkSize: this._rangeChunkSize,
disableRange: this._disableRange
});
this._isRangeSupported = allowRangeRequests;
this._contentLength = suggestedLength || this._contentLength;
this._filename = extractFilenameFromHeader(responseHeaders);
if (!this._isStreamingSupported && this._isRangeSupported) {
this.cancel(new util.AbortException("Streaming is disabled."));
}
}).catch(this._headersCapability.reject);
this.onProgress = null;
}
get headersReady() {
return this._headersCapability.promise;
}
get filename() {
return this._filename;
}
get contentLength() {
return this._contentLength;
}
get isRangeSupported() {
return this._isRangeSupported;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
async read() {
await this._headersCapability.promise;
const {
value,
done
} = await this._reader.read();
if (done) {
return {
value,
done
};
}
this._loaded += value.byteLength;
this.onProgress?.({
loaded: this._loaded,
total: this._contentLength
});
return {
value: getArrayBuffer(value),
done: false
};
}
cancel(reason) {
this._reader?.cancel(reason);
this._abortController.abort();
}
}
class PDFFetchStreamRangeReader {
constructor(stream, begin, end) {
this._stream = stream;
this._reader = null;
this._loaded = 0;
const source = stream.source;
this._withCredentials = source.withCredentials || false;
this._readCapability = Promise.withResolvers();
this._isStreamingSupported = !source.disableStream;
this._abortController = new AbortController();
const headers = new Headers(stream.headers);
headers.append("Range", `bytes=${begin}-${end - 1}`);
const url = source.url;
fetch(url, createFetchOptions(headers, this._withCredentials, this._abortController)).then(response => {
const responseOrigin = getResponseOrigin(response.url);
if (responseOrigin !== stream._responseOrigin) {
throw new Error(`Expected range response-origin "${responseOrigin}" to match "${stream._responseOrigin}".`);
}
if (!validateResponseStatus(response.status)) {
throw createResponseStatusError(response.status, url);
}
this._readCapability.resolve();
this._reader = response.body.getReader();
}).catch(this._readCapability.reject);
this.onProgress = null;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
async read() {
await this._readCapability.promise;
const {
value,
done
} = await this._reader.read();
if (done) {
return {
value,
done
};
}
this._loaded += value.byteLength;
this.onProgress?.({
loaded: this._loaded
});
return {
value: getArrayBuffer(value),
done: false
};
}
cancel(reason) {
this._reader?.cancel(reason);
this._abortController.abort();
}
}
;// ./src/display/network.js
const OK_RESPONSE = 200;
const PARTIAL_CONTENT_RESPONSE = 206;
function network_getArrayBuffer(xhr) {
const data = xhr.response;
if (typeof data !== "string") {
return data;
}
return (0,util.stringToBytes)(data).buffer;
}
class NetworkManager {
_responseOrigin = null;
constructor({
url,
httpHeaders,
withCredentials
}) {
this.url = url;
this.isHttp = /^https?:/i.test(url);
this.headers = createHeaders(this.isHttp, httpHeaders);
this.withCredentials = withCredentials || false;
this.currXhrId = 0;
this.pendingRequests = Object.create(null);
}
request(args) {
const xhr = new XMLHttpRequest();
const xhrId = this.currXhrId++;
const pendingRequest = this.pendingRequests[xhrId] = {
xhr
};
xhr.open("GET", this.url);
xhr.withCredentials = this.withCredentials;
for (const [key, val] of this.headers) {
xhr.setRequestHeader(key, val);
}
if (this.isHttp && "begin" in args && "end" in args) {
xhr.setRequestHeader("Range", `bytes=${args.begin}-${args.end - 1}`);
pendingRequest.expectedStatus = PARTIAL_CONTENT_RESPONSE;
} else {
pendingRequest.expectedStatus = OK_RESPONSE;
}
xhr.responseType = "arraybuffer";
(0,util.assert)(args.onError, "Expected `onError` callback to be provided.");
xhr.onerror = () => {
args.onError(xhr.status);
};
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
xhr.onprogress = this.onProgress.bind(this, xhrId);
pendingRequest.onHeadersReceived = args.onHeadersReceived;
pendingRequest.onDone = args.onDone;
pendingRequest.onError = args.onError;
pendingRequest.onProgress = args.onProgress;
xhr.send(null);
return xhrId;
}
onProgress(xhrId, evt) {
const pendingRequest = this.pendingRequests[xhrId];
if (!pendingRequest) {
return;
}
pendingRequest.onProgress?.(evt);
}
onStateChange(xhrId, evt) {
const pendingRequest = this.pendingRequests[xhrId];
if (!pendingRequest) {
return;
}
const xhr = pendingRequest.xhr;
if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
pendingRequest.onHeadersReceived();
delete pendingRequest.onHeadersReceived;
}
if (xhr.readyState !== 4) {
return;
}
if (!(xhrId in this.pendingRequests)) {
return;
}
delete this.pendingRequests[xhrId];
if (xhr.status === 0 && this.isHttp) {
pendingRequest.onError(xhr.status);
return;
}
const xhrStatus = xhr.status || OK_RESPONSE;
const ok_response_on_range_request = xhrStatus === OK_RESPONSE && pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
if (!ok_response_on_range_request && xhrStatus !== pendingRequest.expectedStatus) {
pendingRequest.onError(xhr.status);
return;
}
const chunk = network_getArrayBuffer(xhr);
if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
const rangeHeader = xhr.getResponseHeader("Content-Range");
const matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
if (matches) {
pendingRequest.onDone({
begin: parseInt(matches[1], 10),
chunk
});
} else {
(0,util.warn)(`Missing or invalid "Content-Range" header.`);
pendingRequest.onError(0);
}
} else if (chunk) {
pendingRequest.onDone({
begin: 0,
chunk
});
} else {
pendingRequest.onError(xhr.status);
}
}
getRequestXhr(xhrId) {
return this.pendingRequests[xhrId].xhr;
}
isPendingRequest(xhrId) {
return xhrId in this.pendingRequests;
}
abortRequest(xhrId) {
const xhr = this.pendingRequests[xhrId].xhr;
delete this.pendingRequests[xhrId];
xhr.abort();
}
}
class PDFNetworkStream {
constructor(source) {
this._source = source;
this._manager = new NetworkManager(source);
this._rangeChunkSize = source.rangeChunkSize;
this._fullRequestReader = null;
this._rangeRequestReaders = [];
}
_onRangeRequestReaderClosed(reader) {
const i = this._rangeRequestReaders.indexOf(reader);
if (i >= 0) {
this._rangeRequestReaders.splice(i, 1);
}
}
getFullReader() {
(0,util.assert)(!this._fullRequestReader, "PDFNetworkStream.getFullReader can only be called once.");
this._fullRequestReader = new PDFNetworkStreamFullRequestReader(this._manager, this._source);
return this._fullRequestReader;
}
getRangeReader(begin, end) {
const reader = new PDFNetworkStreamRangeRequestReader(this._manager, begin, end);
reader.onClosed = this._onRangeRequestReaderClosed.bind(this);
this._rangeRequestReaders.push(reader);
return reader;
}
cancelAllRequests(reason) {
this._fullRequestReader?.cancel(reason);
for (const reader of this._rangeRequestReaders.slice(0)) {
reader.cancel(reason);
}
}
}
class PDFNetworkStreamFullRequestReader {
constructor(manager, source) {
this._manager = manager;
this._url = source.url;
this._fullRequestId = manager.request({
onHeadersReceived: this._onHeadersReceived.bind(this),
onDone: this._onDone.bind(this),
onError: this._onError.bind(this),
onProgress: this._onProgress.bind(this)
});
this._headersCapability = Promise.withResolvers();
this._disableRange = source.disableRange || false;
this._contentLength = source.length;
this._rangeChunkSize = source.rangeChunkSize;
if (!this._rangeChunkSize && !this._disableRange) {
this._disableRange = true;
}
this._isStreamingSupported = false;
this._isRangeSupported = false;
this._cachedChunks = [];
this._requests = [];
this._done = false;
this._storedError = undefined;
this._filename = null;
this.onProgress = null;
}
_onHeadersReceived() {
const fullRequestXhrId = this._fullRequestId;
const fullRequestXhr = this._manager.getRequestXhr(fullRequestXhrId);
this._manager._responseOrigin = getResponseOrigin(fullRequestXhr.responseURL);
const rawResponseHeaders = fullRequestXhr.getAllResponseHeaders();
const responseHeaders = new Headers(rawResponseHeaders ? rawResponseHeaders.trimStart().replace(/[^\S ]+$/, "").split(/[\r\n]+/).map(x => {
const [key, ...val] = x.split(": ");
return [key, val.join(": ")];
}) : []);
const {
allowRangeRequests,
suggestedLength
} = validateRangeRequestCapabilities({
responseHeaders,
isHttp: this._manager.isHttp,
rangeChunkSize: this._rangeChunkSize,
disableRange: this._disableRange
});
if (allowRangeRequests) {
this._isRangeSupported = true;
}
this._contentLength = suggestedLength || this._contentLength;
this._filename = extractFilenameFromHeader(responseHeaders);
if (this._isRangeSupported) {
this._manager.abortRequest(fullRequestXhrId);
}
this._headersCapability.resolve();
}
_onDone(data) {
if (data) {
if (this._requests.length > 0) {
const requestCapability = this._requests.shift();
requestCapability.resolve({
value: data.chunk,
done: false
});
} else {
this._cachedChunks.push(data.chunk);
}
}
this._done = true;
if (this._cachedChunks.length > 0) {
return;
}
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
}
_onError(status) {
this._storedError = createResponseStatusError(status, this._url);
this._headersCapability.reject(this._storedError);
for (const requestCapability of this._requests) {
requestCapability.reject(this._storedError);
}
this._requests.length = 0;
this._cachedChunks.length = 0;
}
_onProgress(evt) {
this.onProgress?.({
loaded: evt.loaded,
total: evt.lengthComputable ? evt.total : this._contentLength
});
}
get filename() {
return this._filename;
}
get isRangeSupported() {
return this._isRangeSupported;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
get contentLength() {
return this._contentLength;
}
get headersReady() {
return this._headersCapability.promise;
}
async read() {
await this._headersCapability.promise;
if (this._storedError) {
throw this._storedError;
}
if (this._cachedChunks.length > 0) {
const chunk = this._cachedChunks.shift();
return {
value: chunk,
done: false
};
}
if (this._done) {
return {
value: undefined,
done: true
};
}
const requestCapability = Promise.withResolvers();
this._requests.push(requestCapability);
return requestCapability.promise;
}
cancel(reason) {
this._done = true;
this._headersCapability.reject(reason);
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
if (this._manager.isPendingRequest(this._fullRequestId)) {
this._manager.abortRequest(this._fullRequestId);
}
this._fullRequestReader = null;
}
}
class PDFNetworkStreamRangeRequestReader {
constructor(manager, begin, end) {
this._manager = manager;
this._url = manager.url;
this._requestId = manager.request({
begin,
end,
onHeadersReceived: this._onHeadersReceived.bind(this),
onDone: this._onDone.bind(this),
onError: this._onError.bind(this),
onProgress: this._onProgress.bind(this)
});
this._requests = [];
this._queuedChunk = null;
this._done = false;
this._storedError = undefined;
this.onProgress = null;
this.onClosed = null;
}
_onHeadersReceived() {
const responseOrigin = getResponseOrigin(this._manager.getRequestXhr(this._requestId)?.responseURL);
if (responseOrigin !== this._manager._responseOrigin) {
this._storedError = new Error(`Expected range response-origin "${responseOrigin}" to match "${this._manager._responseOrigin}".`);
this._onError(0);
}
}
_close() {
this.onClosed?.(this);
}
_onDone(data) {
const chunk = data.chunk;
if (this._requests.length > 0) {
const requestCapability = this._requests.shift();
requestCapability.resolve({
value: chunk,
done: false
});
} else {
this._queuedChunk = chunk;
}
this._done = true;
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
this._close();
}
_onError(status) {
this._storedError ??= createResponseStatusError(status, this._url);
for (const requestCapability of this._requests) {
requestCapability.reject(this._storedError);
}
this._requests.length = 0;
this._queuedChunk = null;
}
_onProgress(evt) {
if (!this.isStreamingSupported) {
this.onProgress?.({
loaded: evt.loaded
});
}
}
get isStreamingSupported() {
return false;
}
async read() {
if (this._storedError) {
throw this._storedError;
}
if (this._queuedChunk !== null) {
const chunk = this._queuedChunk;
this._queuedChunk = null;
return {
value: chunk,
done: false
};
}
if (this._done) {
return {
value: undefined,
done: true
};
}
const requestCapability = Promise.withResolvers();
this._requests.push(requestCapability);
return requestCapability.promise;
}
cancel(reason) {
this._done = true;
for (const requestCapability of this._requests) {
requestCapability.resolve({
value: undefined,
done: true
});
}
this._requests.length = 0;
if (this._manager.isPendingRequest(this._requestId)) {
this._manager.abortRequest(this._requestId);
}
this._close();
}
}
;// ./src/display/node_stream.js
const urlRegex = /^[a-z][a-z0-9\-+.]+:/i;
function parseUrlOrPath(sourceUrl) {
if (urlRegex.test(sourceUrl)) {
return new URL(sourceUrl);
}
const url = process.getBuiltinModule("url");
return new URL(url.pathToFileURL(sourceUrl));
}
class PDFNodeStream {
constructor(source) {
this.source = source;
this.url = parseUrlOrPath(source.url);
(0,util.assert)(this.url.protocol === "file:", "PDFNodeStream only supports file:// URLs.");
this._fullRequestReader = null;
this._rangeRequestReaders = [];
}
get _progressiveDataLength() {
return this._fullRequestReader?._loaded ?? 0;
}
getFullReader() {
(0,util.assert)(!this._fullRequestReader, "PDFNodeStream.getFullReader can only be called once.");
this._fullRequestReader = new PDFNodeStreamFsFullReader(this);
return this._fullRequestReader;
}
getRangeReader(start, end) {
if (end <= this._progressiveDataLength) {
return null;
}
const rangeReader = new PDFNodeStreamFsRangeReader(this, start, end);
this._rangeRequestReaders.push(rangeReader);
return rangeReader;
}
cancelAllRequests(reason) {
this._fullRequestReader?.cancel(reason);
for (const reader of this._rangeRequestReaders.slice(0)) {
reader.cancel(reason);
}
}
}
class PDFNodeStreamFsFullReader {
constructor(stream) {
this._url = stream.url;
this._done = false;
this._storedError = null;
this.onProgress = null;
const source = stream.source;
this._contentLength = source.length;
this._loaded = 0;
this._filename = null;
this._disableRange = source.disableRange || false;
this._rangeChunkSize = source.rangeChunkSize;
if (!this._rangeChunkSize && !this._disableRange) {
this._disableRange = true;
}
this._isStreamingSupported = !source.disableStream;
this._isRangeSupported = !source.disableRange;
this._readableStream = null;
this._readCapability = Promise.withResolvers();
this._headersCapability = Promise.withResolvers();
const fs = process.getBuiltinModule("fs");
fs.promises.lstat(this._url).then(stat => {
this._contentLength = stat.size;
this._setReadableStream(fs.createReadStream(this._url));
this._headersCapability.resolve();
}, error => {
if (error.code === "ENOENT") {
error = new util.MissingPDFException(`Missing PDF "${this._url}".`);
}
this._storedError = error;
this._headersCapability.reject(error);
});
}
get headersReady() {
return this._headersCapability.promise;
}
get filename() {
return this._filename;
}
get contentLength() {
return this._contentLength;
}
get isRangeSupported() {
return this._isRangeSupported;
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
async read() {
await this._readCapability.promise;
if (this._done) {
return {
value: undefined,
done: true
};
}
if (this._storedError) {
throw this._storedError;
}
const chunk = this._readableStream.read();
if (chunk === null) {
this._readCapability = Promise.withResolvers();
return this.read();
}
this._loaded += chunk.length;
this.onProgress?.({
loaded: this._loaded,
total: this._contentLength
});
const buffer = new Uint8Array(chunk).buffer;
return {
value: buffer,
done: false
};
}
cancel(reason) {
if (!this._readableStream) {
this._error(reason);
return;
}
this._readableStream.destroy(reason);
}
_error(reason) {
this._storedError = reason;
this._readCapability.resolve();
}
_setReadableStream(readableStream) {
this._readableStream = readableStream;
readableStream.on("readable", () => {
this._readCapability.resolve();
});
readableStream.on("end", () => {
readableStream.destroy();
this._done = true;
this._readCapability.resolve();
});
readableStream.on("error", reason => {
this._error(reason);
});
if (!this._isStreamingSupported && this._isRangeSupported) {
this._error(new util.AbortException("streaming is disabled"));
}
if (this._storedError) {
this._readableStream.destroy(this._storedError);
}
}
}
class PDFNodeStreamFsRangeReader {
constructor(stream, start, end) {
this._url = stream.url;
this._done = false;
this._storedError = null;
this.onProgress = null;
this._loaded = 0;
this._readableStream = null;
this._readCapability = Promise.withResolvers();
const source = stream.source;
this._isStreamingSupported = !source.disableStream;
const fs = process.getBuiltinModule("fs");
this._setReadableStream(fs.createReadStream(this._url, {
start,
end: end - 1
}));
}
get isStreamingSupported() {
return this._isStreamingSupported;
}
async read() {
await this._readCapability.promise;
if (this._done) {
return {
value: undefined,
done: true
};
}
if (this._storedError) {
throw this._storedError;
}
const chunk = this._readableStream.read();
if (chunk === null) {
this._readCapability = Promise.withResolvers();
return this.read();
}
this._loaded += chunk.length;
this.onProgress?.({
loaded: this._loaded
});
const buffer = new Uint8Array(chunk).buffer;
return {
value: buffer,
done: false
};
}
cancel(reason) {
if (!this._readableStream) {
this._error(reason);
return;
}
this._readableStream.destroy(reason);
}
_error(reason) {
this._storedError = reason;
this._readCapability.resolve();
}
_setReadableStream(readableStream) {
this._readableStream = readableStream;
readableStream.on("readable", () => {
this._readCapability.resolve();
});
readableStream.on("end", () => {
readableStream.destroy();
this._done = true;
this._readCapability.resolve();
});
readableStream.on("error", reason => {
this._error(reason);
});
if (this._storedError) {
this._readableStream.destroy(this._storedError);
}
}
}
// EXTERNAL MODULE: ./src/display/text_layer.js
var text_layer = __webpack_require__(814);
// EXTERNAL MODULE: ./src/display/xfa_text.js
var xfa_text = __webpack_require__(50);
;// ./src/display/api.js
const DEFAULT_RANGE_CHUNK_SIZE = 65536;
const RENDERING_CANCELLED_TIMEOUT = 100;
const DELAYED_CLEANUP_TIMEOUT = 5000;
const DefaultCanvasFactory = util.isNodeJS ? NodeCanvasFactory : DOMCanvasFactory;
const DefaultCMapReaderFactory = util.isNodeJS ? NodeCMapReaderFactory : DOMCMapReaderFactory;
const DefaultFilterFactory = util.isNodeJS ? NodeFilterFactory : DOMFilterFactory;
const DefaultStandardFontDataFactory = util.isNodeJS ? NodeStandardFontDataFactory : DOMStandardFontDataFactory;
function getDocument(src = {}) {
if (typeof src === "string" || src instanceof URL) {
src = {
url: src
};
} else if (src instanceof ArrayBuffer || ArrayBuffer.isView(src)) {
src = {
data: src
};
}
const task = new PDFDocumentLoadingTask();
const {
docId
} = task;
const url = src.url ? getUrlProp(src.url, src.baseHref) : null;
const data = src.data ? getDataProp(src.data) : null;
const httpHeaders = src.httpHeaders || null;
const withCredentials = src.withCredentials === true;
const password = src.password ?? null;
const rangeTransport = src.range instanceof PDFDataRangeTransport ? src.range : null;
const rangeChunkSize = Number.isInteger(src.rangeChunkSize) && src.rangeChunkSize > 0 ? src.rangeChunkSize : DEFAULT_RANGE_CHUNK_SIZE;
let worker = src.worker instanceof PDFWorker ? src.worker : null;
const verbosity = src.verbosity;
const docBaseUrl = typeof src.docBaseUrl === "string" && !(0,display_utils.isDataScheme)(src.docBaseUrl) ? src.docBaseUrl : null;
const cMapUrl = typeof src.cMapUrl === "function" ? src.cMapUrl() : typeof src.cMapUrl === "string" ? src.cMapUrl : null;
const cMapPacked = src.cMapPacked !== false;
const CMapReaderFactory = src.CMapReaderFactory || DefaultCMapReaderFactory;
const standardFontDataUrl = typeof src.standardFontDataUrl === "function" ? src.standardFontDataUrl() : typeof src.standardFontDataUrl === "string" ? src.standardFontDataUrl : null;
const StandardFontDataFactory = src.StandardFontDataFactory || DefaultStandardFontDataFactory;
const ignoreErrors = src.stopAtErrors !== true;
const maxImageSize = Number.isInteger(src.maxImageSize) && src.maxImageSize > -1 ? src.maxImageSize : -1;
const isEvalSupported = src.isEvalSupported !== false;
const isOffscreenCanvasSupported = typeof src.isOffscreenCanvasSupported === "boolean" ? src.isOffscreenCanvasSupported : !util.isNodeJS;
const isImageDecoderSupported = typeof src.isImageDecoderSupported === "boolean" ? src.isImageDecoderSupported : !util.isNodeJS && (util.FeatureTest.platform.isFirefox || !globalThis.chrome);
const canvasMaxAreaInBytes = Number.isInteger(src.canvasMaxAreaInBytes) ? src.canvasMaxAreaInBytes : -1;
const disableFontFace = typeof src.disableFontFace === "boolean" ? src.disableFontFace : util.isNodeJS;
const fontExtraProperties = src.fontExtraProperties === true;
const enableXfa = src.enableXfa === true;
const ownerDocument = src.ownerDocument || globalThis.document;
const disableRange = src.disableRange === true;
const disableStream = src.disableStream === true;
const disableAutoFetch = src.disableAutoFetch === true;
const pdfBug = src.pdfBug === true;
const CanvasFactory = src.CanvasFactory || DefaultCanvasFactory;
const FilterFactory = src.FilterFactory || DefaultFilterFactory;
const enableHWA = src.enableHWA === true;
const length = rangeTransport ? rangeTransport.length : src.length ?? NaN;
const useSystemFonts = typeof src.useSystemFonts === "boolean" ? src.useSystemFonts : !util.isNodeJS && !disableFontFace;
const useWorkerFetch = typeof src.useWorkerFetch === "boolean" ? src.useWorkerFetch : CMapReaderFactory === DOMCMapReaderFactory && StandardFontDataFactory === DOMStandardFontDataFactory && cMapUrl && standardFontDataUrl && (0,display_utils.isValidFetchUrl)(cMapUrl, document.baseURI) && (0,display_utils.isValidFetchUrl)(standardFontDataUrl, document.baseURI);
const styleElement = null;
(0,util.setVerbosityLevel)(verbosity);
const transportFactory = {
canvasFactory: new CanvasFactory({
ownerDocument,
enableHWA
}),
filterFactory: new FilterFactory({
docId,
ownerDocument
}),
cMapReaderFactory: useWorkerFetch ? null : new CMapReaderFactory({
baseUrl: cMapUrl,
isCompressed: cMapPacked
}),
standardFontDataFactory: useWorkerFetch ? null : new StandardFontDataFactory({
baseUrl: standardFontDataUrl
})
};
if (!worker) {
const workerParams = {
verbosity,
port: worker_options.GlobalWorkerOptions.workerPort,
cspPolicyService: src.cspPolicyService
};
worker = workerParams.port ? PDFWorker.fromPort(workerParams) : new PDFWorker(workerParams);
task._worker = worker;
}
const docParams = {
docId,
apiVersion: "4.10.708",
data,
password,
disableAutoFetch,
rangeChunkSize,
length,
docBaseUrl,
enableXfa,
evaluatorOptions: {
maxImageSize,
disableFontFace,
ignoreErrors,
isEvalSupported,
isOffscreenCanvasSupported,
isImageDecoderSupported,
canvasMaxAreaInBytes,
fontExtraProperties,
useSystemFonts,
cMapUrl: useWorkerFetch ? cMapUrl : null,
standardFontDataUrl: useWorkerFetch ? standardFontDataUrl : null
}
};
const transportParams = {
disableFontFace,
fontExtraProperties,
ownerDocument,
pdfBug,
styleElement,
loadingParams: {
disableAutoFetch,
enableXfa
}
};
worker.promise.then(function () {
if (task.destroyed) {
throw new Error("Loading aborted");
}
if (worker.destroyed) {
throw new Error("Worker was destroyed");
}
const workerIdPromise = worker.messageHandler.sendWithPromise("GetDocRequest", docParams, data ? [data.buffer] : null);
let networkStream;
if (rangeTransport) {
networkStream = new PDFDataTransportStream(rangeTransport, {
disableRange,
disableStream
});
} else if (!data) {
if (!url) {
throw new Error("getDocument - no `url` parameter provided.");
}
let NetworkStream;
if (util.isNodeJS) {
if ((0,display_utils.isValidFetchUrl)(url)) {
if (typeof fetch === "undefined" || typeof Response === "undefined" || !("body" in Response.prototype)) {
throw new Error("getDocument - the Fetch API was disabled in Node.js, see `--no-experimental-fetch`.");
}
NetworkStream = PDFFetchStream;
} else {
NetworkStream = PDFNodeStream;
}
} else {
NetworkStream = (0,display_utils.isValidFetchUrl)(url) ? PDFFetchStream : PDFNetworkStream;
}
networkStream = new NetworkStream({
url,
length,
httpHeaders,
withCredentials,
rangeChunkSize,
disableRange,
disableStream
});
}
return workerIdPromise.then(workerId => {
if (task.destroyed) {
throw new Error("Loading aborted");
}
if (worker.destroyed) {
throw new Error("Worker was destroyed");
}
const messageHandler = new MessageHandler(docId, workerId, worker.port);
const transport = new WorkerTransport(messageHandler, task, networkStream, transportParams, transportFactory);
task._transport = transport;
messageHandler.send("Ready", null);
});
}).catch(task._capability.reject);
return task;
}
function getUrlProp(val, baseHref) {
if (val instanceof URL) {
return val.href;
}
try {
if (baseHref) {
return new URL(val, window.location.origin + baseHref).href;
} else {
return new URL(val, window.location).href;
}
} catch (ex) {
if (util.isNodeJS && typeof val === "string") {
return val;
}
}
throw new Error("Invalid PDF url data: " + "either string or URL-object is expected in the url property.");
}
function getDataProp(val) {
if (util.isNodeJS && typeof Buffer !== "undefined" && val instanceof Buffer) {
throw new Error("Please provide binary data as `Uint8Array`, rather than `Buffer`.");
}
if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
return val;
}
if (typeof val === "string") {
return (0,util.stringToBytes)(val);
}
if (val instanceof ArrayBuffer || ArrayBuffer.isView(val) || typeof val === "object" && !isNaN(val?.length)) {
return new Uint8Array(val);
}
throw new Error("Invalid PDF binary data: either TypedArray, " + "string, or array-like object is expected in the data property.");
}
function isRefProxy(ref) {
return typeof ref === "object" && Number.isInteger(ref?.num) && ref.num >= 0 && Number.isInteger(ref?.gen) && ref.gen >= 0;
}
class PDFDocumentLoadingTask {
static #docId = 0;
constructor() {
this._capability = Promise.withResolvers();
this._transport = null;
this._worker = null;
this.docId = `d${PDFDocumentLoadingTask.#docId++}`;
this.destroyed = false;
this.onPassword = null;
this.onProgress = null;
}
get promise() {
return this._capability.promise;
}
async destroy() {
this.destroyed = true;
try {
if (this._worker?.port) {
this._worker._pendingDestroy = true;
}
await this._transport?.destroy();
} catch (ex) {
if (this._worker?.port) {
delete this._worker._pendingDestroy;
}
throw ex;
}
this._transport = null;
this._worker?.destroy();
this._worker = null;
}
}
class PDFDataRangeTransport {
constructor(length, initialData, progressiveDone = false, contentDispositionFilename = null) {
this.length = length;
this.initialData = initialData;
this.progressiveDone = progressiveDone;
this.contentDispositionFilename = contentDispositionFilename;
this._rangeListeners = [];
this._progressListeners = [];
this._progressiveReadListeners = [];
this._progressiveDoneListeners = [];
this._readyCapability = Promise.withResolvers();
}
addRangeListener(listener) {
this._rangeListeners.push(listener);
}
addProgressListener(listener) {
this._progressListeners.push(listener);
}
addProgressiveReadListener(listener) {
this._progressiveReadListeners.push(listener);
}
addProgressiveDoneListener(listener) {
this._progressiveDoneListeners.push(listener);
}
onDataRange(begin, chunk) {
for (const listener of this._rangeListeners) {
listener(begin, chunk);
}
}
onDataProgress(loaded, total) {
this._readyCapability.promise.then(() => {
for (const listener of this._progressListeners) {
listener(loaded, total);
}
});
}
onDataProgressiveRead(chunk) {
this._readyCapability.promise.then(() => {
for (const listener of this._progressiveReadListeners) {
listener(chunk);
}
});
}
onDataProgressiveDone() {
this._readyCapability.promise.then(() => {
for (const listener of this._progressiveDoneListeners) {
listener();
}
});
}
transportReady() {
this._readyCapability.resolve();
}
requestDataRange(begin, end) {
(0,util.unreachable)("Abstract method PDFDataRangeTransport.requestDataRange");
}
abort() {}
}
class PDFDocumentProxy {
constructor(pdfInfo, transport) {
this._pdfInfo = pdfInfo;
this._transport = transport;
}
get annotationStorage() {
return this._transport.annotationStorage;
}
get canvasFactory() {
return this._transport.canvasFactory;
}
get filterFactory() {
return this._transport.filterFactory;
}
get numPages() {
return this._pdfInfo.numPages;
}
get fingerprints() {
return this._pdfInfo.fingerprints;
}
get isPureXfa() {
return (0,util.shadow)(this, "isPureXfa", !!this._transport._htmlForXfa);
}
get allXfaHtml() {
return this._transport._htmlForXfa;
}
getPage(pageNumber) {
return this._transport.getPage(pageNumber);
}
getPageIndex(ref) {
return this._transport.getPageIndex(ref);
}
getDestinations() {
return this._transport.getDestinations();
}
getDestination(id) {
return this._transport.getDestination(id);
}
getPageLabels() {
return this._transport.getPageLabels();
}
getPageLayout() {
return this._transport.getPageLayout();
}
getPageMode() {
return this._transport.getPageMode();
}
getViewerPreferences() {
return this._transport.getViewerPreferences();
}
getOpenAction() {
return this._transport.getOpenAction();
}
getAttachments() {
return this._transport.getAttachments();
}
getJSActions() {
return this._transport.getDocJSActions();
}
getOutline() {
return this._transport.getOutline();
}
getOptionalContentConfig({
intent = "display"
} = {}) {
const {
renderingIntent
} = this._transport.getRenderingIntent(intent);
return this._transport.getOptionalContentConfig(renderingIntent);
}
getPermissions() {
return this._transport.getPermissions();
}
getMetadata() {
return this._transport.getMetadata();
}
getMarkInfo() {
return this._transport.getMarkInfo();
}
getData() {
return this._transport.getData();
}
saveDocument() {
return this._transport.saveDocument();
}
getDownloadInfo() {
return this._transport.downloadInfoCapability.promise;
}
cleanup(keepLoadedFonts = false) {
return this._transport.startCleanup(keepLoadedFonts || this.isPureXfa);
}
destroy() {
return this.loadingTask.destroy();
}
cachedPageNumber(ref) {
return this._transport.cachedPageNumber(ref);
}
get loadingParams() {
return this._transport.loadingParams;
}
get loadingTask() {
return this._transport.loadingTask;
}
getFieldObjects() {
return this._transport.getFieldObjects();
}
hasJSActions() {
return this._transport.hasJSActions();
}
getCalculationOrderIds() {
return this._transport.getCalculationOrderIds();
}
}
class PDFPageProxy {
#delayedCleanupTimeout = null;
#pendingCleanup = false;
constructor(pageIndex, pageInfo, transport, pdfBug = false) {
this._pageIndex = pageIndex;
this._pageInfo = pageInfo;
this._transport = transport;
this._stats = pdfBug ? new display_utils.StatTimer() : null;
this._pdfBug = pdfBug;
this.commonObjs = transport.commonObjs;
this.objs = new PDFObjects();
this._maybeCleanupAfterRender = false;
this._intentStates = new Map();
this.destroyed = false;
}
get pageNumber() {
return this._pageIndex + 1;
}
get rotate() {
return this._pageInfo.rotate;
}
get ref() {
return this._pageInfo.ref;
}
get userUnit() {
return this._pageInfo.userUnit;
}
get view() {
return this._pageInfo.view;
}
getViewport({
scale,
rotation = this.rotate,
offsetX = 0,
offsetY = 0,
dontFlip = false
} = {}) {
return new display_utils.PageViewport({
viewBox: this.view,
userUnit: this.userUnit,
scale,
rotation,
offsetX,
offsetY,
dontFlip
});
}
getAnnotations({
intent = "display"
} = {}) {
const {
renderingIntent
} = this._transport.getRenderingIntent(intent);
return this._transport.getAnnotations(this._pageIndex, renderingIntent);
}
getJSActions() {
return this._transport.getPageJSActions(this._pageIndex);
}
get filterFactory() {
return this._transport.filterFactory;
}
get isPureXfa() {
return (0,util.shadow)(this, "isPureXfa", !!this._transport._htmlForXfa);
}
async getXfa() {
return this._transport._htmlForXfa?.children[this._pageIndex] || null;
}
render({
canvasContext,
viewport,
intent = "display",
annotationMode = util.AnnotationMode.ENABLE,
transform = null,
background = null,
optionalContentConfigPromise = null,
annotationCanvasMap = null,
pageColors = null,
printAnnotationStorage = null,
isEditing = false
}) {
this._stats?.time("Overall");
const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing);
const {
renderingIntent,
cacheKey
} = intentArgs;
this.#pendingCleanup = false;
this.#abortDelayedCleanup();
optionalContentConfigPromise ||= this._transport.getOptionalContentConfig(renderingIntent);
let intentState = this._intentStates.get(cacheKey);
if (!intentState) {
intentState = Object.create(null);
this._intentStates.set(cacheKey, intentState);
}
if (intentState.streamReaderCancelTimeout) {
clearTimeout(intentState.streamReaderCancelTimeout);
intentState.streamReaderCancelTimeout = null;
}
const intentPrint = !!(renderingIntent & util.RenderingIntentFlag.PRINT);
if (!intentState.displayReadyCapability) {
intentState.displayReadyCapability = Promise.withResolvers();
intentState.operatorList = {
fnArray: [],
argsArray: [],
lastChunk: false,
separateAnnots: null
};
this._stats?.time("Page Request");
this._pumpOperatorList(intentArgs);
}
const complete = error => {
intentState.renderTasks.delete(internalRenderTask);
if (this._maybeCleanupAfterRender || intentPrint) {
this.#pendingCleanup = true;
}
this.#tryCleanup(!intentPrint);
if (error) {
internalRenderTask.capability.reject(error);
this._abortOperatorList({
intentState,
reason: error instanceof Error ? error : new Error(error)
});
} else {
internalRenderTask.capability.resolve();
}
if (this._stats) {
this._stats.timeEnd("Rendering");
this._stats.timeEnd("Overall");
if (globalThis.Stats?.enabled) {
globalThis.Stats.add(this.pageNumber, this._stats);
}
}
};
const internalRenderTask = new InternalRenderTask({
callback: complete,
params: {
canvasContext,
viewport,
transform,
background
},
objs: this.objs,
commonObjs: this.commonObjs,
annotationCanvasMap,
operatorList: intentState.operatorList,
pageIndex: this._pageIndex,
canvasFactory: this._transport.canvasFactory,
filterFactory: this._transport.filterFactory,
useRequestAnimationFrame: !intentPrint,
pdfBug: this._pdfBug,
pageColors
});
(intentState.renderTasks ||= new Set()).add(internalRenderTask);
const renderTask = internalRenderTask.task;
Promise.all([intentState.displayReadyCapability.promise, optionalContentConfigPromise]).then(([transparency, optionalContentConfig]) => {
if (this.destroyed) {
complete();
return;
}
this._stats?.time("Rendering");
if (!(optionalContentConfig.renderingIntent & renderingIntent)) {
throw new Error("Must use the same `intent`-argument when calling the `PDFPageProxy.render` " + "and `PDFDocumentProxy.getOptionalContentConfig` methods.");
}
internalRenderTask.initializeGraphics({
transparency,
optionalContentConfig
});
internalRenderTask.operatorListChanged();
}).catch(complete);
return renderTask;
}
getOperatorList({
intent = "display",
annotationMode = util.AnnotationMode.ENABLE,
printAnnotationStorage = null,
isEditing = false
} = {}) {
function operatorListChanged() {
if (intentState.operatorList.lastChunk) {
intentState.opListReadCapability.resolve(intentState.operatorList);
intentState.renderTasks.delete(opListTask);
}
}
const intentArgs = this._transport.getRenderingIntent(intent, annotationMode, printAnnotationStorage, isEditing, true);
let intentState = this._intentStates.get(intentArgs.cacheKey);
if (!intentState) {
intentState = Object.create(null);
this._intentStates.set(intentArgs.cacheKey, intentState);
}
let opListTask;
if (!intentState.opListReadCapability) {
opListTask = Object.create(null);
opListTask.operatorListChanged = operatorListChanged;
intentState.opListReadCapability = Promise.withResolvers();
(intentState.renderTasks ||= new Set()).add(opListTask);
intentState.operatorList = {
fnArray: [],
argsArray: [],
lastChunk: false,
separateAnnots: null
};
this._stats?.time("Page Request");
this._pumpOperatorList(intentArgs);
}
return intentState.opListReadCapability.promise;
}
streamTextContent({
includeMarkedContent = false,
disableNormalization = false
} = {}) {
const TEXT_CONTENT_CHUNK_SIZE = 100;
return this._transport.messageHandler.sendWithStream("GetTextContent", {
pageIndex: this._pageIndex,
includeMarkedContent: includeMarkedContent === true,
disableNormalization: disableNormalization === true
}, {
highWaterMark: TEXT_CONTENT_CHUNK_SIZE,
size(textContent) {
return textContent.items.length;
}
});
}
getTextContent(params = {}) {
if (this._transport._htmlForXfa) {
return this.getXfa().then(xfa => xfa_text.XfaText.textContent(xfa));
}
const readableStream = this.streamTextContent(params);
return new Promise(function (resolve, reject) {
function pump() {
reader.read().then(function ({
value,
done
}) {
if (done) {
resolve(textContent);
return;
}
textContent.lang ??= value.lang;
Object.assign(textContent.styles, value.styles);
textContent.items.push(...value.items);
pump();
}, reject);
}
const reader = readableStream.getReader();
const textContent = {
items: [],
styles: Object.create(null),
lang: null
};
pump();
});
}
getStructTree() {
return this._transport.getStructTree(this._pageIndex);
}
_destroy() {
this.destroyed = true;
const waitOn = [];
for (const intentState of this._intentStates.values()) {
this._abortOperatorList({
intentState,
reason: new Error("Page was destroyed."),
force: true
});
if (intentState.opListReadCapability) {
continue;
}
for (const internalRenderTask of intentState.renderTasks) {
waitOn.push(internalRenderTask.completed);
internalRenderTask.cancel();
}
}
this.objs.clear();
this.#pendingCleanup = false;
this.#abortDelayedCleanup();
return Promise.all(waitOn);
}
cleanup(resetStats = false) {
this.#pendingCleanup = true;
const success = this.#tryCleanup(false);
if (resetStats && success) {
this._stats &&= new display_utils.StatTimer();
}
return success;
}
#tryCleanup(delayed = false) {
this.#abortDelayedCleanup();
if (!this.#pendingCleanup || this.destroyed) {
return false;
}
if (delayed) {
this.#delayedCleanupTimeout = setTimeout(() => {
this.#delayedCleanupTimeout = null;
this.#tryCleanup(false);
}, DELAYED_CLEANUP_TIMEOUT);
return false;
}
for (const {
renderTasks,
operatorList
} of this._intentStates.values()) {
if (renderTasks.size > 0 || !operatorList.lastChunk) {
return false;
}
}
this._intentStates.clear();
this.objs.clear();
this.#pendingCleanup = false;
return true;
}
#abortDelayedCleanup() {
if (this.#delayedCleanupTimeout) {
clearTimeout(this.#delayedCleanupTimeout);
this.#delayedCleanupTimeout = null;
}
}
_startRenderPage(transparency, cacheKey) {
const intentState = this._intentStates.get(cacheKey);
if (!intentState) {
return;
}
this._stats?.timeEnd("Page Request");
intentState.displayReadyCapability?.resolve(transparency);
}
_renderPageChunk(operatorListChunk, intentState) {
for (let i = 0, ii = operatorListChunk.length; i < ii; i++) {
intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
intentState.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
}
intentState.operatorList.lastChunk = operatorListChunk.lastChunk;
intentState.operatorList.separateAnnots = operatorListChunk.separateAnnots;
for (const internalRenderTask of intentState.renderTasks) {
internalRenderTask.operatorListChanged();
}
if (operatorListChunk.lastChunk) {
this.#tryCleanup(true);
}
}
_pumpOperatorList({
renderingIntent,
cacheKey,
annotationStorageSerializable,
modifiedIds
}) {
const {
map,
transfer
} = annotationStorageSerializable;
const readableStream = this._transport.messageHandler.sendWithStream("GetOperatorList", {
pageIndex: this._pageIndex,
intent: renderingIntent,
cacheKey,
annotationStorage: map,
modifiedIds
}, transfer);
const reader = readableStream.getReader();
const intentState = this._intentStates.get(cacheKey);
intentState.streamReader = reader;
const pump = () => {
reader.read().then(({
value,
done
}) => {
if (done) {
intentState.streamReader = null;
return;
}
if (this._transport.destroyed) {
return;
}
this._renderPageChunk(value, intentState);
pump();
}, reason => {
intentState.streamReader = null;
if (this._transport.destroyed) {
return;
}
if (intentState.operatorList) {
intentState.operatorList.lastChunk = true;
for (const internalRenderTask of intentState.renderTasks) {
internalRenderTask.operatorListChanged();
}
this.#tryCleanup(true);
}
if (intentState.displayReadyCapability) {
intentState.displayReadyCapability.reject(reason);
} else if (intentState.opListReadCapability) {
intentState.opListReadCapability.reject(reason);
} else {
throw reason;
}
});
};
pump();
}
_abortOperatorList({
intentState,
reason,
force = false
}) {
if (!intentState.streamReader) {
return;
}
if (intentState.streamReaderCancelTimeout) {
clearTimeout(intentState.streamReaderCancelTimeout);
intentState.streamReaderCancelTimeout = null;
}
if (!force) {
if (intentState.renderTasks.size > 0) {
return;
}
if (reason instanceof display_utils.RenderingCancelledException) {
let delay = RENDERING_CANCELLED_TIMEOUT;
if (reason.extraDelay > 0 && reason.extraDelay < 1000) {
delay += reason.extraDelay;
}
intentState.streamReaderCancelTimeout = setTimeout(() => {
intentState.streamReaderCancelTimeout = null;
this._abortOperatorList({
intentState,
reason,
force: true
});
}, delay);
return;
}
}
intentState.streamReader.cancel(new util.AbortException(reason.message)).catch(() => {});
intentState.streamReader = null;
if (this._transport.destroyed) {
return;
}
for (const [curCacheKey, curIntentState] of this._intentStates) {
if (curIntentState === intentState) {
this._intentStates.delete(curCacheKey);
break;
}
}
this.cleanup();
}
get stats() {
return this._stats;
}
}
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);
}
});
}
addEventListener(name, listener, options = null) {
let rmAbort = null;
if (options?.signal instanceof AbortSignal) {
const {
signal
} = options;
if (signal.aborted) {
(0,util.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);
}
removeEventListener(name, listener) {
const rmAbort = this.#listeners.get(listener);
rmAbort?.();
this.#listeners.delete(listener);
}
terminate() {
for (const [, rmAbort] of this.#listeners) {
rmAbort?.();
}
this.#listeners.clear();
}
}
class PDFWorker {
static #fakeWorkerId = 0;
static #isWorkerDisabled = false;
static #workerPorts;
static {
if (util.isNodeJS) {
this.#isWorkerDisabled = true;
worker_options.GlobalWorkerOptions.workerSrc ||= "./pdf.worker.mjs";
}
this._isSameOrigin = (baseUrl, otherUrl) => {
let base;
try {
base = new URL(baseUrl);
if (!base.origin || base.origin === "null") {
return false;
}
} catch {
return false;
}
const other = new URL(otherUrl, base);
return base.origin === other.origin;
};
this._createCDNWrapper = url => {
const wrapper = `await import("${url}");`;
return URL.createObjectURL(new Blob([wrapper], {
type: "text/javascript"
}));
};
}
constructor({
name = null,
port = null,
verbosity = (0,util.getVerbosityLevel)(),
cspPolicyService
} = {}) {
this.name = name;
this.destroyed = false;
this.verbosity = verbosity;
this._readyCapability = Promise.withResolvers();
this._port = null;
this._webWorker = null;
this._messageHandler = null;
if (port) {
if (PDFWorker.#workerPorts?.has(port)) {
throw new Error("Cannot use more than one PDFWorker per port.");
}
(PDFWorker.#workerPorts ||= new WeakMap()).set(port, this);
this._initializeFromPort(port);
return;
}
this._initialize(cspPolicyService);
}
get promise() {
return this._readyCapability.promise;
}
#resolve() {
this._readyCapability.resolve();
this._messageHandler.send("configure", {
verbosity: this.verbosity
});
}
get port() {
return this._port;
}
get messageHandler() {
return this._messageHandler;
}
_initializeFromPort(port) {
this._port = port;
this._messageHandler = new MessageHandler("main", "worker", port);
this._messageHandler.on("ready", function () {});
this.#resolve();
}
_initialize(cspPolicyService) {
if (PDFWorker.#isWorkerDisabled || PDFWorker.#mainThreadWorkerMessageHandler) {
this._setupFakeWorker();
return;
}
let {
workerSrc
} = PDFWorker;
try {
if (!PDFWorker._isSameOrigin(window.location.href, workerSrc)) {
workerSrc = PDFWorker._createCDNWrapper(new URL(workerSrc, window.location).href);
}
const worker = new Worker(cspPolicyService.generateTrustedURL(workerSrc), {
type: "module"
});
const messageHandler = new MessageHandler("main", "worker", worker);
const terminateEarly = () => {
ac.abort();
messageHandler.destroy();
worker.terminate();
if (this.destroyed) {
this._readyCapability.reject(new Error("Worker was destroyed"));
} else {
this._setupFakeWorker();
}
};
const ac = new AbortController();
worker.addEventListener("error", () => {
if (!this._webWorker) {
terminateEarly();
}
}, {
signal: ac.signal
});
messageHandler.on("test", data => {
ac.abort();
if (this.destroyed || !data) {
terminateEarly();
return;
}
this._messageHandler = messageHandler;
this._port = worker;
this._webWorker = worker;
this.#resolve();
});
messageHandler.on("ready", data => {
ac.abort();
if (this.destroyed) {
terminateEarly();
return;
}
try {
sendTest();
} catch {
this._setupFakeWorker();
}
});
const sendTest = () => {
const testObj = new Uint8Array();
messageHandler.send("test", testObj, [testObj.buffer]);
};
sendTest();
return;
} catch {
(0,util.info)("The worker has been disabled.");
}
this._setupFakeWorker();
}
_setupFakeWorker() {
if (!PDFWorker.#isWorkerDisabled) {
(0,util.warn)("Setting up fake worker.");
PDFWorker.#isWorkerDisabled = true;
}
PDFWorker._setupFakeWorkerGlobal.then(WorkerMessageHandler => {
if (this.destroyed) {
this._readyCapability.reject(new Error("Worker was destroyed"));
return;
}
const port = new LoopbackPort();
this._port = port;
const id = `fake${PDFWorker.#fakeWorkerId++}`;
const workerHandler = new MessageHandler(id + "_worker", id, port);
WorkerMessageHandler.setup(workerHandler, port);
this._messageHandler = new MessageHandler(id, id + "_worker", port);
this.#resolve();
}).catch(reason => {
this._readyCapability.reject(new Error(`Setting up fake worker failed: "${reason.message}".`));
});
}
destroy() {
this.destroyed = true;
this._webWorker?.terminate();
this._webWorker = null;
PDFWorker.#workerPorts?.delete(this._port);
this._port = null;
this._messageHandler?.destroy();
this._messageHandler = null;
}
static fromPort(params) {
if (!params?.port) {
throw new Error("PDFWorker.fromPort - invalid method signature.");
}
const cachedPort = this.#workerPorts?.get(params.port);
if (cachedPort) {
if (cachedPort._pendingDestroy) {
throw new Error("PDFWorker.fromPort - the worker is being destroyed.\n" + "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls.");
}
return cachedPort;
}
return new PDFWorker(params);
}
static get workerSrc() {
if (worker_options.GlobalWorkerOptions.workerSrc) {
return worker_options.GlobalWorkerOptions.workerSrc;
}
throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
}
static get #mainThreadWorkerMessageHandler() {
try {
return globalThis.pdfjsWorker?.WorkerMessageHandler || null;
} catch {
return null;
}
}
static get _setupFakeWorkerGlobal() {
const loader = async () => {
if (this.#mainThreadWorkerMessageHandler) {
return this.#mainThreadWorkerMessageHandler;
}
const worker = await import(/*webpackIgnore: true*/this.workerSrc);
return worker.WorkerMessageHandler;
};
return (0,util.shadow)(this, "_setupFakeWorkerGlobal", loader());
}
}
class WorkerTransport {
#methodPromises = new Map();
#pageCache = new Map();
#pagePromises = new Map();
#pageRefCache = new Map();
#passwordCapability = null;
constructor(messageHandler, loadingTask, networkStream, params, factory) {
this.messageHandler = messageHandler;
this.loadingTask = loadingTask;
this.commonObjs = new PDFObjects();
this.fontLoader = new FontLoader({
ownerDocument: params.ownerDocument,
styleElement: params.styleElement
});
this.loadingParams = params.loadingParams;
this._params = params;
this.canvasFactory = factory.canvasFactory;
this.filterFactory = factory.filterFactory;
this.cMapReaderFactory = factory.cMapReaderFactory;
this.standardFontDataFactory = factory.standardFontDataFactory;
this.destroyed = false;
this.destroyCapability = null;
this._networkStream = networkStream;
this._fullReader = null;
this._lastProgress = null;
this.downloadInfoCapability = Promise.withResolvers();
this.setupMessageHandler();
}
#cacheSimpleMethod(name, data = null) {
const cachedPromise = this.#methodPromises.get(name);
if (cachedPromise) {
return cachedPromise;
}
const promise = this.messageHandler.sendWithPromise(name, data);
this.#methodPromises.set(name, promise);
return promise;
}
get annotationStorage() {
return (0,util.shadow)(this, "annotationStorage", new annotation_storage.AnnotationStorage());
}
getRenderingIntent(intent, annotationMode = util.AnnotationMode.ENABLE, printAnnotationStorage = null, isEditing = false, isOpList = false) {
let renderingIntent = util.RenderingIntentFlag.DISPLAY;
let annotationStorageSerializable = annotation_storage.SerializableEmpty;
switch (intent) {
case "any":
renderingIntent = util.RenderingIntentFlag.ANY;
break;
case "display":
break;
case "print":
renderingIntent = util.RenderingIntentFlag.PRINT;
break;
default:
(0,util.warn)(`getRenderingIntent - invalid intent: ${intent}`);
}
const annotationStorage = renderingIntent & util.RenderingIntentFlag.PRINT && printAnnotationStorage instanceof annotation_storage.PrintAnnotationStorage ? printAnnotationStorage : this.annotationStorage;
switch (annotationMode) {
case util.AnnotationMode.DISABLE:
renderingIntent += util.RenderingIntentFlag.ANNOTATIONS_DISABLE;
break;
case util.AnnotationMode.ENABLE:
break;
case util.AnnotationMode.ENABLE_FORMS:
renderingIntent += util.RenderingIntentFlag.ANNOTATIONS_FORMS;
break;
case util.AnnotationMode.ENABLE_STORAGE:
renderingIntent += util.RenderingIntentFlag.ANNOTATIONS_STORAGE;
annotationStorageSerializable = annotationStorage.serializable;
break;
default:
(0,util.warn)(`getRenderingIntent - invalid annotationMode: ${annotationMode}`);
}
if (isEditing) {
renderingIntent += util.RenderingIntentFlag.IS_EDITING;
}
if (isOpList) {
renderingIntent += util.RenderingIntentFlag.OPLIST;
}
const {
ids: modifiedIds,
hash: modifiedIdsHash
} = annotationStorage.modifiedIds;
const cacheKeyBuf = [renderingIntent, annotationStorageSerializable.hash, modifiedIdsHash];
return {
renderingIntent,
cacheKey: cacheKeyBuf.join("_"),
annotationStorageSerializable,
modifiedIds
};
}
destroy() {
if (this.destroyCapability) {
return this.destroyCapability.promise;
}
this.destroyed = true;
this.destroyCapability = Promise.withResolvers();
this.#passwordCapability?.reject(new Error("Worker was destroyed during onPassword callback"));
const waitOn = [];
for (const page of this.#pageCache.values()) {
waitOn.push(page._destroy());
}
this.#pageCache.clear();
this.#pagePromises.clear();
this.#pageRefCache.clear();
if (this.hasOwnProperty("annotationStorage")) {
this.annotationStorage.resetModified();
}
const terminated = this.messageHandler.sendWithPromise("Terminate", null);
waitOn.push(terminated);
Promise.all(waitOn).then(() => {
this.commonObjs.clear();
this.fontLoader.clear();
this.#methodPromises.clear();
this.filterFactory.destroy();
text_layer.TextLayer.cleanup();
this._networkStream?.cancelAllRequests(new util.AbortException("Worker was terminated."));
this.messageHandler?.destroy();
this.messageHandler = null;
this.destroyCapability.resolve();
}, this.destroyCapability.reject);
return this.destroyCapability.promise;
}
setupMessageHandler() {
const {
messageHandler,
loadingTask
} = this;
messageHandler.on("GetReader", (data, sink) => {
(0,util.assert)(this._networkStream, "GetReader - no `IPDFStream` instance available.");
this._fullReader = this._networkStream.getFullReader();
this._fullReader.onProgress = evt => {
this._lastProgress = {
loaded: evt.loaded,
total: evt.total
};
};
sink.onPull = () => {
this._fullReader.read().then(function ({
value,
done
}) {
if (done) {
sink.close();
return;
}
(0,util.assert)(value instanceof ArrayBuffer, "GetReader - expected an ArrayBuffer.");
sink.enqueue(new Uint8Array(value), 1, [value]);
}).catch(reason => {
sink.error(reason);
});
};
sink.onCancel = reason => {
this._fullReader.cancel(reason);
sink.ready.catch(readyReason => {
if (this.destroyed) {
return;
}
throw readyReason;
});
};
});
messageHandler.on("ReaderHeadersReady", async data => {
await this._fullReader.headersReady;
const {
isStreamingSupported,
isRangeSupported,
contentLength
} = this._fullReader;
if (!isStreamingSupported || !isRangeSupported) {
if (this._lastProgress) {
loadingTask.onProgress?.(this._lastProgress);
}
this._fullReader.onProgress = evt => {
loadingTask.onProgress?.({
loaded: evt.loaded,
total: evt.total
});
};
}
return {
isStreamingSupported,
isRangeSupported,
contentLength
};
});
messageHandler.on("GetRangeReader", (data, sink) => {
(0,util.assert)(this._networkStream, "GetRangeReader - no `IPDFStream` instance available.");
const rangeReader = this._networkStream.getRangeReader(data.begin, data.end);
if (!rangeReader) {
sink.close();
return;
}
sink.onPull = () => {
rangeReader.read().then(function ({
value,
done
}) {
if (done) {
sink.close();
return;
}
(0,util.assert)(value instanceof ArrayBuffer, "GetRangeReader - expected an ArrayBuffer.");
sink.enqueue(new Uint8Array(value), 1, [value]);
}).catch(reason => {
sink.error(reason);
});
};
sink.onCancel = reason => {
rangeReader.cancel(reason);
sink.ready.catch(readyReason => {
if (this.destroyed) {
return;
}
throw readyReason;
});
};
});
messageHandler.on("GetDoc", ({
pdfInfo
}) => {
this._numPages = pdfInfo.numPages;
this._htmlForXfa = pdfInfo.htmlForXfa;
delete pdfInfo.htmlForXfa;
loadingTask._capability.resolve(new PDFDocumentProxy(pdfInfo, this));
});
messageHandler.on("DocException", ex => {
loadingTask._capability.reject(wrapReason(ex));
});
messageHandler.on("PasswordRequest", ex => {
this.#passwordCapability = Promise.withResolvers();
try {
if (!loadingTask.onPassword) {
throw wrapReason(ex);
}
const updatePassword = password => {
if (password instanceof Error) {
this.#passwordCapability.reject(password);
} else {
this.#passwordCapability.resolve({
password
});
}
};
loadingTask.onPassword(updatePassword, ex.code);
} catch (err) {
this.#passwordCapability.reject(err);
}
return this.#passwordCapability.promise;
});
messageHandler.on("DataLoaded", data => {
loadingTask.onProgress?.({
loaded: data.length,
total: data.length
});
this.downloadInfoCapability.resolve(data);
});
messageHandler.on("StartRenderPage", data => {
if (this.destroyed) {
return;
}
const page = this.#pageCache.get(data.pageIndex);
page._startRenderPage(data.transparency, data.cacheKey);
});
messageHandler.on("commonobj", ([id, type, exportedData]) => {
if (this.destroyed) {
return null;
}
if (this.commonObjs.has(id)) {
return null;
}
switch (type) {
case "Font":
const {
disableFontFace,
fontExtraProperties,
pdfBug
} = this._params;
if ("error" in exportedData) {
const exportedError = exportedData.error;
(0,util.warn)(`Error during font loading: ${exportedError}`);
this.commonObjs.resolve(id, exportedError);
break;
}
const inspectFont = pdfBug && globalThis.FontInspector?.enabled ? (font, url) => globalThis.FontInspector.fontAdded(font, url) : null;
const font = new FontFaceObject(exportedData, {
disableFontFace,
fontExtraProperties,
inspectFont
});
this.fontLoader.bind(font).catch(() => messageHandler.sendWithPromise("FontFallback", {
id
})).finally(() => {
if (!fontExtraProperties && font.data) {
font.data = null;
}
this.commonObjs.resolve(id, font);
});
break;
case "CopyLocalImage":
const {
imageRef
} = exportedData;
(0,util.assert)(imageRef, "The imageRef must be defined.");
for (const pageProxy of this.#pageCache.values()) {
for (const [, data] of pageProxy.objs) {
if (data?.ref !== imageRef) {
continue;
}
if (!data.dataLen) {
return null;
}
this.commonObjs.resolve(id, structuredClone(data));
return data.dataLen;
}
}
break;
case "FontPath":
case "Image":
case "Pattern":
this.commonObjs.resolve(id, exportedData);
break;
default:
throw new Error(`Got unknown common object type ${type}`);
}
return null;
});
messageHandler.on("obj", ([id, pageIndex, type, imageData]) => {
if (this.destroyed) {
return;
}
const pageProxy = this.#pageCache.get(pageIndex);
if (pageProxy.objs.has(id)) {
return;
}
if (pageProxy._intentStates.size === 0) {
imageData?.bitmap?.close();
return;
}
switch (type) {
case "Image":
pageProxy.objs.resolve(id, imageData);
if (imageData?.dataLen > util.MAX_IMAGE_SIZE_TO_CACHE) {
pageProxy._maybeCleanupAfterRender = true;
}
break;
case "Pattern":
pageProxy.objs.resolve(id, imageData);
break;
default:
throw new Error(`Got unknown object type ${type}`);
}
});
messageHandler.on("DocProgress", data => {
if (this.destroyed) {
return;
}
loadingTask.onProgress?.({
loaded: data.loaded,
total: data.total
});
});
messageHandler.on("FetchBuiltInCMap", async data => {
if (this.destroyed) {
throw new Error("Worker was destroyed.");
}
if (!this.cMapReaderFactory) {
throw new Error("CMapReaderFactory not initialized, see the `useWorkerFetch` parameter.");
}
return this.cMapReaderFactory.fetch(data);
});
messageHandler.on("FetchStandardFontData", async data => {
if (this.destroyed) {
throw new Error("Worker was destroyed.");
}
if (!this.standardFontDataFactory) {
throw new Error("StandardFontDataFactory not initialized, see the `useWorkerFetch` parameter.");
}
return this.standardFontDataFactory.fetch(data);
});
}
getData() {
return this.messageHandler.sendWithPromise("GetData", null);
}
saveDocument() {
if (this.annotationStorage.size <= 0) {
(0,util.warn)("saveDocument called while `annotationStorage` is empty, " + "please use the getData-method instead.");
}
const {
map,
transfer
} = this.annotationStorage.serializable;
return this.messageHandler.sendWithPromise("SaveDocument", {
isPureXfa: !!this._htmlForXfa,
numPages: this._numPages,
annotationStorage: map,
filename: this._fullReader?.filename ?? null
}, transfer).finally(() => {
this.annotationStorage.resetModified();
});
}
getPage(pageNumber) {
if (!Number.isInteger(pageNumber) || pageNumber <= 0 || pageNumber > this._numPages) {
return Promise.reject(new Error("Invalid page request."));
}
const pageIndex = pageNumber - 1,
cachedPromise = this.#pagePromises.get(pageIndex);
if (cachedPromise) {
return cachedPromise;
}
const promise = this.messageHandler.sendWithPromise("GetPage", {
pageIndex
}).then(pageInfo => {
if (this.destroyed) {
throw new Error("Transport destroyed");
}
if (pageInfo.refStr) {
this.#pageRefCache.set(pageInfo.refStr, pageNumber);
}
const page = new PDFPageProxy(pageIndex, pageInfo, this, this._params.pdfBug);
this.#pageCache.set(pageIndex, page);
return page;
});
this.#pagePromises.set(pageIndex, promise);
return promise;
}
getPageIndex(ref) {
if (!isRefProxy(ref)) {
return Promise.reject(new Error("Invalid pageIndex request."));
}
return this.messageHandler.sendWithPromise("GetPageIndex", {
num: ref.num,
gen: ref.gen
});
}
getAnnotations(pageIndex, intent) {
return this.messageHandler.sendWithPromise("GetAnnotations", {
pageIndex,
intent
});
}
getFieldObjects() {
return this.#cacheSimpleMethod("GetFieldObjects");
}
hasJSActions() {
return this.#cacheSimpleMethod("HasJSActions");
}
getCalculationOrderIds() {
return this.messageHandler.sendWithPromise("GetCalculationOrderIds", null);
}
getDestinations() {
return this.messageHandler.sendWithPromise("GetDestinations", null);
}
getDestination(id) {
if (typeof id !== "string") {
return Promise.reject(new Error("Invalid destination request."));
}
return this.messageHandler.sendWithPromise("GetDestination", {
id
});
}
getPageLabels() {
return this.messageHandler.sendWithPromise("GetPageLabels", null);
}
getPageLayout() {
return this.messageHandler.sendWithPromise("GetPageLayout", null);
}
getPageMode() {
return this.messageHandler.sendWithPromise("GetPageMode", null);
}
getViewerPreferences() {
return this.messageHandler.sendWithPromise("GetViewerPreferences", null);
}
getOpenAction() {
return this.messageHandler.sendWithPromise("GetOpenAction", null);
}
getAttachments() {
return this.messageHandler.sendWithPromise("GetAttachments", null);
}
getDocJSActions() {
return this.#cacheSimpleMethod("GetDocJSActions");
}
getPageJSActions(pageIndex) {
return this.messageHandler.sendWithPromise("GetPageJSActions", {
pageIndex
});
}
getStructTree(pageIndex) {
return this.messageHandler.sendWithPromise("GetStructTree", {
pageIndex
});
}
getOutline() {
return this.messageHandler.sendWithPromise("GetOutline", null);
}
getOptionalContentConfig(renderingIntent) {
return this.#cacheSimpleMethod("GetOptionalContentConfig").then(data => new OptionalContentConfig(data, renderingIntent));
}
getPermissions() {
return this.messageHandler.sendWithPromise("GetPermissions", null);
}
getMetadata() {
const name = "GetMetadata",
cachedPromise = this.#methodPromises.get(name);
if (cachedPromise) {
return cachedPromise;
}
const promise = this.messageHandler.sendWithPromise(name, null).then(results => ({
info: results[0],
metadata: results[1] ? new Metadata(results[1]) : null,
contentDispositionFilename: this._fullReader?.filename ?? null,
contentLength: this._fullReader?.contentLength ?? null
}));
this.#methodPromises.set(name, promise);
return promise;
}
getMarkInfo() {
return this.messageHandler.sendWithPromise("GetMarkInfo", null);
}
async startCleanup(keepLoadedFonts = false) {
if (this.destroyed) {
return;
}
await this.messageHandler.sendWithPromise("Cleanup", null);
for (const page of this.#pageCache.values()) {
const cleanupSuccessful = page.cleanup();
if (!cleanupSuccessful) {
throw new Error(`startCleanup: Page ${page.pageNumber} is currently rendering.`);
}
}
this.commonObjs.clear();
if (!keepLoadedFonts) {
this.fontLoader.clear();
}
this.#methodPromises.clear();
this.filterFactory.destroy(true);
text_layer.TextLayer.cleanup();
}
cachedPageNumber(ref) {
if (!isRefProxy(ref)) {
return null;
}
const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`;
return this.#pageRefCache.get(refStr) ?? null;
}
}
const INITIAL_DATA = Symbol("INITIAL_DATA");
class PDFObjects {
#objs = Object.create(null);
#ensureObj(objId) {
return this.#objs[objId] ||= {
...Promise.withResolvers(),
data: INITIAL_DATA
};
}
get(objId, callback = null) {
if (callback) {
const obj = this.#ensureObj(objId);
obj.promise.then(() => callback(obj.data));
return null;
}
const obj = this.#objs[objId];
if (!obj || obj.data === INITIAL_DATA) {
throw new Error(`Requesting object that isn't resolved yet ${objId}.`);
}
return obj.data;
}
has(objId) {
const obj = this.#objs[objId];
return !!obj && obj.data !== INITIAL_DATA;
}
delete(objId) {
const obj = this.#objs[objId];
if (!obj || obj.data === INITIAL_DATA) {
return false;
}
delete this.#objs[objId];
return true;
}
resolve(objId, data = null) {
const obj = this.#ensureObj(objId);
obj.data = data;
obj.resolve();
}
clear() {
for (const objId in this.#objs) {
const {
data
} = this.#objs[objId];
data?.bitmap?.close();
}
this.#objs = Object.create(null);
}
*[Symbol.iterator]() {
for (const objId in this.#objs) {
const {
data
} = this.#objs[objId];
if (data === INITIAL_DATA) {
continue;
}
yield [objId, data];
}
}
}
class RenderTask {
#internalRenderTask = null;
constructor(internalRenderTask) {
this.#internalRenderTask = internalRenderTask;
this.onContinue = null;
}
get promise() {
return this.#internalRenderTask.capability.promise;
}
cancel(extraDelay = 0) {
this.#internalRenderTask.cancel(null, extraDelay);
}
get separateAnnots() {
const {
separateAnnots
} = this.#internalRenderTask.operatorList;
if (!separateAnnots) {
return false;
}
const {
annotationCanvasMap
} = this.#internalRenderTask;
return separateAnnots.form || separateAnnots.canvas && annotationCanvasMap?.size > 0;
}
}
class InternalRenderTask {
#rAF = null;
static #canvasInUse = new WeakSet();
constructor({
callback,
params,
objs,
commonObjs,
annotationCanvasMap,
operatorList,
pageIndex,
canvasFactory,
filterFactory,
useRequestAnimationFrame = false,
pdfBug = false,
pageColors = null
}) {
this.callback = callback;
this.params = params;
this.objs = objs;
this.commonObjs = commonObjs;
this.annotationCanvasMap = annotationCanvasMap;
this.operatorListIdx = null;
this.operatorList = operatorList;
this._pageIndex = pageIndex;
this.canvasFactory = canvasFactory;
this.filterFactory = filterFactory;
this._pdfBug = pdfBug;
this.pageColors = pageColors;
this.running = false;
this.graphicsReadyCallback = null;
this.graphicsReady = false;
this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined";
this.cancelled = false;
this.capability = Promise.withResolvers();
this.task = new RenderTask(this);
this._cancelBound = this.cancel.bind(this);
this._continueBound = this._continue.bind(this);
this._scheduleNextBound = this._scheduleNext.bind(this);
this._nextBound = this._next.bind(this);
this._canvas = params.canvasContext.canvas;
}
get completed() {
return this.capability.promise.catch(function () {});
}
initializeGraphics({
transparency = false,
optionalContentConfig
}) {
if (this.cancelled) {
return;
}
if (this._canvas) {
if (InternalRenderTask.#canvasInUse.has(this._canvas)) {
throw new Error("Cannot use the same canvas during multiple render() operations. " + "Use different canvas or ensure previous operations were " + "cancelled or completed.");
}
InternalRenderTask.#canvasInUse.add(this._canvas);
}
if (this._pdfBug && globalThis.StepperManager?.enabled) {
this.stepper = globalThis.StepperManager.create(this._pageIndex);
this.stepper.init(this.operatorList);
this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint();
}
const {
canvasContext,
viewport,
transform,
background
} = this.params;
this.gfx = new CanvasGraphics(canvasContext, this.commonObjs, this.objs, this.canvasFactory, this.filterFactory, {
optionalContentConfig
}, this.annotationCanvasMap, this.pageColors);
this.gfx.beginDrawing({
transform,
viewport,
transparency,
background
});
this.operatorListIdx = 0;
this.graphicsReady = true;
this.graphicsReadyCallback?.();
}
cancel(error = null, extraDelay = 0) {
this.running = false;
this.cancelled = true;
this.gfx?.endDrawing();
if (this.#rAF) {
window.cancelAnimationFrame(this.#rAF);
this.#rAF = null;
}
InternalRenderTask.#canvasInUse.delete(this._canvas);
this.callback(error || new display_utils.RenderingCancelledException(`Rendering cancelled, page ${this._pageIndex + 1}`, extraDelay));
}
operatorListChanged() {
if (!this.graphicsReady) {
this.graphicsReadyCallback ||= this._continueBound;
return;
}
this.stepper?.updateOperatorList(this.operatorList);
if (this.running) {
return;
}
this._continue();
}
_continue() {
this.running = true;
if (this.cancelled) {
return;
}
if (this.task.onContinue) {
this.task.onContinue(this._scheduleNextBound);
} else {
this._scheduleNext();
}
}
_scheduleNext() {
if (this._useRequestAnimationFrame) {
this.#rAF = window.requestAnimationFrame(() => {
this.#rAF = null;
this._nextBound().catch(this._cancelBound);
});
} else {
Promise.resolve().then(this._nextBound).catch(this._cancelBound);
}
}
async _next() {
if (this.cancelled) {
return;
}
this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continueBound, this.stepper);
if (this.operatorListIdx === this.operatorList.argsArray.length) {
this.running = false;
if (this.operatorList.lastChunk) {
this.gfx.endDrawing();
InternalRenderTask.#canvasInUse.delete(this._canvas);
this.callback();
}
}
}
}
const version = "4.10.708";
const build = "f47106c3e";
/***/ }),
/***/ 419:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ OutputScale: () => (/* binding */ OutputScale),
/* harmony export */ PDFDateString: () => (/* binding */ PDFDateString),
/* harmony export */ PageViewport: () => (/* binding */ PageViewport),
/* harmony export */ PixelsPerInch: () => (/* binding */ PixelsPerInch),
/* harmony export */ RenderingCancelledException: () => (/* binding */ RenderingCancelledException),
/* harmony export */ SVG_NS: () => (/* binding */ SVG_NS),
/* harmony export */ StatTimer: () => (/* binding */ StatTimer),
/* harmony export */ fetchData: () => (/* binding */ fetchData),
/* harmony export */ getColorValues: () => (/* binding */ getColorValues),
/* harmony export */ getCurrentTransform: () => (/* binding */ getCurrentTransform),
/* harmony export */ getCurrentTransformInverse: () => (/* binding */ getCurrentTransformInverse),
/* harmony export */ getFilenameFromUrl: () => (/* binding */ getFilenameFromUrl),
/* harmony export */ getPdfFilenameFromUrl: () => (/* binding */ getPdfFilenameFromUrl),
/* harmony export */ getRGB: () => (/* binding */ getRGB),
/* harmony export */ getXfaPageViewport: () => (/* binding */ getXfaPageViewport),
/* harmony export */ isDataScheme: () => (/* binding */ isDataScheme),
/* harmony export */ isPdfFile: () => (/* binding */ isPdfFile),
/* harmony export */ isValidFetchUrl: () => (/* binding */ isValidFetchUrl),
/* harmony export */ noContextMenu: () => (/* binding */ noContextMenu),
/* harmony export */ setLayerDimensions: () => (/* binding */ setLayerDimensions),
/* harmony export */ stopEvent: () => (/* binding */ stopEvent)
/* harmony export */ });
/* unused harmony export deprecated */
/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(292);
const SVG_NS = "http://www.w3.org/2000/svg";
class PixelsPerInch {
static CSS = 96.0;
static PDF = 72.0;
static PDF_TO_CSS_UNITS = this.CSS / this.PDF;
}
async function fetchData(url, type = "text") {
if (isValidFetchUrl(url, document.baseURI)) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(response.statusText);
}
switch (type) {
case "arraybuffer":
return response.arrayBuffer();
case "blob":
return response.blob();
case "json":
return response.json();
}
return response.text();
}
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = type;
request.onreadystatechange = () => {
if (request.readyState !== XMLHttpRequest.DONE) {
return;
}
if (request.status === 200 || request.status === 0) {
switch (type) {
case "arraybuffer":
case "blob":
case "json":
resolve(request.response);
return;
}
resolve(request.responseText);
return;
}
reject(new Error(request.statusText));
};
request.send(null);
});
}
class PageViewport {
constructor({
viewBox,
userUnit,
scale,
rotation,
offsetX = 0,
offsetY = 0,
dontFlip = false
}) {
this.viewBox = viewBox;
this.userUnit = userUnit;
this.scale = scale;
this.rotation = rotation;
this.offsetX = offsetX;
this.offsetY = offsetY;
scale *= userUnit;
const centerX = (viewBox[2] + viewBox[0]) / 2;
const centerY = (viewBox[3] + viewBox[1]) / 2;
let rotateA, rotateB, rotateC, rotateD;
rotation %= 360;
if (rotation < 0) {
rotation += 360;
}
switch (rotation) {
case 180:
rotateA = -1;
rotateB = 0;
rotateC = 0;
rotateD = 1;
break;
case 90:
rotateA = 0;
rotateB = 1;
rotateC = 1;
rotateD = 0;
break;
case 270:
rotateA = 0;
rotateB = -1;
rotateC = -1;
rotateD = 0;
break;
case 0:
rotateA = 1;
rotateB = 0;
rotateC = 0;
rotateD = -1;
break;
default:
throw new Error("PageViewport: Invalid rotation, must be a multiple of 90 degrees.");
}
if (dontFlip) {
rotateC = -rotateC;
rotateD = -rotateD;
}
let offsetCanvasX, offsetCanvasY;
let width, height;
if (rotateA === 0) {
offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
width = (viewBox[3] - viewBox[1]) * scale;
height = (viewBox[2] - viewBox[0]) * scale;
} else {
offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
width = (viewBox[2] - viewBox[0]) * scale;
height = (viewBox[3] - viewBox[1]) * scale;
}
this.transform = [rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY];
this.width = width;
this.height = height;
}
get rawDims() {
const {
userUnit,
viewBox
} = this;
const dims = viewBox.map(x => x * userUnit);
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "rawDims", {
pageWidth: dims[2] - dims[0],
pageHeight: dims[3] - dims[1],
pageX: dims[0],
pageY: dims[1]
});
}
clone({
scale = this.scale,
rotation = this.rotation,
offsetX = this.offsetX,
offsetY = this.offsetY,
dontFlip = false
} = {}) {
return new PageViewport({
viewBox: this.viewBox.slice(),
userUnit: this.userUnit,
scale,
rotation,
offsetX,
offsetY,
dontFlip
});
}
convertToViewportPoint(x, y) {
return _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.applyTransform([x, y], this.transform);
}
convertToViewportRectangle(rect) {
const topLeft = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.applyTransform([rect[0], rect[1]], this.transform);
const bottomRight = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.applyTransform([rect[2], rect[3]], this.transform);
return [topLeft[0], topLeft[1], bottomRight[0], bottomRight[1]];
}
convertToPdfPoint(x, y) {
return _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.applyInverseTransform([x, y], this.transform);
}
}
class RenderingCancelledException extends _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.BaseException {
constructor(msg, extraDelay = 0) {
super(msg, "RenderingCancelledException");
this.extraDelay = extraDelay;
}
}
function isDataScheme(url) {
const ii = url.length;
let i = 0;
while (i < ii && url[i].trim() === "") {
i++;
}
return url.substring(i, i + 5).toLowerCase() === "data:";
}
function isPdfFile(filename) {
return typeof filename === "string" && /\.pdf$/i.test(filename);
}
function getFilenameFromUrl(url) {
[url] = url.split(/[#?]/, 1);
return url.substring(url.lastIndexOf("/") + 1);
}
function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") {
if (defaultFilename && defaultFilename !== "document.pdf") {
return defaultFilename;
}
if (typeof url !== "string") {
return defaultFilename;
}
if (isDataScheme(url)) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)('getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.');
return defaultFilename;
}
const reURI = /^(?:(?:[^:]+:)?\/\/[^/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
const reFilename = /[^/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
const splitURI = reURI.exec(url);
let suggestedFilename = reFilename.exec(splitURI[1]) || reFilename.exec(splitURI[2]) || reFilename.exec(splitURI[3]);
if (suggestedFilename) {
suggestedFilename = suggestedFilename[0];
if (suggestedFilename.includes("%")) {
try {
suggestedFilename = reFilename.exec(decodeURIComponent(suggestedFilename))[0];
} catch {}
}
}
return suggestedFilename || defaultFilename;
}
class StatTimer {
started = Object.create(null);
times = [];
time(name) {
if (name in this.started) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Timer is already running for ${name}`);
}
this.started[name] = Date.now();
}
timeEnd(name) {
if (!(name in this.started)) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Timer has not been started for ${name}`);
}
this.times.push({
name,
start: this.started[name],
end: Date.now()
});
delete this.started[name];
}
toString() {
const outBuf = [];
let longest = 0;
for (const {
name
} of this.times) {
longest = Math.max(name.length, longest);
}
for (const {
name,
start,
end
} of this.times) {
outBuf.push(`${name.padEnd(longest)} ${end - start}ms\n`);
}
return outBuf.join("");
}
}
function isValidFetchUrl(url, baseUrl) {
try {
const {
protocol
} = baseUrl ? new URL(url, baseUrl) : new URL(url);
return protocol === "http:" || protocol === "https:" || protocol === "capacitor:";
} catch {
return false;
}
}
function generateTrustedURL(sourcePath) {
if (window.trustedTypes) {
const sanitizer = window.trustedTypes.createPolicy("pdf-viewer-2", {
createScriptURL: url => url
});
return sanitizer.createScriptURL(sourcePath);
}
return sourcePath;
}
function noContextMenu(e) {
e.preventDefault();
}
function stopEvent(e) {
e.preventDefault();
e.stopPropagation();
}
function deprecated(details) {
NgxConsole.log("Deprecated API usage: " + details);
}
class PDFDateString {
static #regex;
static toDateObject(input) {
if (!input || typeof input !== "string") {
return null;
}
this.#regex ||= new RegExp("^D:" + "(\\d{4})" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "(\\d{2})?" + "([Z|+|-])?" + "(\\d{2})?" + "'?" + "(\\d{2})?" + "'?");
const matches = this.#regex.exec(input);
if (!matches) {
return null;
}
const year = parseInt(matches[1], 10);
let month = parseInt(matches[2], 10);
month = month >= 1 && month <= 12 ? month - 1 : 0;
let day = parseInt(matches[3], 10);
day = day >= 1 && day <= 31 ? day : 1;
let hour = parseInt(matches[4], 10);
hour = hour >= 0 && hour <= 23 ? hour : 0;
let minute = parseInt(matches[5], 10);
minute = minute >= 0 && minute <= 59 ? minute : 0;
let second = parseInt(matches[6], 10);
second = second >= 0 && second <= 59 ? second : 0;
const universalTimeRelation = matches[7] || "Z";
let offsetHour = parseInt(matches[8], 10);
offsetHour = offsetHour >= 0 && offsetHour <= 23 ? offsetHour : 0;
let offsetMinute = parseInt(matches[9], 10) || 0;
offsetMinute = offsetMinute >= 0 && offsetMinute <= 59 ? offsetMinute : 0;
if (universalTimeRelation === "-") {
hour += offsetHour;
minute += offsetMinute;
} else if (universalTimeRelation === "+") {
hour -= offsetHour;
minute -= offsetMinute;
}
return new Date(Date.UTC(year, month, day, hour, minute, second));
}
}
function getXfaPageViewport(xfaPage, {
scale = 1,
rotation = 0
}) {
const {
width,
height
} = xfaPage.attributes.style;
const viewBox = [0, 0, parseInt(width), parseInt(height)];
return new PageViewport({
viewBox,
userUnit: 1,
scale,
rotation
});
}
function getRGB(color) {
if (color.startsWith("#")) {
const colorRGB = parseInt(color.slice(1), 16);
return [(colorRGB & 0xff0000) >> 16, (colorRGB & 0x00ff00) >> 8, colorRGB & 0x0000ff];
}
if (color.startsWith("rgb(")) {
return color.slice(4, -1).split(",").map(x => parseInt(x));
}
if (color.startsWith("rgba(")) {
return color.slice(5, -1).split(",").map(x => parseInt(x)).slice(0, 3);
}
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`Not a valid color format: "${color}"`);
return [0, 0, 0];
}
function getColorValues(colors) {
const span = document.createElement("span");
span.style.visibility = "hidden";
document.body.append(span);
for (const name of colors.keys()) {
span.style.color = name;
const computedColor = window.getComputedStyle(span).color;
colors.set(name, getRGB(computedColor));
}
span.remove();
}
function getCurrentTransform(ctx) {
const {
a,
b,
c,
d,
e,
f
} = ctx.getTransform();
return [a, b, c, d, e, f];
}
function getCurrentTransformInverse(ctx) {
const {
a,
b,
c,
d,
e,
f
} = ctx.getTransform().invertSelf();
return [a, b, c, d, e, f];
}
function setLayerDimensions(div, viewport, mustFlip = false, mustRotate = true) {
if (viewport instanceof PageViewport) {
const {
pageWidth,
pageHeight
} = viewport.rawDims;
const {
style
} = div;
const useRound = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest.isCSSRoundSupported;
const w = `var(--scale-factor) * ${pageWidth}px`,
h = `var(--scale-factor) * ${pageHeight}px`;
const widthStr = useRound ? `round(down, ${w}, var(--scale-round-x, 1px))` : `calc(${w})`,
heightStr = useRound ? `round(down, ${h}, var(--scale-round-y, 1px))` : `calc(${h})`;
if (!mustFlip || viewport.rotation % 180 === 0) {
style.width = widthStr;
style.height = heightStr;
} else {
style.width = heightStr;
style.height = widthStr;
}
}
if (mustRotate) {
div.setAttribute("data-main-rotation", viewport.rotation);
}
}
class OutputScale {
constructor() {
const pixelRatio = window.devicePixelRatio || 1;
this.sx = pixelRatio;
this.sy = pixelRatio;
}
get scaled() {
return this.sx !== 1 || this.sy !== 1;
}
get symmetric() {
return this.sx === this.sy;
}
}
/***/ }),
/***/ 310:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
AnnotationEditor: () => (/* binding */ AnnotationEditor)
});
// EXTERNAL MODULE: ./src/display/editor/tools.js
var tools = __webpack_require__(830);
// EXTERNAL MODULE: ./src/shared/util.js
var util = __webpack_require__(292);
// EXTERNAL MODULE: ./src/display/display_utils.js
var display_utils = __webpack_require__(419);
;// ./src/display/editor/alt_text.js
class AltText {
#altText = null;
#altTextDecorative = false;
#altTextButton = null;
#altTextButtonLabel = null;
#altTextTooltip = null;
#altTextTooltipTimeout = null;
#altTextWasFromKeyBoard = false;
#badge = null;
#editor = null;
#guessedText = null;
#textWithDisclaimer = null;
#useNewAltTextFlow = false;
static #l10nNewButton = null;
static _l10n = null;
constructor(editor) {
this.#editor = editor;
this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow;
AltText.#l10nNewButton ||= Object.freeze({
added: "pdfjs-editor-new-alt-text-added-button",
"added-label": "pdfjs-editor-new-alt-text-added-button-label",
missing: "pdfjs-editor-new-alt-text-missing-button",
"missing-label": "pdfjs-editor-new-alt-text-missing-button-label",
review: "pdfjs-editor-new-alt-text-to-review-button",
"review-label": "pdfjs-editor-new-alt-text-to-review-button-label"
});
}
static initialize(l10n) {
AltText._l10n ??= l10n;
}
async render() {
const altText = this.#altTextButton = document.createElement("button");
altText.className = "altText";
altText.tabIndex = "0";
const label = this.#altTextButtonLabel = document.createElement("span");
altText.append(label);
if (this.#useNewAltTextFlow) {
altText.classList.add("new");
altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing);
label.setAttribute("data-l10n-id", AltText.#l10nNewButton["missing-label"]);
} else {
altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button");
label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label");
}
const signal = this.#editor._uiManager._signal;
altText.addEventListener("contextmenu", display_utils.noContextMenu, {
signal
});
altText.addEventListener("pointerdown", event => event.stopPropagation(), {
signal
});
const onClick = event => {
event.preventDefault();
this.#editor._uiManager.editAltText(this.#editor);
if (this.#useNewAltTextFlow) {
this.#editor._reportTelemetry({
action: "pdfjs.image.alt_text.image_status_label_clicked",
data: {
label: this.#label
}
});
}
};
altText.addEventListener("click", onClick, {
capture: true,
signal
});
altText.addEventListener("keydown", event => {
if (event.target === altText && event.key === "Enter") {
this.#altTextWasFromKeyBoard = true;
onClick(event);
}
}, {
signal
});
await this.#setState();
return altText;
}
get #label() {
return this.#altText && "added" || this.#altText === null && this.guessedText && "review" || "missing";
}
finish() {
if (!this.#altTextButton) {
return;
}
this.#altTextButton.focus({
focusVisible: this.#altTextWasFromKeyBoard
});
this.#altTextWasFromKeyBoard = false;
}
isEmpty() {
if (this.#useNewAltTextFlow) {
return this.#altText === null;
}
return !this.#altText && !this.#altTextDecorative;
}
hasData() {
if (this.#useNewAltTextFlow) {
return this.#altText !== null || !!this.#guessedText;
}
return this.isEmpty();
}
get guessedText() {
return this.#guessedText;
}
async setGuessedText(guessedText) {
if (this.#altText !== null) {
return;
}
this.#guessedText = guessedText;
this.#textWithDisclaimer = await AltText._l10n.get("pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer", {
generatedAltText: guessedText
});
this.#setState();
}
toggleAltTextBadge(visibility = false) {
if (!this.#useNewAltTextFlow || this.#altText) {
this.#badge?.remove();
this.#badge = null;
return;
}
if (!this.#badge) {
const badge = this.#badge = document.createElement("div");
badge.className = "noAltTextBadge";
this.#editor.div.append(badge);
}
this.#badge.classList.toggle("hidden", !visibility);
}
serialize(isForCopying) {
let altText = this.#altText;
if (!isForCopying && this.#guessedText === altText) {
altText = this.#textWithDisclaimer;
}
return {
altText,
decorative: this.#altTextDecorative,
guessedText: this.#guessedText,
textWithDisclaimer: this.#textWithDisclaimer
};
}
get data() {
return {
altText: this.#altText,
decorative: this.#altTextDecorative
};
}
set data({
altText,
decorative,
guessedText,
textWithDisclaimer,
cancel = false
}) {
if (guessedText) {
this.#guessedText = guessedText;
this.#textWithDisclaimer = textWithDisclaimer;
}
if (this.#altText === altText && this.#altTextDecorative === decorative) {
return;
}
if (!cancel) {
this.#altText = altText;
this.#altTextDecorative = decorative;
}
this.#setState();
}
toggle(enabled = false) {
if (!this.#altTextButton) {
return;
}
if (!enabled && this.#altTextTooltipTimeout) {
clearTimeout(this.#altTextTooltipTimeout);
this.#altTextTooltipTimeout = null;
}
this.#altTextButton.disabled = !enabled;
}
shown() {
this.#editor._reportTelemetry({
action: "pdfjs.image.alt_text.image_status_label_displayed",
data: {
label: this.#label
}
});
}
destroy() {
this.#altTextButton?.remove();
this.#altTextButton = null;
this.#altTextButtonLabel = null;
this.#altTextTooltip = null;
this.#badge?.remove();
this.#badge = null;
}
async #setState() {
const button = this.#altTextButton;
if (!button) {
return;
}
if (this.#useNewAltTextFlow) {
button.classList.toggle("done", !!this.#altText);
button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]);
this.#altTextButtonLabel?.setAttribute("data-l10n-id", AltText.#l10nNewButton[`${this.#label}-label`]);
if (!this.#altText) {
this.#altTextTooltip?.remove();
return;
}
} else {
if (!this.#altText && !this.#altTextDecorative) {
button.classList.remove("done");
this.#altTextTooltip?.remove();
return;
}
button.classList.add("done");
button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button");
}
let tooltip = this.#altTextTooltip;
if (!tooltip) {
this.#altTextTooltip = tooltip = document.createElement("span");
tooltip.className = "tooltip";
tooltip.setAttribute("role", "tooltip");
tooltip.id = `alt-text-tooltip-${this.#editor.id}`;
const DELAY_TO_SHOW_TOOLTIP = 100;
const signal = this.#editor._uiManager._signal;
signal.addEventListener("abort", () => {
clearTimeout(this.#altTextTooltipTimeout);
this.#altTextTooltipTimeout = null;
}, {
once: true
});
button.addEventListener("mouseenter", () => {
this.#altTextTooltipTimeout = setTimeout(() => {
this.#altTextTooltipTimeout = null;
this.#altTextTooltip.classList.add("show");
this.#editor._reportTelemetry({
action: "alt_text_tooltip"
});
}, DELAY_TO_SHOW_TOOLTIP);
}, {
signal
});
button.addEventListener("mouseleave", () => {
if (this.#altTextTooltipTimeout) {
clearTimeout(this.#altTextTooltipTimeout);
this.#altTextTooltipTimeout = null;
}
this.#altTextTooltip?.classList.remove("show");
}, {
signal
});
}
if (this.#altTextDecorative) {
tooltip.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-decorative-tooltip");
} else {
tooltip.removeAttribute("data-l10n-id");
tooltip.textContent = this.#altText;
}
if (!tooltip.parentNode) {
button.append(tooltip);
}
const element = this.#editor.getImageForAltText();
element?.setAttribute("aria-describedby", tooltip.id);
}
}
// EXTERNAL MODULE: ./src/display/editor/toolbar.js
var toolbar = __webpack_require__(362);
// EXTERNAL MODULE: ./src/display/touch_manager.js
var touch_manager = __webpack_require__(900);
;// ./src/display/editor/editor.js
class AnnotationEditor {
#accessibilityData = null;
#allResizerDivs = null;
#altText = null;
#disabled = false;
#dragPointerId = null;
#dragPointerType = "";
#keepAspectRatio = false;
#resizersDiv = null;
#lastPointerCoords = null;
#savedDimensions = null;
#focusAC = null;
#focusedResizerName = "";
#hasBeenClicked = false;
#initialRect = null;
#isEditing = false;
#isInEditMode = false;
#isResizerEnabledForKeyboard = false;
#moveInDOMTimeout = null;
#prevDragX = 0;
#prevDragY = 0;
#telemetryTimeouts = null;
#touchManager = null;
_editToolbar = null;
_initialOptions = Object.create(null);
_initialData = null;
_isVisible = true;
_uiManager = null;
_focusEventsAllowed = true;
static _l10n = null;
static _l10nResizer = null;
#isDraggable = false;
#zIndex = AnnotationEditor._zIndex++;
doNotMove = false;
static _borderLineWidth = -1;
static _colorManager = new tools.ColorManager();
static _zIndex = 1;
static _telemetryTimeout = 1000;
static get _resizerKeyboardManager() {
const resize = AnnotationEditor.prototype._resizeWithKeyboard;
const small = tools.AnnotationEditorUIManager.TRANSLATE_SMALL;
const big = tools.AnnotationEditorUIManager.TRANSLATE_BIG;
return (0,util.shadow)(this, "_resizerKeyboardManager", new tools.KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], resize, {
args: [-small, 0]
}], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], resize, {
args: [-big, 0]
}], [["ArrowRight", "mac+ArrowRight"], resize, {
args: [small, 0]
}], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], resize, {
args: [big, 0]
}], [["ArrowUp", "mac+ArrowUp"], resize, {
args: [0, -small]
}], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], resize, {
args: [0, -big]
}], [["ArrowDown", "mac+ArrowDown"], resize, {
args: [0, small]
}], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], resize, {
args: [0, big]
}], [["Escape", "mac+Escape"], AnnotationEditor.prototype._stopResizingWithKeyboard]]));
}
constructor(parameters) {
this.parent = parameters.parent;
this.id = parameters.id;
this.width = this.height = null;
this.pageIndex = parameters.parent.pageIndex;
this.name = parameters.name;
this.div = null;
this._uiManager = parameters.uiManager;
this.annotationElementId = null;
this._willKeepAspectRatio = false;
this._initialOptions.isCentered = parameters.isCentered;
this._structTreeParentId = null;
const {
rotation,
rawDims: {
pageWidth,
pageHeight,
pageX,
pageY
}
} = this.parent.viewport;
this.rotation = rotation;
this.pageRotation = (360 + rotation - this._uiManager.viewParameters.rotation) % 360;
this.pageDimensions = [pageWidth, pageHeight];
this.pageTranslation = [pageX, pageY];
const [width, height] = this.parentDimensions;
this.x = parameters.x / width;
this.y = parameters.y / height;
this.isAttachedToDOM = false;
this.deleted = false;
this.eventBus = parameters.eventBus;
}
get editorType() {
return Object.getPrototypeOf(this).constructor._type;
}
static get isDrawer() {
return false;
}
static get _defaultLineColor() {
return (0,util.shadow)(this, "_defaultLineColor", this._colorManager.getHexCode("CanvasText"));
}
static deleteAnnotationElement(editor) {
const fakeEditor = new FakeEditor({
id: editor.parent.getNextId(),
parent: editor.parent,
uiManager: editor._uiManager
});
fakeEditor.annotationElementId = editor.annotationElementId;
fakeEditor.deleted = true;
fakeEditor._uiManager.addToAnnotationStorage(fakeEditor);
}
static initialize(l10n, _uiManager) {
AnnotationEditor._l10n ??= l10n;
AnnotationEditor._l10nResizer ||= Object.freeze({
topLeft: "pdfjs-editor-resizer-top-left",
topMiddle: "pdfjs-editor-resizer-top-middle",
topRight: "pdfjs-editor-resizer-top-right",
middleRight: "pdfjs-editor-resizer-middle-right",
bottomRight: "pdfjs-editor-resizer-bottom-right",
bottomMiddle: "pdfjs-editor-resizer-bottom-middle",
bottomLeft: "pdfjs-editor-resizer-bottom-left",
middleLeft: "pdfjs-editor-resizer-middle-left"
});
if (AnnotationEditor._borderLineWidth !== -1) {
return;
}
const style = getComputedStyle(document.documentElement);
AnnotationEditor._borderLineWidth = parseFloat(style.getPropertyValue("--outline-width")) || 0;
}
static updateDefaultParams(_type, _value) {}
static get defaultPropertiesToUpdate() {
return [];
}
static isHandlingMimeForPasting(mime) {
return false;
}
static paste(item, parent) {
(0,util.unreachable)("Not implemented");
}
get propertiesToUpdate() {
return [];
}
get _isDraggable() {
return this.#isDraggable;
}
set _isDraggable(value) {
this.#isDraggable = value;
this.div?.classList.toggle("draggable", value);
}
get isEnterHandled() {
return true;
}
center() {
const [pageWidth, pageHeight] = this.pageDimensions;
switch (this.parentRotation) {
case 90:
this.x -= this.height * pageHeight / (pageWidth * 2);
this.y += this.width * pageWidth / (pageHeight * 2);
break;
case 180:
this.x += this.width / 2;
this.y += this.height / 2;
break;
case 270:
this.x += this.height * pageHeight / (pageWidth * 2);
this.y -= this.width * pageWidth / (pageHeight * 2);
break;
default:
this.x -= this.width / 2;
this.y -= this.height / 2;
break;
}
this.fixAndSetPosition();
}
addCommands(params) {
this._uiManager.addCommands(params);
}
get currentLayer() {
return this._uiManager.currentLayer;
}
setInBackground() {
this.div.style.zIndex = 0;
}
setInForeground() {
this.div.style.zIndex = this.#zIndex;
}
setParent(parent) {
if (parent !== null) {
this.pageIndex = parent.pageIndex;
this.pageDimensions = parent.pageDimensions;
} else {
this.#stopResizing();
}
this.parent = parent;
}
focusin(event) {
if (!this._focusEventsAllowed) {
return;
}
if (!this.#hasBeenClicked) {
this.parent.setSelected(this);
} else {
this.#hasBeenClicked = false;
}
}
focusout(event) {
if (!this._focusEventsAllowed) {
return;
}
if (!this.isAttachedToDOM) {
return;
}
const target = event.relatedTarget;
if (target?.closest(`#${this.id}`)) {
return;
}
event.preventDefault();
if (!this.parent?.isMultipleSelection) {
this.commitOrRemove();
}
}
commitOrRemove() {
if (this.isEmpty()) {
this.remove();
} else {
this.commit();
}
}
commit() {
this.addToAnnotationStorage();
this._eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "commit",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: this
});
}
addToAnnotationStorage() {
this._uiManager.addToAnnotationStorage(this);
}
setAt(x, y, tx, ty) {
const [width, height] = this.parentDimensions;
[tx, ty] = this.screenToPageTranslation(tx, ty);
if (this.doNotMove) {
[tx, ty] = [0, 0];
}
this.x = (x + tx) / width;
this.y = (y + ty) / height;
this.fixAndSetPosition();
}
#translate([width, height], x, y) {
[x, y] = this.screenToPageTranslation(x, y);
this.x += x / width;
this.y += y / height;
this._onTranslating(this.x, this.y);
this.fixAndSetPosition();
}
translate(x, y) {
this.#translate(this.parentDimensions, x, y);
}
translateInPage(x, y) {
this.#initialRect ||= [this.x, this.y, this.width, this.height];
this.#translate(this.pageDimensions, x, y);
this.div.scrollIntoView({
block: "nearest"
});
}
drag(tx, ty) {
this.#initialRect ||= [this.x, this.y, this.width, this.height];
const {
div,
parentDimensions: [parentWidth, parentHeight]
} = this;
this.x += tx / parentWidth;
this.y += ty / parentHeight;
if (this.parent && (this.x < 0 || this.x > 1 || this.y < 0 || this.y > 1)) {
const {
x,
y
} = this.div.getBoundingClientRect();
if (this.parent.findNewParent(this, x, y)) {
this.x -= Math.floor(this.x);
this.y -= Math.floor(this.y);
}
}
let {
x,
y
} = this;
const [bx, by] = this.getBaseTranslation();
x += bx;
y += by;
const {
style
} = div;
style.left = `${(100 * x).toFixed(2)}%`;
style.top = `${(100 * y).toFixed(2)}%`;
this._onTranslating(x, y);
div.scrollIntoView({
block: "nearest"
});
}
_onTranslating(x, y) {}
_onTranslated(x, y) {}
get _hasBeenMoved() {
return !!this.#initialRect && (this.#initialRect[0] !== this.x || this.#initialRect[1] !== this.y);
}
get _hasBeenResized() {
return !!this.#initialRect && (this.#initialRect[2] !== this.width || this.#initialRect[3] !== this.height);
}
getBaseTranslation() {
const [parentWidth, parentHeight] = this.parentDimensions;
const {
_borderLineWidth
} = AnnotationEditor;
const x = _borderLineWidth / parentWidth;
const y = _borderLineWidth / parentHeight;
switch (this.rotation) {
case 90:
return [-x, y];
case 180:
return [x, y];
case 270:
return [x, -y];
default:
return [-x, -y];
}
}
get _mustFixPosition() {
return true;
}
fixAndSetPosition(rotation = this.rotation) {
const {
div: {
style
},
pageDimensions: [pageWidth, pageHeight]
} = this;
let {
x,
y,
width,
height
} = this;
width *= pageWidth;
height *= pageHeight;
x *= pageWidth;
y *= pageHeight;
if (this._mustFixPosition) {
switch (rotation) {
case 0:
x = Math.max(0, Math.min(pageWidth - width, x));
y = Math.max(0, Math.min(pageHeight - height, y));
break;
case 90:
x = Math.max(0, Math.min(pageWidth - height, x));
y = Math.min(pageHeight, Math.max(width, y));
break;
case 180:
x = Math.min(pageWidth, Math.max(width, x));
y = Math.min(pageHeight, Math.max(height, y));
break;
case 270:
x = Math.min(pageWidth, Math.max(height, x));
y = Math.max(0, Math.min(pageHeight - width, y));
break;
}
}
this.x = x /= pageWidth;
this.y = y /= pageHeight;
const [bx, by] = this.getBaseTranslation();
x += bx;
y += by;
style.left = `${(100 * x).toFixed(2)}%`;
style.top = `${(100 * y).toFixed(2)}%`;
this.moveInDOM();
}
static #rotatePoint(x, y, angle) {
switch (angle) {
case 90:
return [y, -x];
case 180:
return [-x, -y];
case 270:
return [-y, x];
default:
return [x, y];
}
}
screenToPageTranslation(x, y) {
return AnnotationEditor.#rotatePoint(x, y, this.parentRotation);
}
pageTranslationToScreen(x, y) {
return AnnotationEditor.#rotatePoint(x, y, 360 - this.parentRotation);
}
#getRotationMatrix(rotation) {
switch (rotation) {
case 90:
{
const [pageWidth, pageHeight] = this.pageDimensions;
return [0, -pageWidth / pageHeight, pageHeight / pageWidth, 0];
}
case 180:
return [-1, 0, 0, -1];
case 270:
{
const [pageWidth, pageHeight] = this.pageDimensions;
return [0, pageWidth / pageHeight, -pageHeight / pageWidth, 0];
}
default:
return [1, 0, 0, 1];
}
}
get parentScale() {
return this._uiManager.viewParameters.realScale;
}
get parentRotation() {
return (this._uiManager.viewParameters.rotation + this.pageRotation) % 360;
}
get parentDimensions() {
const {
parentScale,
pageDimensions: [pageWidth, pageHeight]
} = this;
return [pageWidth * parentScale, pageHeight * parentScale];
}
setDims(width, height) {
const [parentWidth, parentHeight] = this.parentDimensions;
const {
style
} = this.div;
style.width = `${(100 * width / parentWidth).toFixed(2)}%`;
if (!this.#keepAspectRatio) {
style.height = `${(100 * height / parentHeight).toFixed(2)}%`;
}
}
fixDims() {
const {
style
} = this.div;
const {
height,
width
} = style;
const widthPercent = width.endsWith("%");
const heightPercent = !this.#keepAspectRatio && height.endsWith("%");
if (widthPercent && heightPercent) {
return;
}
const [parentWidth, parentHeight] = this.parentDimensions;
if (!widthPercent) {
style.width = `${(100 * parseFloat(width) / parentWidth).toFixed(2)}%`;
}
if (!this.#keepAspectRatio && !heightPercent) {
style.height = `${(100 * parseFloat(height) / parentHeight).toFixed(2)}%`;
}
}
getInitialTranslation() {
return [0, 0];
}
#createResizers() {
if (this.#resizersDiv) {
return;
}
this.#resizersDiv = document.createElement("div");
this.#resizersDiv.classList.add("resizers");
const classes = this._willKeepAspectRatio ? ["topLeft", "topRight", "bottomRight", "bottomLeft"] : ["topLeft", "topMiddle", "topRight", "middleRight", "bottomRight", "bottomMiddle", "bottomLeft", "middleLeft"];
const signal = this._uiManager._signal;
for (const name of classes) {
const div = document.createElement("div");
this.#resizersDiv.append(div);
div.classList.add("resizer", name);
div.setAttribute("data-resizer-name", name);
div.addEventListener("pointerdown", this.#resizerPointerdown.bind(this, name), {
signal
});
div.addEventListener("contextmenu", display_utils.noContextMenu, {
signal
});
div.tabIndex = -1;
}
this.div.prepend(this.#resizersDiv);
}
#resizerPointerdown(name, event) {
event.preventDefault();
const {
isMac
} = util.FeatureTest.platform;
if (event.button !== 0 || event.ctrlKey && isMac) {
return;
}
this.#altText?.toggle(false);
const savedDraggable = this._isDraggable;
this._isDraggable = false;
this.#lastPointerCoords = [event.screenX, event.screenY];
const ac = new AbortController();
const signal = this._uiManager.combinedSignal(ac);
this.parent.togglePointerEvents(false);
window.addEventListener("pointermove", this.#resizerPointermove.bind(this, name), {
passive: true,
capture: true,
signal
});
window.addEventListener("touchmove", display_utils.stopEvent, {
passive: false,
signal
});
window.addEventListener("contextmenu", display_utils.noContextMenu, {
signal
});
this.#savedDimensions = {
savedX: this.x,
savedY: this.y,
savedWidth: this.width,
savedHeight: this.height
};
const savedParentCursor = this.parent.div.style.cursor;
const savedCursor = this.div.style.cursor;
this.div.style.cursor = this.parent.div.style.cursor = window.getComputedStyle(event.target).cursor;
const pointerUpCallback = () => {
ac.abort();
this.parent.togglePointerEvents(true);
this.#altText?.toggle(true);
this._isDraggable = savedDraggable;
this.parent.div.style.cursor = savedParentCursor;
this.div.style.cursor = savedCursor;
this.#addResizeToUndoStack();
};
window.addEventListener("pointerup", pointerUpCallback, {
signal
});
window.addEventListener("blur", pointerUpCallback, {
signal
});
}
#resize(x, y, width, height) {
this.width = width;
this.height = height;
this.x = x;
this.y = y;
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(parentWidth * width, parentHeight * height);
this.fixAndSetPosition();
this._onResized();
}
_onResized() {}
#addResizeToUndoStack() {
if (!this.#savedDimensions) {
return;
}
const {
savedX,
savedY,
savedWidth,
savedHeight
} = this.#savedDimensions;
this.#savedDimensions = null;
const newX = this.x;
const newY = this.y;
const newWidth = this.width;
const newHeight = this.height;
if (newX === savedX && newY === savedY && newWidth === savedWidth && newHeight === savedHeight) {
return;
}
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "sizeChanged",
editorType: this.constructor.name,
page: this.pageIndex + 1,
value: {
x: savedX,
y: savedY,
width: savedWidth,
height: savedHeight
}
});
this.addCommands({
cmd: this.#resize.bind(this, newX, newY, newWidth, newHeight),
undo: this.#resize.bind(this, savedX, savedY, savedWidth, savedHeight),
mustExec: true
});
}
static _round(x) {
return Math.round(x * 10000) / 10000;
}
#resizerPointermove(name, event) {
const [parentWidth, parentHeight] = this.parentDimensions;
const savedX = this.x;
const savedY = this.y;
const savedWidth = this.width;
const savedHeight = this.height;
const minWidth = AnnotationEditor.MIN_SIZE / parentWidth;
const minHeight = AnnotationEditor.MIN_SIZE / parentHeight;
const rotationMatrix = this.#getRotationMatrix(this.rotation);
const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y];
const invRotationMatrix = this.#getRotationMatrix(360 - this.rotation);
const invTransf = (x, y) => [invRotationMatrix[0] * x + invRotationMatrix[2] * y, invRotationMatrix[1] * x + invRotationMatrix[3] * y];
let getPoint;
let getOpposite;
let isDiagonal = false;
let isHorizontal = false;
switch (name) {
case "topLeft":
isDiagonal = true;
getPoint = (w, h) => [0, 0];
getOpposite = (w, h) => [w, h];
break;
case "topMiddle":
getPoint = (w, h) => [w / 2, 0];
getOpposite = (w, h) => [w / 2, h];
break;
case "topRight":
isDiagonal = true;
getPoint = (w, h) => [w, 0];
getOpposite = (w, h) => [0, h];
break;
case "middleRight":
isHorizontal = true;
getPoint = (w, h) => [w, h / 2];
getOpposite = (w, h) => [0, h / 2];
break;
case "bottomRight":
isDiagonal = true;
getPoint = (w, h) => [w, h];
getOpposite = (w, h) => [0, 0];
break;
case "bottomMiddle":
getPoint = (w, h) => [w / 2, h];
getOpposite = (w, h) => [w / 2, 0];
break;
case "bottomLeft":
isDiagonal = true;
getPoint = (w, h) => [0, h];
getOpposite = (w, h) => [w, 0];
break;
case "middleLeft":
isHorizontal = true;
getPoint = (w, h) => [0, h / 2];
getOpposite = (w, h) => [w, h / 2];
break;
}
const point = getPoint(savedWidth, savedHeight);
const oppositePoint = getOpposite(savedWidth, savedHeight);
let transfOppositePoint = transf(...oppositePoint);
const oppositeX = AnnotationEditor._round(savedX + transfOppositePoint[0]);
const oppositeY = AnnotationEditor._round(savedY + transfOppositePoint[1]);
let ratioX = 1;
let ratioY = 1;
let deltaX, deltaY;
if (!event.fromKeyboard) {
const {
screenX,
screenY
} = event;
const [lastScreenX, lastScreenY] = this.#lastPointerCoords;
[deltaX, deltaY] = this.screenToPageTranslation(screenX - lastScreenX, screenY - lastScreenY);
this.#lastPointerCoords[0] = screenX;
this.#lastPointerCoords[1] = screenY;
} else {
({
deltaX,
deltaY
} = event);
}
[deltaX, deltaY] = invTransf(deltaX / parentWidth, deltaY / parentHeight);
if (isDiagonal) {
const oldDiag = Math.hypot(savedWidth, savedHeight);
ratioX = ratioY = Math.max(Math.min(Math.hypot(oppositePoint[0] - point[0] - deltaX, oppositePoint[1] - point[1] - deltaY) / oldDiag, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight);
} else if (isHorizontal) {
ratioX = Math.max(minWidth, Math.min(1, Math.abs(oppositePoint[0] - point[0] - deltaX))) / savedWidth;
} else {
ratioY = Math.max(minHeight, Math.min(1, Math.abs(oppositePoint[1] - point[1] - deltaY))) / savedHeight;
}
const newWidth = AnnotationEditor._round(savedWidth * ratioX);
const newHeight = AnnotationEditor._round(savedHeight * ratioY);
transfOppositePoint = transf(...getOpposite(newWidth, newHeight));
const newX = oppositeX - transfOppositePoint[0];
const newY = oppositeY - transfOppositePoint[1];
this.#initialRect ||= [this.x, this.y, this.width, this.height];
this.width = newWidth;
this.height = newHeight;
this.x = newX;
this.y = newY;
this.setDims(parentWidth * newWidth, parentHeight * newHeight);
this.fixAndSetPosition();
this._onResizing();
}
_onResizing() {}
altTextFinish() {
this.#altText?.finish();
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "altTextChanged",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: this.#altText
});
}
async addEditToolbar() {
if (this._editToolbar || this.#isInEditMode) {
return this._editToolbar;
}
this._editToolbar = new toolbar.EditorToolbar(this);
this.div.append(this._editToolbar.render());
if (this.#altText) {
await this._editToolbar.addAltText(this.#altText);
}
return this._editToolbar;
}
removeEditToolbar() {
if (!this._editToolbar) {
return;
}
this._editToolbar.remove();
this._editToolbar = null;
this.#altText?.destroy();
}
addContainer(container) {
const editToolbarDiv = this._editToolbar?.div;
if (editToolbarDiv) {
editToolbarDiv.before(container);
} else {
this.div.append(container);
}
}
getClientDimensions() {
return this.div.getBoundingClientRect();
}
async addAltTextButton() {
if (this.#altText) {
return;
}
AltText.initialize(AnnotationEditor._l10n);
this.#altText = new AltText(this);
if (this.#accessibilityData) {
this.#altText.data = this.#accessibilityData;
this.#accessibilityData = null;
}
await this.addEditToolbar();
}
get altTextData() {
return this.#altText?.data;
}
set altTextData(data) {
if (!this.#altText) {
return;
}
this.#altText.data = data;
}
get guessedAltText() {
return this.#altText?.guessedText;
}
async setGuessedAltText(text) {
await this.#altText?.setGuessedText(text);
}
serializeAltText(isForCopying) {
return this.#altText?.serialize(isForCopying);
}
hasAltText() {
return !!this.#altText && !this.#altText.isEmpty();
}
hasAltTextData() {
return this.#altText?.hasData() ?? false;
}
render() {
this.div = document.createElement("div");
this.div.setAttribute("data-editor-rotation", (360 - this.rotation) % 360);
this.div.className = this.name;
this.div.setAttribute("id", this.id);
this.div.tabIndex = this.#disabled ? -1 : 0;
if (!this._isVisible) {
this.div.classList.add("hidden");
}
this.setInForeground();
this.#addFocusListeners();
const [parentWidth, parentHeight] = this.parentDimensions;
if (this.parentRotation % 180 !== 0) {
this.div.style.maxWidth = `${(100 * parentHeight / parentWidth).toFixed(2)}%`;
this.div.style.maxHeight = `${(100 * parentWidth / parentHeight).toFixed(2)}%`;
}
const [tx, ty] = this.getInitialTranslation();
this.translate(tx, ty);
(0,tools.bindEvents)(this, this.div, ["pointerdown"]);
if (this.isResizable && this._uiManager._supportsPinchToZoom) {
this.#touchManager ||= new touch_manager.TouchManager({
container: this.div,
isPinchingDisabled: () => !this.isSelected,
onPinchStart: this.#touchPinchStartCallback.bind(this),
onPinching: this.#touchPinchCallback.bind(this),
onPinchEnd: this.#touchPinchEndCallback.bind(this),
signal: this._uiManager._signal
});
}
this._uiManager._editorUndoBar?.hide();
return this.div;
}
#touchPinchStartCallback() {
this.#savedDimensions = {
savedX: this.x,
savedY: this.y,
savedWidth: this.width,
savedHeight: this.height
};
this.#altText?.toggle(false);
this.parent.togglePointerEvents(false);
}
#touchPinchCallback(_origin, prevDistance, distance) {
const slowDownFactor = 0.7;
let factor = slowDownFactor * (distance / prevDistance) + 1 - slowDownFactor;
if (factor === 1) {
return;
}
const rotationMatrix = this.#getRotationMatrix(this.rotation);
const transf = (x, y) => [rotationMatrix[0] * x + rotationMatrix[2] * y, rotationMatrix[1] * x + rotationMatrix[3] * y];
const [parentWidth, parentHeight] = this.parentDimensions;
const savedX = this.x;
const savedY = this.y;
const savedWidth = this.width;
const savedHeight = this.height;
const minWidth = AnnotationEditor.MIN_SIZE / parentWidth;
const minHeight = AnnotationEditor.MIN_SIZE / parentHeight;
factor = Math.max(Math.min(factor, 1 / savedWidth, 1 / savedHeight), minWidth / savedWidth, minHeight / savedHeight);
const newWidth = AnnotationEditor._round(savedWidth * factor);
const newHeight = AnnotationEditor._round(savedHeight * factor);
if (newWidth === savedWidth && newHeight === savedHeight) {
return;
}
this.#initialRect ||= [savedX, savedY, savedWidth, savedHeight];
const transfCenterPoint = transf(savedWidth / 2, savedHeight / 2);
const centerX = AnnotationEditor._round(savedX + transfCenterPoint[0]);
const centerY = AnnotationEditor._round(savedY + transfCenterPoint[1]);
const newTransfCenterPoint = transf(newWidth / 2, newHeight / 2);
this.x = centerX - newTransfCenterPoint[0];
this.y = centerY - newTransfCenterPoint[1];
this.width = newWidth;
this.height = newHeight;
this.setDims(parentWidth * newWidth, parentHeight * newHeight);
this.fixAndSetPosition();
this._onResizing();
}
#touchPinchEndCallback() {
this.#altText?.toggle(true);
this.parent.togglePointerEvents(true);
this.#addResizeToUndoStack();
}
pointerdown(event) {
const {
isMac
} = util.FeatureTest.platform;
if (event.button !== 0 || event.ctrlKey && isMac) {
event.preventDefault();
return;
}
this.#hasBeenClicked = true;
if (this._isDraggable) {
this.#setUpDragSession(event);
return;
}
this.#selectOnPointerEvent(event);
}
get isSelected() {
return this._uiManager.isSelected(this);
}
#selectOnPointerEvent(event) {
const {
isMac
} = util.FeatureTest.platform;
if (event.ctrlKey && !isMac || event.shiftKey || event.metaKey && isMac) {
this.parent.toggleSelected(this);
} else {
this.parent.setSelected(this);
}
}
#setUpDragSession(event) {
const {
isSelected
} = this;
this._uiManager.setUpDragSession();
let hasDraggingStarted = false;
const ac = new AbortController();
const signal = this._uiManager.combinedSignal(ac);
const opts = {
capture: true,
passive: false,
signal
};
const cancelDrag = e => {
ac.abort();
this.#dragPointerId = null;
this.#hasBeenClicked = false;
if (!this._uiManager.endDragSession()) {
this.#selectOnPointerEvent(e);
}
if (hasDraggingStarted) {
this._onStopDragging();
}
};
if (isSelected) {
this.#prevDragX = event.clientX;
this.#prevDragY = event.clientY;
this.#dragPointerId = event.pointerId;
this.#dragPointerType = event.pointerType;
window.addEventListener("pointermove", e => {
if (!hasDraggingStarted) {
hasDraggingStarted = true;
this._onStartDragging();
}
const {
clientX: x,
clientY: y,
pointerId
} = e;
if (pointerId !== this.#dragPointerId) {
(0,display_utils.stopEvent)(e);
return;
}
const [tx, ty] = this.screenToPageTranslation(x - this.#prevDragX, y - this.#prevDragY);
this.#prevDragX = x;
this.#prevDragY = y;
this._uiManager.dragSelectedEditors(tx, ty);
}, opts);
window.addEventListener("touchmove", display_utils.stopEvent, opts);
window.addEventListener("pointerdown", e => {
if (e.pointerType === this.#dragPointerType) {
if (this.#touchManager || e.isPrimary) {
cancelDrag(e);
}
}
(0,display_utils.stopEvent)(e);
}, opts);
}
const pointerUpCallback = e => {
if (!this.#dragPointerId || this.#dragPointerId === e.pointerId) {
cancelDrag(e);
return;
}
(0,display_utils.stopEvent)(e);
};
window.addEventListener("pointerup", pointerUpCallback, {
signal
});
window.addEventListener("blur", pointerUpCallback, {
signal
});
}
_onStartDragging() {}
_onStopDragging() {}
moveInDOM() {
if (this.#moveInDOMTimeout) {
clearTimeout(this.#moveInDOMTimeout);
}
this.#moveInDOMTimeout = setTimeout(() => {
this.#moveInDOMTimeout = null;
this.parent?.moveEditorInDOM(this);
}, 0);
}
_setParentAndPosition(parent, x, y) {
parent.changeParent(this);
this.x = x;
this.y = y;
this.fixAndSetPosition();
this._onTranslated();
}
getRect(tx, ty, rotation = this.rotation) {
const scale = this.parentScale;
const [pageWidth, pageHeight] = this.pageDimensions;
const [pageX, pageY] = this.pageTranslation;
const shiftX = tx / scale;
const shiftY = ty / scale;
const x = this.x * pageWidth;
const y = this.y * pageHeight;
const width = this.width * pageWidth;
const height = this.height * pageHeight;
switch (rotation) {
case 0:
return [x + shiftX + pageX, pageHeight - y - shiftY - height + pageY, x + shiftX + width + pageX, pageHeight - y - shiftY + pageY];
case 90:
return [x + shiftY + pageX, pageHeight - y + shiftX + pageY, x + shiftY + height + pageX, pageHeight - y + shiftX + width + pageY];
case 180:
return [x - shiftX - width + pageX, pageHeight - y + shiftY + pageY, x - shiftX + pageX, pageHeight - y + shiftY + height + pageY];
case 270:
return [x - shiftY - height + pageX, pageHeight - y - shiftX - width + pageY, x - shiftY + pageX, pageHeight - y - shiftX + pageY];
default:
throw new Error("Invalid rotation");
}
}
getRectInCurrentCoords(rect, pageHeight) {
const [x1, y1, x2, y2] = rect;
const width = x2 - x1;
const height = y2 - y1;
switch (this.rotation) {
case 0:
return [x1, pageHeight - y2, width, height];
case 90:
return [x1, pageHeight - y1, height, width];
case 180:
return [x2, pageHeight - y1, width, height];
case 270:
return [x2, pageHeight - y2, height, width];
default:
throw new Error("Invalid rotation");
}
}
onceAdded(focus) {}
isEmpty() {
return false;
}
enableEditMode() {
this.#isInEditMode = true;
}
disableEditMode() {
this.#isInEditMode = false;
}
isInEditMode() {
return this.#isInEditMode;
}
shouldGetKeyboardEvents() {
return this.#isResizerEnabledForKeyboard;
}
needsToBeRebuilt() {
return this.div && !this.isAttachedToDOM;
}
get isOnScreen() {
const {
top,
left,
bottom,
right
} = this.getClientDimensions();
const {
innerHeight,
innerWidth
} = window;
return left < innerWidth && right > 0 && top < innerHeight && bottom > 0;
}
#addFocusListeners() {
if (this.#focusAC || !this.div) {
return;
}
this.#focusAC = new AbortController();
const signal = this._uiManager.combinedSignal(this.#focusAC);
this.div.addEventListener("focusin", this.focusin.bind(this), {
signal
});
this.div.addEventListener("focusout", this.focusout.bind(this), {
signal
});
}
rebuild() {
this.#addFocusListeners();
}
rotate(_angle) {}
resize() {}
serializeDeleted() {
return {
id: this.annotationElementId,
deleted: true,
pageIndex: this.pageIndex,
popupRef: this._initialData?.popupRef || ""
};
}
serialize(isForCopying = false, context = null) {
(0,util.unreachable)("An editor must be serializable");
}
static async deserialize(data, parent, uiManager) {
const editor = new this.prototype.constructor({
parent,
id: parent.getNextId(),
uiManager,
eventBus: parent.eventBus
});
editor.rotation = data.rotation;
editor.#accessibilityData = data.accessibilityData;
const [pageWidth, pageHeight] = editor.pageDimensions;
const [x, y, width, height] = editor.getRectInCurrentCoords(data.rect, pageHeight);
editor.x = x / pageWidth;
editor.y = y / pageHeight;
editor.width = width / pageWidth;
editor.height = height / pageHeight;
return editor;
}
get hasBeenModified() {
return !!this.annotationElementId && (this.deleted || this.serialize() !== null);
}
remove() {
this.#focusAC?.abort();
this.#focusAC = null;
if (!this.isEmpty()) {
this.commit();
}
if (this.parent) {
this.parent.remove(this);
} else {
this._uiManager.removeEditor(this);
}
if (this.#moveInDOMTimeout) {
clearTimeout(this.#moveInDOMTimeout);
this.#moveInDOMTimeout = null;
}
this.#stopResizing();
this.removeEditToolbar();
if (this.#telemetryTimeouts) {
for (const timeout of this.#telemetryTimeouts.values()) {
clearTimeout(timeout);
}
this.#telemetryTimeouts = null;
}
this.parent = null;
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "removed",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: this
});
this.#touchManager?.destroy();
this.#touchManager = null;
}
get isResizable() {
return false;
}
makeResizable() {
if (this.isResizable) {
this.#createResizers();
this.#resizersDiv.classList.remove("hidden");
(0,tools.bindEvents)(this, this.div, ["keydown"]);
}
}
get toolbarPosition() {
return null;
}
keydown(event) {
if (!this.isResizable || event.target !== this.div || event.key !== "Enter") {
return;
}
this._uiManager.setSelected(this);
this.#savedDimensions = {
savedX: this.x,
savedY: this.y,
savedWidth: this.width,
savedHeight: this.height
};
const children = this.#resizersDiv.children;
if (!this.#allResizerDivs) {
this.#allResizerDivs = Array.from(children);
const boundResizerKeydown = this.#resizerKeydown.bind(this);
const boundResizerBlur = this.#resizerBlur.bind(this);
const signal = this._uiManager._signal;
for (const div of this.#allResizerDivs) {
const name = div.getAttribute("data-resizer-name");
div.setAttribute("role", "spinbutton");
div.addEventListener("keydown", boundResizerKeydown, {
signal
});
div.addEventListener("blur", boundResizerBlur, {
signal
});
div.addEventListener("focus", this.#resizerFocus.bind(this, name), {
signal
});
div.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]);
}
}
const first = this.#allResizerDivs[0];
let firstPosition = 0;
for (const div of children) {
if (div === first) {
break;
}
firstPosition++;
}
const nextFirstPosition = (360 - this.rotation + this.parentRotation) % 360 / 90 * (this.#allResizerDivs.length / 4);
if (nextFirstPosition !== firstPosition) {
if (nextFirstPosition < firstPosition) {
for (let i = 0; i < firstPosition - nextFirstPosition; i++) {
this.#resizersDiv.append(this.#resizersDiv.firstChild);
}
} else if (nextFirstPosition > firstPosition) {
for (let i = 0; i < nextFirstPosition - firstPosition; i++) {
this.#resizersDiv.firstChild.before(this.#resizersDiv.lastChild);
}
}
let i = 0;
for (const child of children) {
const div = this.#allResizerDivs[i++];
const name = div.getAttribute("data-resizer-name");
child.setAttribute("data-l10n-id", AnnotationEditor._l10nResizer[name]);
}
}
this.#setResizerTabIndex(0);
this.#isResizerEnabledForKeyboard = true;
this.#resizersDiv.firstChild.focus({
focusVisible: true
});
event.preventDefault();
event.stopImmediatePropagation();
}
#resizerKeydown(event) {
AnnotationEditor._resizerKeyboardManager.exec(this, event);
}
#resizerBlur(event) {
if (this.#isResizerEnabledForKeyboard && event.relatedTarget?.parentNode !== this.#resizersDiv) {
this.#stopResizing();
}
}
#resizerFocus(name) {
this.#focusedResizerName = this.#isResizerEnabledForKeyboard ? name : "";
}
#setResizerTabIndex(value) {
if (!this.#allResizerDivs) {
return;
}
for (const div of this.#allResizerDivs) {
div.tabIndex = value;
}
}
_resizeWithKeyboard(x, y) {
if (!this.#isResizerEnabledForKeyboard) {
return;
}
this.#resizerPointermove(this.#focusedResizerName, {
deltaX: x,
deltaY: y,
fromKeyboard: true
});
}
#stopResizing() {
this.#isResizerEnabledForKeyboard = false;
this.#setResizerTabIndex(-1);
this.#addResizeToUndoStack();
}
_stopResizingWithKeyboard() {
this.#stopResizing();
this.div.focus();
}
select() {
this.makeResizable();
this.div?.classList.add("selectedEditor");
if (!this._editToolbar) {
this.addEditToolbar().then(() => {
if (this.div?.classList.contains("selectedEditor")) {
this._editToolbar?.show();
}
});
return;
}
this._editToolbar?.show();
this.#altText?.toggleAltTextBadge(false);
}
unselect() {
this.#resizersDiv?.classList.add("hidden");
this.div?.classList.remove("selectedEditor");
if (this.div?.contains(document.activeElement)) {
this._uiManager.currentLayer.div.focus({
preventScroll: true
});
}
this._editToolbar?.hide();
this.#altText?.toggleAltTextBadge(true);
}
updateParams(type, value) {}
disableEditing() {}
enableEditing() {}
enterInEditMode() {}
getImageForAltText() {
return null;
}
get contentDiv() {
return this.div;
}
get isEditing() {
return this.#isEditing;
}
set isEditing(value) {
this.#isEditing = value;
if (!this.parent) {
return;
}
if (value) {
this.parent.setSelected(this);
this.parent.setActiveEditor(this);
} else {
this.parent.setActiveEditor(null);
}
}
setAspectRatio(width, height) {
this.#keepAspectRatio = true;
const aspectRatio = width / height;
const {
style
} = this.div;
style.aspectRatio = aspectRatio;
style.height = "auto";
}
static get MIN_SIZE() {
return 16;
}
static canCreateNewEmptyEditor() {
return true;
}
get telemetryInitialData() {
return {
action: "added"
};
}
get telemetryFinalData() {
return null;
}
_reportTelemetry(data, mustWait = false) {
if (mustWait) {
this.#telemetryTimeouts ||= new Map();
const {
action
} = data;
let timeout = this.#telemetryTimeouts.get(action);
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
this._reportTelemetry(data);
this.#telemetryTimeouts.delete(action);
if (this.#telemetryTimeouts.size === 0) {
this.#telemetryTimeouts = null;
}
}, AnnotationEditor._telemetryTimeout);
this.#telemetryTimeouts.set(action, timeout);
return;
}
data.type ||= this.editorType;
this._uiManager._eventBus.dispatch("reporttelemetry", {
source: this,
details: {
type: "editing",
data
}
});
}
show(visible = this._isVisible) {
this.div.classList.toggle("hidden", !visible);
this._isVisible = visible;
}
enable() {
if (this.div) {
this.div.tabIndex = 0;
}
this.#disabled = false;
}
disable() {
if (this.div) {
this.div.tabIndex = -1;
}
this.#disabled = true;
}
renderAnnotationElement(annotation) {
let content = annotation.container.querySelector(".annotationContent");
if (!content) {
content = document.createElement("div");
content.classList.add("annotationContent", this.editorType);
annotation.container.prepend(content);
} else if (content.nodeName === "CANVAS") {
const canvas = content;
content = document.createElement("div");
content.classList.add("annotationContent", this.editorType);
canvas.before(content);
}
return content;
}
resetAnnotationElement(annotation) {
const {
firstChild
} = annotation.container;
if (firstChild?.nodeName === "DIV" && firstChild.classList.contains("annotationContent")) {
firstChild.remove();
}
}
}
class FakeEditor extends AnnotationEditor {
constructor(params) {
super(params);
this.annotationElementId = params.annotationElementId;
this.deleted = true;
}
serialize() {
return this.serializeDeleted();
}
}
/***/ }),
/***/ 362:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ EditorToolbar: () => (/* binding */ EditorToolbar),
/* harmony export */ HighlightToolbar: () => (/* binding */ HighlightToolbar)
/* harmony export */ });
/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(419);
class EditorToolbar {
#toolbar = null;
#colorPicker = null;
#editor;
#buttons = null;
#altText = null;
static #l10nRemove = null;
constructor(editor) {
this.#editor = editor;
EditorToolbar.#l10nRemove ||= Object.freeze({
freetext: "pdfjs-editor-remove-freetext-button",
highlight: "pdfjs-editor-remove-highlight-button",
ink: "pdfjs-editor-remove-ink-button",
stamp: "pdfjs-editor-remove-stamp-button"
});
}
render() {
const editToolbar = this.#toolbar = document.createElement("div");
editToolbar.classList.add("editToolbar", "hidden");
editToolbar.setAttribute("role", "toolbar");
const signal = this.#editor._uiManager._signal;
editToolbar.addEventListener("contextmenu", _display_utils_js__WEBPACK_IMPORTED_MODULE_0__.noContextMenu, {
signal
});
editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown, {
signal
});
const buttons = this.#buttons = document.createElement("div");
buttons.className = "buttons";
editToolbar.append(buttons);
const position = this.#editor.toolbarPosition;
if (position) {
const {
style
} = editToolbar;
const x = this.#editor._uiManager.direction === "ltr" ? 1 - position[0] : position[0];
style.insetInlineEnd = `${100 * x}%`;
style.top = `calc(${100 * position[1]}% + var(--editor-toolbar-vert-offset))`;
}
this.#addDeleteButton();
return editToolbar;
}
get div() {
return this.#toolbar;
}
static #pointerDown(e) {
e.stopPropagation();
}
#focusIn(e) {
this.#editor._focusEventsAllowed = false;
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_0__.stopEvent)(e);
}
#focusOut(e) {
this.#editor._focusEventsAllowed = true;
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_0__.stopEvent)(e);
}
#addListenersToElement(element) {
const signal = this.#editor._uiManager._signal;
element.addEventListener("focusin", this.#focusIn.bind(this), {
capture: true,
signal
});
element.addEventListener("focusout", this.#focusOut.bind(this), {
capture: true,
signal
});
element.addEventListener("contextmenu", _display_utils_js__WEBPACK_IMPORTED_MODULE_0__.noContextMenu, {
signal
});
}
hide() {
this.#toolbar.classList.add("hidden");
this.#colorPicker?.hideDropdown();
}
show() {
this.#toolbar.classList.remove("hidden");
this.#altText?.shown();
}
#addDeleteButton() {
const {
editorType,
_uiManager
} = this.#editor;
const button = document.createElement("button");
button.className = "delete";
button.tabIndex = 0;
button.setAttribute("data-l10n-id", EditorToolbar.#l10nRemove[editorType]);
this.#addListenersToElement(button);
button.addEventListener("click", e => {
_uiManager.delete();
}, {
signal: _uiManager._signal
});
this.#buttons.append(button);
}
get #divider() {
const divider = document.createElement("div");
divider.className = "divider";
return divider;
}
async addAltText(altText) {
const button = await altText.render();
this.#addListenersToElement(button);
this.#buttons.prepend(button, this.#divider);
this.#altText = altText;
}
addColorPicker(colorPicker) {
this.#colorPicker = colorPicker;
const button = colorPicker.renderButton();
this.#addListenersToElement(button);
this.#buttons.prepend(button, this.#divider);
}
remove() {
this.#toolbar.remove();
this.#colorPicker?.destroy();
this.#colorPicker = null;
}
}
class HighlightToolbar {
#buttons = null;
#toolbar = null;
#uiManager;
constructor(uiManager) {
this.#uiManager = uiManager;
}
#render() {
const editToolbar = this.#toolbar = document.createElement("div");
editToolbar.className = "editToolbar";
editToolbar.setAttribute("role", "toolbar");
editToolbar.addEventListener("contextmenu", _display_utils_js__WEBPACK_IMPORTED_MODULE_0__.noContextMenu, {
signal: this.#uiManager._signal
});
const buttons = this.#buttons = document.createElement("div");
buttons.className = "buttons";
editToolbar.append(buttons);
this.#addHighlightButton();
return editToolbar;
}
#getLastPoint(boxes, isLTR) {
let lastY = 0;
let lastX = 0;
for (const box of boxes) {
const y = box.y + box.height;
if (y < lastY) {
continue;
}
const x = box.x + (isLTR ? box.width : 0);
if (y > lastY) {
lastX = x;
lastY = y;
continue;
}
if (isLTR) {
if (x > lastX) {
lastX = x;
}
} else if (x < lastX) {
lastX = x;
}
}
return [isLTR ? 1 - lastX : lastX, lastY];
}
show(parent, boxes, isLTR) {
const [x, y] = this.#getLastPoint(boxes, isLTR);
const {
style
} = this.#toolbar ||= this.#render();
parent.append(this.#toolbar);
style.insetInlineEnd = `${100 * x}%`;
style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`;
}
hide() {
this.#toolbar.remove();
}
#addHighlightButton() {
const button = document.createElement("button");
button.className = "highlightButton";
button.tabIndex = 0;
button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button1`);
const span = document.createElement("span");
button.append(span);
span.className = "visuallyHidden";
span.setAttribute("data-l10n-id", "pdfjs-highlight-floating-button-label");
const signal = this.#uiManager._signal;
button.addEventListener("contextmenu", _display_utils_js__WEBPACK_IMPORTED_MODULE_0__.noContextMenu, {
signal
});
button.addEventListener("click", () => {
this.#uiManager.highlightSelection("floating_button");
}, {
signal
});
this.#buttons.append(button);
}
}
/***/ }),
/***/ 830:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AnnotationEditorUIManager: () => (/* binding */ AnnotationEditorUIManager),
/* harmony export */ ColorManager: () => (/* binding */ ColorManager),
/* harmony export */ KeyboardManager: () => (/* binding */ KeyboardManager),
/* harmony export */ bindEvents: () => (/* binding */ bindEvents)
/* harmony export */ });
/* unused harmony exports CommandManager, opacityToHex */
/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(292);
/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(419);
/* harmony import */ var _toolbar_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(362);
function bindEvents(obj, element, names) {
for (const name of names) {
element.addEventListener(name, obj[name].bind(obj));
}
}
function opacityToHex(opacity) {
return Math.round(Math.min(255, Math.max(1, 255 * opacity))).toString(16).padStart(2, "0");
}
class IdManager {
#id = 0;
get id() {
return `${_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorPrefix}${this.#id++}`;
}
}
class ImageManager {
#baseId = (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.getUuid)();
#id = 0;
#cache = null;
static get _isSVGFittingCanvas() {
const svg = `data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 1 1" width="1" height="1" xmlns="http://www.w3.org/2000/svg"><rect width="1" height="1" style="fill:red;"/></svg>`;
const canvas = new OffscreenCanvas(1, 3);
const ctx = canvas.getContext("2d", {
willReadFrequently: true
});
const image = new Image();
image.src = svg;
const promise = image.decode().then(() => {
ctx.drawImage(image, 0, 0, 1, 1, 0, 0, 1, 3);
return new Uint32Array(ctx.getImageData(0, 0, 1, 1).data.buffer)[0] === 0;
});
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "_isSVGFittingCanvas", promise);
}
async #get(key, rawData) {
this.#cache ||= new Map();
let data = this.#cache.get(key);
if (data === null) {
return null;
}
if (data?.bitmap) {
data.refCounter += 1;
return data;
}
try {
data ||= {
bitmap: null,
id: `image_${this.#baseId}_${this.#id++}`,
refCounter: 0,
isSvg: false
};
let image;
if (typeof rawData === "string") {
data.url = rawData;
image = await (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.fetchData)(rawData, "blob");
} else if (rawData instanceof File) {
image = data.file = rawData;
} else if (rawData instanceof Blob) {
image = rawData;
}
if (image.type === "image/svg+xml") {
const mustRemoveAspectRatioPromise = ImageManager._isSVGFittingCanvas;
const fileReader = new FileReader();
const imageElement = new Image();
const imagePromise = new Promise((resolve, reject) => {
imageElement.onload = () => {
data.bitmap = imageElement;
data.isSvg = true;
resolve();
};
fileReader.onload = async () => {
const url = data.svgUrl = fileReader.result;
imageElement.src = (await mustRemoveAspectRatioPromise) ? `${url}#svgView(preserveAspectRatio(none))` : url;
};
imageElement.onerror = fileReader.onerror = reject;
});
fileReader.readAsDataURL(image);
await imagePromise;
} else {
data.bitmap = await createImageBitmap(image);
}
data.refCounter = 1;
} catch (e) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(e);
data = null;
}
this.#cache.set(key, data);
if (data) {
this.#cache.set(data.id, data);
}
return data;
}
async getFromFile(file) {
const {
lastModified,
name,
size,
type
} = file;
return this.#get(`${lastModified}_${name}_${size}_${type}`, file);
}
async getFromUrl(url) {
return this.#get(url, url);
}
async getFromBlob(id, blobPromise) {
const blob = await blobPromise;
return this.#get(id, blob);
}
async getFromId(id) {
this.#cache ||= new Map();
const data = this.#cache.get(id);
if (!data) {
return null;
}
if (data.bitmap) {
data.refCounter += 1;
return data;
}
if (data.file) {
return this.getFromFile(data.file);
}
if (data.blobPromise) {
const {
blobPromise
} = data;
delete data.blobPromise;
return this.getFromBlob(data.id, blobPromise);
}
return this.getFromUrl(data.url);
}
getFromCanvas(id, canvas) {
this.#cache ||= new Map();
let data = this.#cache.get(id);
if (data?.bitmap) {
data.refCounter += 1;
return data;
}
const offscreen = new OffscreenCanvas(canvas.width, canvas.height);
const ctx = offscreen.getContext("2d");
ctx.drawImage(canvas, 0, 0);
data = {
bitmap: offscreen.transferToImageBitmap(),
id: `image_${this.#baseId}_${this.#id++}`,
refCounter: 1,
isSvg: false
};
this.#cache.set(id, data);
this.#cache.set(data.id, data);
return data;
}
getSvgUrl(id) {
const data = this.#cache.get(id);
if (!data?.isSvg) {
return null;
}
return data.svgUrl;
}
deleteId(id) {
this.#cache ||= new Map();
const data = this.#cache.get(id);
if (!data) {
return;
}
data.refCounter -= 1;
if (data.refCounter !== 0) {
return;
}
const {
bitmap
} = data;
if (!data.url && !data.file) {
const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
const ctx = canvas.getContext("bitmaprenderer");
ctx.transferFromImageBitmap(bitmap);
data.blobPromise = canvas.convertToBlob();
}
bitmap.close?.();
data.bitmap = null;
}
isValidId(id) {
return id.startsWith(`image_${this.#baseId}_`);
}
}
class CommandManager {
#commands = [];
#locked = false;
#maxSize;
#position = -1;
constructor(maxSize = 128) {
this.#maxSize = maxSize;
}
add({
cmd,
undo,
post,
mustExec,
type = NaN,
overwriteIfSameType = false,
keepUndo = false
}) {
if (mustExec) {
cmd();
}
if (this.#locked) {
return;
}
const save = {
cmd,
undo,
post,
type
};
if (this.#position === -1) {
if (this.#commands.length > 0) {
this.#commands.length = 0;
}
this.#position = 0;
this.#commands.push(save);
return;
}
if (overwriteIfSameType && this.#commands[this.#position].type === type) {
if (keepUndo) {
save.undo = this.#commands[this.#position].undo;
}
this.#commands[this.#position] = save;
return;
}
const next = this.#position + 1;
if (next === this.#maxSize) {
this.#commands.splice(0, 1);
} else {
this.#position = next;
if (next < this.#commands.length) {
this.#commands.splice(next);
}
}
this.#commands.push(save);
}
undo() {
if (this.#position === -1) {
return;
}
this.#locked = true;
const {
undo,
post
} = this.#commands[this.#position];
undo();
post?.();
this.#locked = false;
this.#position -= 1;
}
redo() {
if (this.#position < this.#commands.length - 1) {
this.#position += 1;
this.#locked = true;
const {
cmd,
post
} = this.#commands[this.#position];
cmd();
post?.();
this.#locked = false;
}
}
hasSomethingToUndo() {
return this.#position !== -1;
}
hasSomethingToRedo() {
return this.#position < this.#commands.length - 1;
}
cleanType(type) {
if (this.#position === -1) {
return;
}
for (let i = this.#position; i >= 0; i--) {
if (this.#commands[i].type !== type) {
this.#commands.splice(i + 1, this.#position - i);
this.#position = i;
return;
}
}
this.#commands.length = 0;
this.#position = -1;
}
destroy() {
this.#commands = null;
}
reset() {
this.#commands = [];
this.#position = -1;
this.#locked = false;
}
}
class KeyboardManager {
constructor(callbacks) {
this.buffer = [];
this.callbacks = new Map();
this.allKeys = new Set();
const {
isMac
} = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest.platform;
for (const [keys, callback, options = {}] of callbacks) {
for (const key of keys) {
const isMacKey = key.startsWith("mac+");
if (isMac && isMacKey) {
this.callbacks.set(key.slice(4), {
callback,
options
});
this.allKeys.add(key.split("+").at(-1));
} else if (!isMac && !isMacKey) {
this.callbacks.set(key, {
callback,
options
});
this.allKeys.add(key.split("+").at(-1));
}
}
}
}
#serialize(event) {
if (event.altKey) {
this.buffer.push("alt");
}
if (event.ctrlKey) {
this.buffer.push("ctrl");
}
if (event.metaKey) {
this.buffer.push("meta");
}
if (event.shiftKey) {
this.buffer.push("shift");
}
this.buffer.push(event.key);
const str = this.buffer.join("+");
this.buffer.length = 0;
return str;
}
exec(self, event) {
if (!this.allKeys.has(event.key)) {
return;
}
const info = this.callbacks.get(this.#serialize(event));
if (!info) {
return;
}
const {
callback,
options: {
bubbles = false,
args = [],
checker = null
}
} = info;
if (checker && !checker(self, event)) {
return;
}
callback.bind(self, ...args, event)();
if (!bubbles) {
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.stopEvent)(event);
}
}
}
class ColorManager {
static _colorsMapping = new Map([["CanvasText", [0, 0, 0]], ["Canvas", [255, 255, 255]]]);
get _colors() {
const colors = new Map([["CanvasText", null], ["Canvas", null]]);
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.getColorValues)(colors);
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "_colors", colors);
}
convert(color) {
const rgb = (0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.getRGB)(color);
if (!window.matchMedia("(forced-colors: active)").matches) {
return rgb;
}
for (const [name, RGB] of this._colors) {
if (RGB.every((x, i) => x === rgb[i])) {
return ColorManager._colorsMapping.get(name);
}
}
return rgb;
}
getHexCode(name) {
const rgb = this._colors.get(name);
if (!rgb) {
return name;
}
return _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.makeHexColor(...rgb);
}
}
class AnnotationEditorUIManager {
#abortController = new AbortController();
#activeEditor = null;
#allEditors = new Map();
#allLayers = new Map();
#altTextManager = null;
#annotationStorage = null;
#changedExistingAnnotations = null;
#commandManager = new CommandManager();
#copyPasteAC = null;
#currentDrawingSession = null;
#currentPageIndex = 0;
#deletedAnnotationsElementIds = new Set();
#draggingEditors = null;
#editorTypes = null;
#editorsToRescale = new Set();
_editorUndoBar = null;
#enableHighlightFloatingButton = false;
#enableUpdatedAddImage = false;
#enableNewAltTextWhenAddingImage = false;
#filterFactory = null;
#focusMainContainerTimeoutId = null;
#focusManagerAC = null;
#highlightColors = null;
#highlightWhenShiftUp = false;
#highlightToolbar = null;
#idManager = new IdManager();
#isEnabled = false;
#isWaiting = false;
#keyboardManagerAC = null;
#lastActiveElement = null;
#mainHighlightColorPicker = null;
#mlManager = null;
#mode = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE;
#selectedEditors = new Set();
#selectedTextNode = null;
#pageColors = null;
#showAllStates = null;
#previousStates = {
isEditing: false,
isEmpty: true,
hasSomethingToUndo: false,
hasSomethingToRedo: false,
hasSelectedEditor: false,
hasSelectedText: false
};
#translation = [0, 0];
#translationTimeoutId = null;
#container = null;
#viewer = null;
#updateModeCapability = null;
static TRANSLATE_SMALL = 1;
static TRANSLATE_BIG = 10;
static get _keyboardManager() {
const proto = AnnotationEditorUIManager.prototype;
const arrowChecker = self => self.#container.contains(document.activeElement) && document.activeElement.tagName !== "BUTTON" && self.hasSomethingToControl();
const textInputChecker = (_self, {
target: el
}) => {
if (el instanceof HTMLInputElement) {
const {
type
} = el;
return type !== "text" && type !== "number";
}
return true;
};
const small = this.TRANSLATE_SMALL;
const big = this.TRANSLATE_BIG;
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "_keyboardManager", new KeyboardManager([[["ctrl+a", "mac+meta+a"], proto.selectAll, {
checker: textInputChecker
}], [["ctrl+z", "mac+meta+z"], proto.undo, {
checker: textInputChecker
}], [["ctrl+y", "ctrl+shift+z", "mac+meta+shift+z", "ctrl+shift+Z", "mac+meta+shift+Z"], proto.redo, {
checker: textInputChecker
}], [["Backspace", "alt+Backspace", "ctrl+Backspace", "shift+Backspace", "mac+Backspace", "mac+alt+Backspace", "mac+ctrl+Backspace", "Delete", "ctrl+Delete", "shift+Delete", "mac+Delete"], proto.delete, {
checker: textInputChecker
}], [["Enter", "mac+Enter"], proto.addNewEditorFromKeyboard, {
checker: (self, {
target: el
}) => !(el instanceof HTMLButtonElement) && self.#container.contains(el) && !self.isEnterHandled
}], [[" ", "mac+ "], proto.addNewEditorFromKeyboard, {
checker: (self, {
target: el
}) => !(el instanceof HTMLButtonElement) && self.#container.contains(document.activeElement)
}], [["Escape", "mac+Escape"], proto.unselectAll], [["ArrowLeft", "mac+ArrowLeft"], proto.translateSelectedEditors, {
args: [-small, 0],
checker: arrowChecker
}], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto.translateSelectedEditors, {
args: [-big, 0],
checker: arrowChecker
}], [["ArrowRight", "mac+ArrowRight"], proto.translateSelectedEditors, {
args: [small, 0],
checker: arrowChecker
}], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto.translateSelectedEditors, {
args: [big, 0],
checker: arrowChecker
}], [["ArrowUp", "mac+ArrowUp"], proto.translateSelectedEditors, {
args: [0, -small],
checker: arrowChecker
}], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto.translateSelectedEditors, {
args: [0, -big],
checker: arrowChecker
}], [["ArrowDown", "mac+ArrowDown"], proto.translateSelectedEditors, {
args: [0, small],
checker: arrowChecker
}], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto.translateSelectedEditors, {
args: [0, big],
checker: arrowChecker
}]]));
}
constructor(container, viewer, altTextManager, eventBus, pdfDocument, pageColors, highlightColors, enableHighlightFloatingButton, enableUpdatedAddImage, enableNewAltTextWhenAddingImage, mlManager, editorUndoBar, supportsPinchToZoom) {
const signal = this._signal = this.#abortController.signal;
this.#container = container;
this.#viewer = viewer;
this.#altTextManager = altTextManager;
this._eventBus = eventBus;
eventBus._on("editingaction", this.onEditingAction.bind(this), {
signal
});
eventBus._on("pagechanging", this.onPageChanging.bind(this), {
signal
});
eventBus._on("scalechanging", this.onScaleChanging.bind(this), {
signal
});
eventBus._on("rotationchanging", this.onRotationChanging.bind(this), {
signal
});
eventBus._on("setpreference", this.onSetPreference.bind(this), {
signal
});
eventBus._on("switchannotationeditorparams", evt => this.updateParams(evt.type, evt.value), {
signal
});
this.#addSelectionListener();
this.#addDragAndDropListeners();
this.#addKeyboardManager();
this.#annotationStorage = pdfDocument.annotationStorage;
this.#filterFactory = pdfDocument.filterFactory;
this.#pageColors = pageColors;
this.#highlightColors = highlightColors || null;
this.#enableHighlightFloatingButton = enableHighlightFloatingButton;
this.#enableUpdatedAddImage = enableUpdatedAddImage;
this.#enableNewAltTextWhenAddingImage = enableNewAltTextWhenAddingImage;
this.#mlManager = mlManager || null;
this.viewParameters = {
realScale: _display_utils_js__WEBPACK_IMPORTED_MODULE_1__.PixelsPerInch.PDF_TO_CSS_UNITS,
rotation: 0
};
this.isShiftKeyDown = false;
this._editorUndoBar = editorUndoBar || null;
this._supportsPinchToZoom = supportsPinchToZoom !== false;
}
destroy() {
this.#updateModeCapability?.resolve();
this.#updateModeCapability = null;
this.#abortController?.abort();
this.#abortController = null;
this._signal = null;
for (const layer of this.#allLayers.values()) {
layer.destroy();
}
this.#allLayers.clear();
this.#allEditors.clear();
this.#editorsToRescale.clear();
this.#activeEditor = null;
this.#selectedEditors.clear();
this.#commandManager.destroy();
this.#altTextManager?.destroy();
this.#highlightToolbar?.hide();
this.#highlightToolbar = null;
if (this.#focusMainContainerTimeoutId) {
clearTimeout(this.#focusMainContainerTimeoutId);
this.#focusMainContainerTimeoutId = null;
}
if (this.#translationTimeoutId) {
clearTimeout(this.#translationTimeoutId);
this.#translationTimeoutId = null;
}
this._editorUndoBar?.destroy();
}
combinedSignal(ac) {
return AbortSignal.any([this._signal, ac.signal]);
}
get mlManager() {
return this.#mlManager;
}
get useNewAltTextFlow() {
return this.#enableUpdatedAddImage;
}
get useNewAltTextWhenAddingImage() {
return this.#enableNewAltTextWhenAddingImage;
}
get hcmFilter() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "hcmFilter", this.#pageColors ? this.#filterFactory.addHCMFilter(this.#pageColors.foreground, this.#pageColors.background) : "none");
}
get direction() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "direction", getComputedStyle(this.#container).direction);
}
get highlightColors() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "highlightColors", this.#highlightColors ? new Map(this.#highlightColors.split(",").map(pair => pair.split("=").map(x => x.trim()))) : null);
}
get highlightColorNames() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "highlightColorNames", this.highlightColors ? new Map(Array.from(this.highlightColors, e => e.reverse())) : null);
}
setCurrentDrawingSession(layer) {
if (layer) {
this.unselectAll();
this.disableUserSelect(true);
} else {
this.disableUserSelect(false);
}
this.#currentDrawingSession = layer;
}
setMainHighlightColorPicker(colorPicker) {
this.#mainHighlightColorPicker = colorPicker;
}
editAltText(editor, firstTime = false) {
this.#altTextManager?.editAltText(this, editor, firstTime);
}
switchToMode(mode, callback) {
this._eventBus.on("annotationeditormodechanged", callback, {
once: true,
signal: this._signal
});
this._eventBus.dispatch("showannotationeditorui", {
source: this,
mode
});
}
setPreference(name, value) {
this._eventBus.dispatch("setpreference", {
source: this,
name,
value
});
}
onSetPreference({
name,
value
}) {
switch (name) {
case "enableNewAltTextWhenAddingImage":
this.#enableNewAltTextWhenAddingImage = value;
break;
}
}
onPageChanging({
pageNumber
}) {
this.#currentPageIndex = pageNumber - 1;
}
focusMainContainer() {
this.#container.focus();
}
findParent(x, y) {
for (const layer of this.#allLayers.values()) {
const {
x: layerX,
y: layerY,
width,
height
} = layer.div.getBoundingClientRect();
if (x >= layerX && x <= layerX + width && y >= layerY && y <= layerY + height) {
return layer;
}
}
return null;
}
disableUserSelect(value = false) {
this.#viewer.classList.toggle("noUserSelect", value);
}
addShouldRescale(editor) {
this.#editorsToRescale.add(editor);
}
removeShouldRescale(editor) {
this.#editorsToRescale.delete(editor);
}
onScaleChanging({
scale
}) {
this.commitOrRemove();
this.viewParameters.realScale = scale * _display_utils_js__WEBPACK_IMPORTED_MODULE_1__.PixelsPerInch.PDF_TO_CSS_UNITS;
for (const editor of this.#editorsToRescale) {
editor.onScaleChanging();
}
this.#currentDrawingSession?.onScaleChanging();
}
onRotationChanging({
pagesRotation
}) {
this.commitOrRemove();
this.viewParameters.rotation = pagesRotation;
}
#getAnchorElementForSelection({
anchorNode
}) {
return anchorNode.nodeType === Node.TEXT_NODE ? anchorNode.parentElement : anchorNode;
}
#getLayerForTextLayer(textLayer) {
const {
currentLayer
} = this;
if (currentLayer.hasTextLayer(textLayer)) {
return currentLayer;
}
for (const layer of this.#allLayers.values()) {
if (layer.hasTextLayer(textLayer)) {
return layer;
}
}
return null;
}
highlightSelection(methodOfCreation = "") {
const selection = document.getSelection();
if (!selection || selection.isCollapsed) {
return;
}
const {
anchorNode,
anchorOffset,
focusNode,
focusOffset
} = selection;
const text = selection.toString();
const anchorElement = this.#getAnchorElementForSelection(selection);
const textLayer = anchorElement.closest(".textLayer");
const boxes = this.getSelectionBoxes(textLayer);
if (!boxes) {
return;
}
selection.empty();
const layer = this.#getLayerForTextLayer(textLayer);
const isNoneMode = this.#mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE;
const callback = () => {
layer?.createAndAddNewEditor({
x: 0,
y: 0
}, false, {
methodOfCreation,
boxes,
anchorNode,
anchorOffset,
focusNode,
focusOffset,
text
});
if (isNoneMode) {
this.showAllEditors("highlight", true, true);
}
};
if (isNoneMode) {
this.switchToMode(_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.HIGHLIGHT, callback);
return;
}
callback();
}
#displayHighlightToolbar() {
const selection = document.getSelection();
if (!selection || selection.isCollapsed) {
return;
}
const anchorElement = this.#getAnchorElementForSelection(selection);
const textLayer = anchorElement.closest(".textLayer");
const boxes = this.getSelectionBoxes(textLayer);
if (!boxes) {
return;
}
this.#highlightToolbar ||= new _toolbar_js__WEBPACK_IMPORTED_MODULE_2__.HighlightToolbar(this);
this.#highlightToolbar.show(textLayer, boxes, this.direction === "ltr");
}
addToAnnotationStorage(editor) {
if (!editor.isEmpty() && this.#annotationStorage && !this.#annotationStorage.has(editor.id)) {
this.#annotationStorage.setValue(editor.id, editor);
}
}
#selectionChange() {
const selection = document.getSelection();
if (!selection || selection.isCollapsed) {
if (this.#selectedTextNode) {
this.#highlightToolbar?.hide();
this.#selectedTextNode = null;
this.#dispatchUpdateStates({
hasSelectedText: false
});
}
return;
}
const {
anchorNode
} = selection;
if (anchorNode === this.#selectedTextNode) {
return;
}
const anchorElement = this.#getAnchorElementForSelection(selection);
const textLayer = anchorElement.closest(".textLayer");
if (!textLayer) {
if (this.#selectedTextNode) {
this.#highlightToolbar?.hide();
this.#selectedTextNode = null;
this.#dispatchUpdateStates({
hasSelectedText: false
});
}
return;
}
this.#highlightToolbar?.hide();
this.#selectedTextNode = anchorNode;
this.#dispatchUpdateStates({
hasSelectedText: true
});
if (this.#mode !== _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.HIGHLIGHT && this.#mode !== _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE) {
return;
}
if (this.#mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.HIGHLIGHT) {
this.showAllEditors("highlight", true, true);
}
this.#highlightWhenShiftUp = this.isShiftKeyDown;
if (!this.isShiftKeyDown) {
const activeLayer = this.#mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.HIGHLIGHT ? this.#getLayerForTextLayer(textLayer) : null;
activeLayer?.toggleDrawing();
const ac = new AbortController();
const signal = this.combinedSignal(ac);
const pointerup = e => {
if (e.type === "pointerup" && e.button !== 0) {
return;
}
ac.abort();
activeLayer?.toggleDrawing(true);
if (e.type === "pointerup") {
this.#onSelectEnd("main_toolbar");
}
};
window.addEventListener("pointerup", pointerup, {
signal
});
window.addEventListener("blur", pointerup, {
signal
});
}
}
#onSelectEnd(methodOfCreation = "") {
if (this.#mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.HIGHLIGHT) {
this.highlightSelection(methodOfCreation);
} else if (this.#enableHighlightFloatingButton) {
this.#displayHighlightToolbar();
}
}
#addSelectionListener() {
document.addEventListener("selectionchange", this.#selectionChange.bind(this), {
signal: this._signal
});
}
#addFocusManager() {
if (this.#focusManagerAC) {
return;
}
this.#focusManagerAC = new AbortController();
const signal = this.combinedSignal(this.#focusManagerAC);
window.addEventListener("focus", this.focus.bind(this), {
signal
});
window.addEventListener("blur", this.blur.bind(this), {
signal
});
}
#removeFocusManager() {
this.#focusManagerAC?.abort();
this.#focusManagerAC = null;
}
blur() {
this.isShiftKeyDown = false;
if (this.#highlightWhenShiftUp) {
this.#highlightWhenShiftUp = false;
this.#onSelectEnd("main_toolbar");
}
if (!this.hasSelection) {
return;
}
const {
activeElement
} = document;
for (const editor of this.#selectedEditors) {
if (editor.div.contains(activeElement)) {
this.#lastActiveElement = [editor, activeElement];
editor._focusEventsAllowed = false;
break;
}
}
}
focus() {
if (!this.#lastActiveElement) {
return;
}
const [lastEditor, lastActiveElement] = this.#lastActiveElement;
this.#lastActiveElement = null;
lastActiveElement.addEventListener("focusin", () => {
lastEditor._focusEventsAllowed = true;
}, {
once: true,
signal: this._signal
});
lastActiveElement.focus();
}
#addKeyboardManager() {
if (this.#keyboardManagerAC) {
return;
}
this.#keyboardManagerAC = new AbortController();
const signal = this.combinedSignal(this.#keyboardManagerAC);
window.addEventListener("keydown", this.keydown.bind(this), {
signal
});
window.addEventListener("keyup", this.keyup.bind(this), {
signal
});
}
#removeKeyboardManager() {
this.#keyboardManagerAC?.abort();
this.#keyboardManagerAC = null;
}
#addCopyPasteListeners() {
if (this.#copyPasteAC) {
return;
}
this.#copyPasteAC = new AbortController();
const signal = this.combinedSignal(this.#copyPasteAC);
document.addEventListener("copy", this.copy.bind(this), {
signal
});
document.addEventListener("cut", this.cut.bind(this), {
signal
});
document.addEventListener("paste", this.paste.bind(this), {
signal
});
}
#removeCopyPasteListeners() {
this.#copyPasteAC?.abort();
this.#copyPasteAC = null;
}
#addDragAndDropListeners() {
const signal = this._signal;
document.addEventListener("dragover", this.dragOver.bind(this), {
signal
});
document.addEventListener("drop", this.drop.bind(this), {
signal
});
}
addEditListeners() {
this.#addKeyboardManager();
this.#addCopyPasteListeners();
}
removeEditListeners() {
this.#removeKeyboardManager();
this.#removeCopyPasteListeners();
}
dragOver(event) {
for (const {
type
} of event.dataTransfer.items) {
for (const editorType of this.#editorTypes) {
if (editorType.isHandlingMimeForPasting(type)) {
event.dataTransfer.dropEffect = "copy";
event.preventDefault();
return;
}
}
}
}
drop(event) {
for (const item of event.dataTransfer.items) {
for (const editorType of this.#editorTypes) {
if (editorType.isHandlingMimeForPasting(item.type)) {
editorType.paste(item, this.currentLayer);
event.preventDefault();
return;
}
}
}
}
copy(event) {
event.preventDefault();
this.#activeEditor?.commitOrRemove();
if (!this.hasSelection) {
return;
}
const editors = [];
for (const editor of this.#selectedEditors) {
const serialized = editor.serialize(true);
if (serialized) {
editors.push(serialized);
}
}
if (editors.length === 0) {
return;
}
event.clipboardData.setData("application/pdfjs", JSON.stringify(editors));
}
cut(event) {
this.copy(event);
this.delete();
}
async paste(event) {
event.preventDefault();
const {
clipboardData
} = event;
for (const item of clipboardData.items) {
for (const editorType of this.#editorTypes) {
if (editorType.isHandlingMimeForPasting(item.type)) {
editorType.paste(item, this.currentLayer);
return;
}
}
}
const data = clipboardData.getData("application/pdfjs");
await this.addSerializedEditor(data);
}
async addSerializedEditor(data, activateEditorIfNecessary = false, doNotMove = false, ignorePageNumber = true) {
if (!data) {
return;
}
try {
if (typeof data === "string") {
data = JSON.parse(data);
}
} catch (ex) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`paste: "${ex.message}".`);
return;
}
if (!Array.isArray(data)) {
return;
}
const previousMode = this.#mode;
if (activateEditorIfNecessary && previousMode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE) {
this.updateMode(_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.FREETEXT);
}
this.unselectAll();
try {
const newEditors = [];
for (const editor of data) {
const pageNumberMissing = editor.pageIndex === undefined;
const useCurrentPage = ignorePageNumber || pageNumberMissing;
const layer = useCurrentPage ? this.currentLayer : this.getLayer(editor.pageIndex);
const deserializedEditor = await layer.deserialize(editor);
if (!deserializedEditor) {
return;
}
deserializedEditor.doNotMove = doNotMove;
newEditors.push(deserializedEditor);
}
const cmd = () => {
for (const editor of newEditors) {
this.#addEditorToLayer(editor);
}
this.#selectEditors(newEditors);
};
const undo = () => {
for (const editor of newEditors) {
editor.remove();
}
};
this.addCommands({
cmd,
undo,
mustExec: true
});
} catch (ex) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)(`paste: "${ex.message}".`);
}
if (activateEditorIfNecessary && previousMode !== this.#mode) {
this.updateMode(previousMode);
}
}
keydown(event) {
if (!this.isShiftKeyDown && event.key === "Shift") {
this.isShiftKeyDown = true;
}
if (this.#mode !== _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE && !this.isEditorHandlingKeyboard) {
AnnotationEditorUIManager._keyboardManager.exec(this, event);
}
}
keyup(event) {
if (this.isShiftKeyDown && event.key === "Shift") {
this.isShiftKeyDown = false;
if (this.#highlightWhenShiftUp) {
this.#highlightWhenShiftUp = false;
this.#onSelectEnd("main_toolbar");
}
}
}
onEditingAction({
name
}) {
switch (name) {
case "undo":
case "redo":
case "delete":
case "selectAll":
this[name]();
break;
case "highlightSelection":
this.highlightSelection("context_menu");
break;
}
}
#dispatchUpdateStates(details) {
const hasChanged = Object.entries(details).some(([key, value]) => this.#previousStates[key] !== value);
if (hasChanged) {
this._eventBus.dispatch("annotationeditorstateschanged", {
source: this,
details: Object.assign(this.#previousStates, details)
});
if (this.#mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.HIGHLIGHT && details.hasSelectedEditor === false) {
this.#dispatchUpdateUI([[_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.HIGHLIGHT_FREE, true]]);
}
}
}
#dispatchUpdateUI(details) {
this._eventBus.dispatch("annotationeditorparamschanged", {
source: this,
details
});
}
setEditingState(isEditing) {
if (isEditing) {
this.#addFocusManager();
this.#addCopyPasteListeners();
this.#dispatchUpdateStates({
isEditing: this.#mode !== _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE,
isEmpty: this.#isEmpty(),
hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),
hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),
hasSelectedEditor: false
});
} else {
this.#removeFocusManager();
this.#removeCopyPasteListeners();
this.#dispatchUpdateStates({
isEditing: false
});
this.disableUserSelect(false);
}
}
registerEditorTypes(types) {
if (this.#editorTypes) {
return;
}
this.#editorTypes = types;
for (const editorType of this.#editorTypes) {
this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate);
}
}
getId() {
return this.#idManager.id;
}
get currentLayer() {
return this.#allLayers.get(this.#currentPageIndex);
}
getLayer(pageIndex) {
return this.#allLayers.get(pageIndex);
}
get currentPageIndex() {
return this.#currentPageIndex;
}
addLayer(layer) {
this.#allLayers.set(layer.pageIndex, layer);
if (this.#isEnabled) {
layer.enable();
} else {
layer.disable();
}
}
removeLayer(layer) {
this.#allLayers.delete(layer.pageIndex);
}
async updateMode(mode, editId = null, isFromKeyboard = false) {
if (this.#mode === mode) {
return;
}
if (this.#updateModeCapability) {
await this.#updateModeCapability.promise;
if (!this.#updateModeCapability) {
return;
}
}
this.#updateModeCapability = Promise.withResolvers();
this.#mode = mode;
if (mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE) {
this.setEditingState(false);
this.#disableAll();
this._editorUndoBar?.hide();
this.#updateModeCapability.resolve();
return;
}
this.setEditingState(true);
await this.#enableAll();
this.unselectAll();
for (const layer of this.#allLayers.values()) {
layer.updateMode(mode);
}
if (!editId) {
if (isFromKeyboard) {
this.addNewEditorFromKeyboard();
}
this.#updateModeCapability.resolve();
return;
}
for (const editor of this.#allEditors.values()) {
if (editor.annotationElementId === editId) {
this.setSelected(editor);
editor.enterInEditMode();
} else {
editor.unselect();
}
}
this.#updateModeCapability.resolve();
}
addNewEditorFromKeyboard() {
if (this.currentLayer.canCreateNewEmptyEditor()) {
this.currentLayer.addNewEditor();
}
}
updateToolbar(mode) {
if (mode === this.#mode) {
return;
}
this._eventBus.dispatch("switchannotationeditormode", {
source: this,
mode
});
}
updateParams(type, value) {
if (!this.#editorTypes) {
return;
}
switch (type) {
case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.CREATE:
this.currentLayer.addNewEditor();
return;
case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
this.#mainHighlightColorPicker?.updateColor(value);
break;
case _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL:
this._eventBus.dispatch("reporttelemetry", {
source: this,
details: {
type: "editing",
data: {
type: "highlight",
action: "toggle_visibility"
}
}
});
(this.#showAllStates ||= new Map()).set(type, value);
this.showAllEditors("highlight", value);
break;
}
for (const editor of this.#selectedEditors) {
editor.updateParams(type, value);
}
for (const editorType of this.#editorTypes) {
editorType.updateDefaultParams(type, value);
}
}
showAllEditors(type, visible, updateButton = false) {
for (const editor of this.#allEditors.values()) {
if (editor.editorType === type) {
editor.show(visible);
}
}
const state = this.#showAllStates?.get(_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL) ?? true;
if (state !== visible) {
this.#dispatchUpdateUI([[_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL, visible]]);
}
}
enableWaiting(mustWait = false) {
if (this.#isWaiting === mustWait) {
return;
}
this.#isWaiting = mustWait;
for (const layer of this.#allLayers.values()) {
if (mustWait) {
layer.disableClick();
} else {
layer.enableClick();
}
layer.div.classList.toggle("waiting", mustWait);
}
}
async #enableAll() {
if (!this.#isEnabled) {
this.#isEnabled = true;
const promises = [];
for (const layer of this.#allLayers.values()) {
promises.push(layer.enable());
}
await Promise.all(promises);
for (const editor of this.#allEditors.values()) {
editor.enable();
}
}
}
#disableAll() {
this.unselectAll();
if (this.#isEnabled) {
this.#isEnabled = false;
for (const layer of this.#allLayers.values()) {
layer.disable();
}
for (const editor of this.#allEditors.values()) {
editor.disable();
}
}
}
getEditors(pageIndex) {
const editors = [];
for (const editor of this.#allEditors.values()) {
if (editor.pageIndex === pageIndex) {
editors.push(editor);
}
}
return editors;
}
getEditor(id) {
return this.#allEditors.get(id);
}
addEditor(editor) {
this.#allEditors.set(editor.id, editor);
}
removeEditor(editor) {
if (editor.div.contains(document.activeElement)) {
if (this.#focusMainContainerTimeoutId) {
clearTimeout(this.#focusMainContainerTimeoutId);
}
this.#focusMainContainerTimeoutId = setTimeout(() => {
this.focusMainContainer();
this.#focusMainContainerTimeoutId = null;
}, 0);
}
this.#allEditors.delete(editor.id);
this.unselect(editor);
if (!editor.annotationElementId || !this.#deletedAnnotationsElementIds.has(editor.annotationElementId)) {
this.#annotationStorage?.remove(editor.id);
}
}
addDeletedAnnotationElement(editor) {
this.#deletedAnnotationsElementIds.add(editor.annotationElementId);
this.addChangedExistingAnnotation(editor);
editor.deleted = true;
}
isDeletedAnnotationElement(annotationElementId) {
return this.#deletedAnnotationsElementIds.has(annotationElementId);
}
removeDeletedAnnotationElement(editor) {
this.#deletedAnnotationsElementIds.delete(editor.annotationElementId);
this.removeChangedExistingAnnotation(editor);
editor.deleted = false;
}
#addEditorToLayer(editor) {
const layer = this.#allLayers.get(editor.pageIndex);
if (layer) {
layer.addOrRebuild(editor);
} else {
this.addEditor(editor);
this.addToAnnotationStorage(editor);
}
}
setActiveEditor(editor) {
if (this.#activeEditor === editor) {
return;
}
this.#activeEditor = editor;
if (editor) {
this.#dispatchUpdateUI(editor.propertiesToUpdate);
}
}
get #lastSelectedEditor() {
let ed = null;
for (ed of this.#selectedEditors) {}
return ed;
}
updateUI(editor) {
if (this.#lastSelectedEditor === editor) {
this.#dispatchUpdateUI(editor.propertiesToUpdate);
}
}
updateUIForDefaultProperties(editorType) {
this.#dispatchUpdateUI(editorType.defaultPropertiesToUpdate);
}
toggleSelected(editor) {
if (this.#selectedEditors.has(editor)) {
this.#selectedEditors.delete(editor);
editor.unselect();
this.#dispatchUpdateStates({
hasSelectedEditor: this.hasSelection
});
return;
}
this.#selectedEditors.add(editor);
editor.select();
this.#dispatchUpdateUI(editor.propertiesToUpdate);
this.#dispatchUpdateStates({
hasSelectedEditor: true
});
}
setSelected(editor) {
this.#currentDrawingSession?.commitOrRemove();
for (const ed of this.#selectedEditors) {
if (ed !== editor) {
ed.unselect();
}
}
this.#selectedEditors.clear();
this.#selectedEditors.add(editor);
editor.select();
this.#dispatchUpdateUI(editor.propertiesToUpdate);
this.#dispatchUpdateStates({
hasSelectedEditor: true
});
}
isSelected(editor) {
return this.#selectedEditors.has(editor);
}
get firstSelectedEditor() {
return this.#selectedEditors.values().next().value;
}
unselect(editor) {
editor.unselect();
this.#selectedEditors.delete(editor);
this.#dispatchUpdateStates({
hasSelectedEditor: this.hasSelection
});
}
get hasSelection() {
return this.#selectedEditors.size !== 0;
}
get isEnterHandled() {
return this.#selectedEditors.size === 1 && this.firstSelectedEditor.isEnterHandled;
}
undo() {
this.#commandManager.undo();
this.#dispatchUpdateStates({
hasSomethingToUndo: this.#commandManager.hasSomethingToUndo(),
hasSomethingToRedo: true,
isEmpty: this.#isEmpty()
});
this._editorUndoBar?.hide();
}
redo() {
this.#commandManager.redo();
this.#dispatchUpdateStates({
hasSomethingToUndo: true,
hasSomethingToRedo: this.#commandManager.hasSomethingToRedo(),
isEmpty: this.#isEmpty()
});
}
addCommands(params) {
this.#commandManager.add(params);
this.#dispatchUpdateStates({
hasSomethingToUndo: true,
hasSomethingToRedo: false,
isEmpty: this.#isEmpty()
});
}
cleanUndoStack(type) {
this.#commandManager.cleanType(type);
}
#isEmpty() {
if (this.#allEditors.size === 0) {
return true;
}
if (this.#allEditors.size === 1) {
for (const editor of this.#allEditors.values()) {
return editor.isEmpty();
}
}
return false;
}
delete() {
this.commitOrRemove();
const drawingEditor = this.currentLayer?.endDrawingSession(true);
if (!this.hasSelection && !drawingEditor) {
return;
}
const editors = drawingEditor ? [drawingEditor] : [...this.#selectedEditors];
const cmd = () => {
this._editorUndoBar?.show(undo, editors.length === 1 ? editors[0].editorType : editors.length);
for (const editor of editors) {
editor.remove();
}
};
const undo = () => {
for (const editor of editors) {
this.#addEditorToLayer(editor);
}
};
this.addCommands({
cmd,
undo,
mustExec: true
});
}
commitOrRemove() {
this.#activeEditor?.commitOrRemove();
}
hasSomethingToControl() {
return this.#activeEditor || this.hasSelection;
}
#selectEditors(editors) {
for (const editor of this.#selectedEditors) {
editor.unselect();
}
this.#selectedEditors.clear();
for (const editor of editors) {
if (editor.isEmpty()) {
continue;
}
this.#selectedEditors.add(editor);
editor.select();
}
this.#dispatchUpdateStates({
hasSelectedEditor: this.hasSelection
});
}
selectAll() {
for (const editor of this.#selectedEditors) {
editor.commit();
}
this.#selectEditors(this.#allEditors.values());
}
unselectAll() {
if (this.#activeEditor) {
this.#activeEditor.commitOrRemove();
if (this.#mode !== _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE) {
return;
}
}
if (this.#currentDrawingSession?.commitOrRemove()) {
return;
}
if (!this.hasSelection) {
return;
}
for (const editor of this.#selectedEditors) {
editor.unselect();
}
this.#selectedEditors.clear();
this.#dispatchUpdateStates({
hasSelectedEditor: false
});
}
translateSelectedEditors(x, y, noCommit = false) {
if (!noCommit) {
this.commitOrRemove();
}
if (!this.hasSelection) {
return;
}
this.#translation[0] += x;
this.#translation[1] += y;
const [totalX, totalY] = this.#translation;
const editors = [...this.#selectedEditors];
const TIME_TO_WAIT = 1000;
if (this.#translationTimeoutId) {
clearTimeout(this.#translationTimeoutId);
}
this.#translationTimeoutId = setTimeout(() => {
this.#translationTimeoutId = null;
this.#translation[0] = this.#translation[1] = 0;
this.addCommands({
cmd: () => {
for (const editor of editors) {
if (this.#allEditors.has(editor.id)) {
editor.translateInPage(totalX, totalY);
}
}
},
undo: () => {
for (const editor of editors) {
if (this.#allEditors.has(editor.id)) {
editor.translateInPage(-totalX, -totalY);
}
}
},
mustExec: false
});
}, TIME_TO_WAIT);
for (const editor of editors) {
editor.translateInPage(x, y);
}
}
setUpDragSession() {
if (!this.hasSelection) {
return;
}
this.disableUserSelect(true);
this.#draggingEditors = new Map();
for (const editor of this.#selectedEditors) {
this.#draggingEditors.set(editor, {
savedX: editor.x,
savedY: editor.y,
savedPageIndex: editor.pageIndex,
newX: 0,
newY: 0,
newPageIndex: -1
});
}
}
endDragSession() {
if (!this.#draggingEditors) {
return false;
}
this.disableUserSelect(false);
const map = this.#draggingEditors;
this.#draggingEditors = null;
let mustBeAddedInUndoStack = false;
for (const [{
x,
y,
pageIndex
}, value] of map) {
value.newX = x;
value.newY = y;
value.newPageIndex = pageIndex;
mustBeAddedInUndoStack ||= x !== value.savedX || y !== value.savedY || pageIndex !== value.savedPageIndex;
}
if (!mustBeAddedInUndoStack) {
return false;
}
const move = (editor, x, y, pageIndex) => {
if (this.#allEditors.has(editor.id)) {
const parent = this.#allLayers.get(pageIndex);
if (parent) {
editor._setParentAndPosition(parent, x, y);
} else {
editor.pageIndex = pageIndex;
editor.x = x;
editor.y = y;
}
}
};
this.addCommands({
cmd: () => {
for (const [editor, {
newX,
newY,
newPageIndex,
savedX,
savedY,
savedPageIndex
}] of map) {
move(editor, newX, newY, newPageIndex);
this._eventBus?.dispatch("annotation-editor-event", {
source: editor,
type: "moved",
page: newPageIndex + 1,
editorType: editor.constructor.name,
previousValue: {
x: savedX,
y: savedY,
page: savedPageIndex + 1
},
value: {
x: newX,
y: newY,
page: newPageIndex + 1
}
});
}
},
undo: () => {
for (const [editor, {
savedX,
savedY,
savedPageIndex
}] of map) {
move(editor, savedX, savedY, savedPageIndex);
}
},
mustExec: true
});
return true;
}
dragSelectedEditors(tx, ty) {
if (!this.#draggingEditors) {
return;
}
for (const editor of this.#draggingEditors.keys()) {
editor.drag(tx, ty);
}
}
rebuild(editor) {
if (editor.parent === null) {
const parent = this.getLayer(editor.pageIndex);
if (parent) {
parent.changeParent(editor);
parent.addOrRebuild(editor);
} else {
this.addEditor(editor);
this.addToAnnotationStorage(editor);
editor.rebuild();
}
} else {
editor.parent.addOrRebuild(editor);
}
}
get isEditorHandlingKeyboard() {
return this.getActive()?.shouldGetKeyboardEvents() || this.#selectedEditors.size === 1 && this.firstSelectedEditor.shouldGetKeyboardEvents();
}
isActive(editor) {
return this.#activeEditor === editor;
}
getActive() {
return this.#activeEditor;
}
getMode() {
return this.#mode;
}
get imageManager() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "imageManager", new ImageManager());
}
removeEditors(filterFunction = () => true) {
let hasChanged = false;
this.#allEditors.forEach(editor => {
if (editor?.serialize()) {
if (filterFunction(editor.serialize())) {
editor.remove();
hasChanged = true;
}
}
});
if (hasChanged) {
this.#dispatchUpdateStates({
hasSomethingToUndo: false,
hasSomethingToRedo: false,
isEmpty: this.#isEmpty()
});
this.#commandManager.reset();
}
}
getSelectionBoxes(textLayer) {
if (!textLayer) {
return null;
}
const selection = document.getSelection();
for (let i = 0, ii = selection.rangeCount; i < ii; i++) {
if (!textLayer.contains(selection.getRangeAt(i).commonAncestorContainer)) {
return null;
}
}
const {
x: layerX,
y: layerY,
width: parentWidth,
height: parentHeight
} = textLayer.getBoundingClientRect();
let rotator;
switch (textLayer.getAttribute("data-main-rotation")) {
case "90":
rotator = (x, y, w, h) => ({
x: (y - layerY) / parentHeight,
y: 1 - (x + w - layerX) / parentWidth,
width: h / parentHeight,
height: w / parentWidth
});
break;
case "180":
rotator = (x, y, w, h) => ({
x: 1 - (x + w - layerX) / parentWidth,
y: 1 - (y + h - layerY) / parentHeight,
width: w / parentWidth,
height: h / parentHeight
});
break;
case "270":
rotator = (x, y, w, h) => ({
x: 1 - (y + h - layerY) / parentHeight,
y: (x - layerX) / parentWidth,
width: h / parentHeight,
height: w / parentWidth
});
break;
default:
rotator = (x, y, w, h) => ({
x: (x - layerX) / parentWidth,
y: (y - layerY) / parentHeight,
width: w / parentWidth,
height: h / parentHeight
});
break;
}
const boxes = [];
for (let i = 0, ii = selection.rangeCount; i < ii; i++) {
const range = selection.getRangeAt(i);
if (range.collapsed) {
continue;
}
for (const {
x,
y,
width,
height
} of range.getClientRects()) {
if (width === 0 || height === 0) {
continue;
}
boxes.push(rotator(x, y, width, height));
}
}
return boxes.length === 0 ? null : boxes;
}
addChangedExistingAnnotation({
annotationElementId,
id
}) {
(this.#changedExistingAnnotations ||= new Map()).set(annotationElementId, id);
}
removeChangedExistingAnnotation({
annotationElementId
}) {
this.#changedExistingAnnotations?.delete(annotationElementId);
}
renderAnnotationElement(annotation) {
const editorId = this.#changedExistingAnnotations?.get(annotation.data.id);
if (!editorId) {
return;
}
const editor = this.#annotationStorage.getRawValue(editorId);
if (!editor) {
return;
}
if (this.#mode === _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AnnotationEditorType.NONE && !editor.hasBeenModified) {
return;
}
editor.renderAnnotationElement(annotation);
}
}
/***/ }),
/***/ 814:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ TextLayer: () => (/* binding */ TextLayer)
/* harmony export */ });
/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(292);
/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(419);
const MAX_TEXT_DIVS_TO_RENDER = 100000;
const DEFAULT_FONT_SIZE = 30;
const DEFAULT_FONT_ASCENT = 0.8;
class TextLayer {
#capability = Promise.withResolvers();
#container = null;
#disableProcessItems = false;
#fontInspectorEnabled = !!globalThis.FontInspector?.enabled;
#lang = null;
#layoutTextParams = null;
#pageHeight = 0;
#pageWidth = 0;
#reader = null;
#rootContainer = null;
#rotation = 0;
#scale = 0;
#styleCache = Object.create(null);
#textContentItemsStr = [];
#textContentSource = null;
#textDivs = [];
#textDivProperties = new WeakMap();
#transform = null;
static #ascentCache = new Map();
static #canvasContexts = new Map();
static #canvasCtxFonts = new WeakMap();
static #minFontSize = null;
static #pendingTextLayers = new Set();
constructor({
textContentSource,
container,
viewport
}) {
if (textContentSource instanceof ReadableStream) {
this.#textContentSource = textContentSource;
} else if (typeof textContentSource === "object") {
this.#textContentSource = new ReadableStream({
start(controller) {
controller.enqueue(textContentSource);
controller.close();
}
});
} else {
throw new Error('No "textContentSource" parameter specified.');
}
this.#container = this.#rootContainer = container;
this.#scale = viewport.scale * (globalThis.devicePixelRatio || 1);
this.#rotation = viewport.rotation;
this.#layoutTextParams = {
div: null,
properties: null,
ctx: null
};
const {
pageWidth,
pageHeight,
pageX,
pageY
} = viewport.rawDims;
this.#transform = [1, 0, 0, -1, -pageX, pageY + pageHeight];
this.#pageWidth = pageWidth;
this.#pageHeight = pageHeight;
TextLayer.#ensureMinFontSizeComputed();
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.setLayerDimensions)(container, viewport);
this.#capability.promise.finally(() => {
TextLayer.#pendingTextLayers.delete(this);
this.#layoutTextParams = null;
this.#styleCache = null;
}).catch(() => {});
}
static get fontFamilyMap() {
const {
isWindows,
isFirefox
} = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.FeatureTest.platform;
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "fontFamilyMap", new Map([["sans-serif", `${isWindows && isFirefox ? "Calibri, " : ""}sans-serif`], ["monospace", `${isWindows && isFirefox ? "Lucida Console, " : ""}monospace`]]));
}
render() {
const pump = () => {
this.#reader.read().then(({
value,
done
}) => {
if (done) {
this.#capability.resolve();
return;
}
this.#lang ??= value.lang;
Object.assign(this.#styleCache, value.styles);
this.#processItems(value.items);
pump();
}, this.#capability.reject);
};
this.#reader = this.#textContentSource.getReader();
TextLayer.#pendingTextLayers.add(this);
pump();
return this.#capability.promise;
}
update({
viewport,
onBefore = null
}) {
const scale = viewport.scale * (globalThis.devicePixelRatio || 1);
const rotation = viewport.rotation;
if (rotation !== this.#rotation) {
onBefore?.();
this.#rotation = rotation;
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.setLayerDimensions)(this.#rootContainer, {
rotation
});
}
if (scale !== this.#scale) {
onBefore?.();
this.#scale = scale;
const params = {
div: null,
properties: null,
ctx: TextLayer.#getCtx(this.#lang)
};
for (const div of this.#textDivs) {
params.properties = this.#textDivProperties.get(div);
params.div = div;
this.#layout(params);
}
}
}
cancel() {
const abortEx = new _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.AbortException("TextLayer task cancelled.");
this.#reader?.cancel(abortEx).catch(() => {});
this.#reader = null;
this.#capability.reject(abortEx);
}
get textDivs() {
return this.#textDivs;
}
get textContentItemsStr() {
return this.#textContentItemsStr;
}
#processItems(items) {
if (this.#disableProcessItems) {
return;
}
this.#layoutTextParams.ctx ??= TextLayer.#getCtx(this.#lang);
const textDivs = this.#textDivs,
textContentItemsStr = this.#textContentItemsStr;
for (const item of items) {
if (textDivs.length > MAX_TEXT_DIVS_TO_RENDER) {
(0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.warn)("Ignoring additional textDivs for performance reasons.");
this.#disableProcessItems = true;
return;
}
if (item.str === undefined) {
if (item.type === "beginMarkedContentProps" || item.type === "beginMarkedContent") {
const parent = this.#container;
this.#container = document.createElement("span");
this.#container.classList.add("markedContent");
if (item.id !== null) {
this.#container.setAttribute("id", `${item.id}`);
}
parent.append(this.#container);
} else if (item.type === "endMarkedContent") {
this.#container = this.#container.parentNode;
}
continue;
}
textContentItemsStr.push(item.str);
this.#appendText(item);
}
}
#appendText(geom) {
const textDiv = document.createElement("span");
const textDivProperties = {
angle: 0,
canvasWidth: 0,
hasText: geom.str !== "",
hasEOL: geom.hasEOL,
fontSize: 0
};
this.#textDivs.push(textDiv);
const tx = _shared_util_js__WEBPACK_IMPORTED_MODULE_0__.Util.transform(this.#transform, geom.transform);
let angle = Math.atan2(tx[1], tx[0]);
const style = this.#styleCache[geom.fontName];
if (style.vertical) {
angle += Math.PI / 2;
}
let fontFamily = this.#fontInspectorEnabled && style.fontSubstitution || style.fontFamily;
fontFamily = TextLayer.fontFamilyMap.get(fontFamily) || fontFamily;
const fontHeight = Math.hypot(tx[2], tx[3]);
const fontAscent = fontHeight * TextLayer.#getAscent(fontFamily, this.#lang);
let left, top;
if (angle === 0) {
left = tx[4];
top = tx[5] - fontAscent;
} else {
left = tx[4] + fontAscent * Math.sin(angle);
top = tx[5] - fontAscent * Math.cos(angle);
}
const scaleFactorStr = "calc(var(--scale-factor)*";
const divStyle = textDiv.style;
if (this.#container === this.#rootContainer) {
divStyle.left = `${(100 * left / this.#pageWidth).toFixed(2)}%`;
divStyle.top = `${(100 * top / this.#pageHeight).toFixed(2)}%`;
} else {
divStyle.left = `${scaleFactorStr}${left.toFixed(2)}px)`;
divStyle.top = `${scaleFactorStr}${top.toFixed(2)}px)`;
}
divStyle.fontSize = `${scaleFactorStr}${(TextLayer.#minFontSize * fontHeight).toFixed(2)}px)`;
divStyle.fontFamily = fontFamily;
textDivProperties.fontSize = fontHeight;
textDiv.setAttribute("role", "presentation");
textDiv.textContent = geom.str;
textDiv.dir = geom.dir;
if (this.#fontInspectorEnabled) {
textDiv.dataset.fontName = style.fontSubstitutionLoadedName || geom.fontName;
}
if (angle !== 0) {
textDivProperties.angle = angle * (180 / Math.PI);
}
let shouldScaleText = false;
if (geom.str.length > 1) {
shouldScaleText = true;
} else if (geom.str !== " " && geom.transform[0] !== geom.transform[3]) {
const absScaleX = Math.abs(geom.transform[0]),
absScaleY = Math.abs(geom.transform[3]);
if (absScaleX !== absScaleY && Math.max(absScaleX, absScaleY) / Math.min(absScaleX, absScaleY) > 1.5) {
shouldScaleText = true;
}
}
if (shouldScaleText) {
textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;
}
this.#textDivProperties.set(textDiv, textDivProperties);
this.#layoutTextParams.div = textDiv;
this.#layoutTextParams.properties = textDivProperties;
this.#layout(this.#layoutTextParams);
if (textDivProperties.hasText) {
this.#container.append(textDiv);
}
if (textDivProperties.hasEOL) {
const br = document.createElement("br");
br.setAttribute("role", "presentation");
this.#container.append(br);
}
}
#layout(params) {
const {
div,
properties,
ctx
} = params;
const {
style
} = div;
let transform = "";
if (TextLayer.#minFontSize > 1) {
transform = `scale(${1 / TextLayer.#minFontSize})`;
}
if (properties.canvasWidth !== 0 && properties.hasText) {
const {
fontFamily
} = style;
const {
canvasWidth,
fontSize
} = properties;
TextLayer.#ensureCtxFont(ctx, fontSize * this.#scale, fontFamily);
try {
const {
width
} = ctx.measureText(div.textContent);
if (width > 0) {
transform = `scaleX(${canvasWidth * this.#scale / width}) ${transform}`;
}
} catch (fingerprintIsBlockedException) {}
}
if (properties.angle !== 0) {
transform = `rotate(${properties.angle}deg) ${transform}`;
}
if (transform.length > 0) {
style.transform = transform;
}
}
static cleanup() {
if (this.#pendingTextLayers.size > 0) {
return;
}
this.#ascentCache.clear();
for (const {
canvas
} of this.#canvasContexts.values()) {
canvas.remove();
}
this.#canvasContexts.clear();
}
static #getCtx(lang = null) {
let ctx = this.#canvasContexts.get(lang ||= "");
if (!ctx) {
const canvas = document.createElement("canvas");
canvas.className = "hiddenCanvasElement";
canvas.lang = lang;
canvas.width = canvas.height = 0;
document.body.append(canvas);
ctx = canvas.getContext("2d", {
alpha: false,
willReadFrequently: true
});
this.#canvasContexts.set(lang, ctx);
this.#canvasCtxFonts.set(ctx, {
size: 0,
family: ""
});
}
return ctx;
}
static #ensureCtxFont(ctx, size, family) {
const cached = this.#canvasCtxFonts.get(ctx);
if (size === cached.size && family === cached.family) {
return;
}
ctx.font = `${size}px ${family}`;
cached.size = size;
cached.family = family;
}
static #ensureMinFontSizeComputed() {
if (this.#minFontSize !== null) {
return;
}
const div = document.createElement("div");
div.style.opacity = 0;
div.style.lineHeight = 1;
div.style.fontSize = "1px";
div.style.position = "absolute";
div.textContent = "X";
document.body.append(div);
this.#minFontSize = div.getBoundingClientRect().height;
div.remove();
}
static #getAscent(fontFamily, lang) {
const cachedAscent = this.#ascentCache.get(fontFamily);
if (cachedAscent) {
return cachedAscent;
}
const ctx = this.#getCtx(lang);
ctx.canvas.width = ctx.canvas.height = DEFAULT_FONT_SIZE;
this.#ensureCtxFont(ctx, DEFAULT_FONT_SIZE, fontFamily);
const metrics = ctx.measureText("");
let ascent = metrics?.fontBoundingBoxAscent;
let descent = Math.abs(metrics?.fontBoundingBoxDescent);
if (ascent) {
const ratio = ascent / (ascent + descent);
this.#ascentCache.set(fontFamily, ratio);
ctx.canvas.width = ctx.canvas.height = 0;
return ratio;
}
ctx.strokeStyle = "red";
ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);
ctx.strokeText("g", 0, 0);
let pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;
descent = 0;
for (let i = pixels.length - 1 - 3; i >= 0; i -= 4) {
if (pixels[i] > 0) {
descent = Math.ceil(i / 4 / DEFAULT_FONT_SIZE);
break;
}
}
ctx.clearRect(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE);
ctx.strokeText("A", 0, DEFAULT_FONT_SIZE);
pixels = ctx.getImageData(0, 0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE).data;
ascent = 0;
for (let i = 0, ii = pixels.length; i < ii; i += 4) {
if (pixels[i] > 0) {
ascent = DEFAULT_FONT_SIZE - Math.floor(i / 4 / DEFAULT_FONT_SIZE);
break;
}
}
ctx.canvas.width = ctx.canvas.height = 0;
const ratio = ascent ? ascent / (ascent + descent) : DEFAULT_FONT_ASCENT;
this.#ascentCache.set(fontFamily, ratio);
return ratio;
}
}
/***/ }),
/***/ 900:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ TouchManager: () => (/* binding */ TouchManager)
/* harmony export */ });
/* harmony import */ var _shared_util_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(292);
/* harmony import */ var _display_utils_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(419);
class TouchManager {
#container;
#isPinching = false;
#isPinchingStopped = null;
#isPinchingDisabled;
#onPinchStart;
#onPinching;
#onPinchEnd;
#signal;
#touchInfo = null;
#touchManagerAC;
#touchMoveAC = null;
constructor({
container,
isPinchingDisabled = null,
isPinchingStopped = null,
onPinchStart = null,
onPinching = null,
onPinchEnd = null,
signal
}) {
this.#container = container;
this.#isPinchingStopped = isPinchingStopped;
this.#isPinchingDisabled = isPinchingDisabled;
this.#onPinchStart = onPinchStart;
this.#onPinching = onPinching;
this.#onPinchEnd = onPinchEnd;
this.#touchManagerAC = new AbortController();
this.#signal = AbortSignal.any([signal, this.#touchManagerAC.signal]);
container.addEventListener("touchstart", this.#onTouchStart.bind(this), {
passive: false,
signal: this.#signal
});
}
get MIN_TOUCH_DISTANCE_TO_PINCH() {
return (0,_shared_util_js__WEBPACK_IMPORTED_MODULE_0__.shadow)(this, "MIN_TOUCH_DISTANCE_TO_PINCH", 35 / (window.devicePixelRatio || 1));
}
#onTouchStart(evt) {
if (this.#isPinchingDisabled?.() || evt.touches.length < 2) {
return;
}
if (!this.#touchMoveAC) {
this.#touchMoveAC = new AbortController();
const signal = AbortSignal.any([this.#signal, this.#touchMoveAC.signal]);
const container = this.#container;
const opt = {
signal,
passive: false
};
container.addEventListener("touchmove", this.#onTouchMove.bind(this), opt);
container.addEventListener("touchend", this.#onTouchEnd.bind(this), opt);
container.addEventListener("touchcancel", this.#onTouchEnd.bind(this), opt);
this.#onPinchStart?.();
}
(0,_display_utils_js__WEBPACK_IMPORTED_MODULE_1__.stopEvent)(evt);
if (evt.touches.length !== 2 || this.#isPinchingStopped?.()) {
this.#touchInfo = null;
return;
}
let [touch0, touch1] = evt.touches;
if (touch0.identifier > touch1.identifier) {
[touch0, touch1] = [touch1, touch0];
}
this.#touchInfo = {
touch0X: touch0.screenX,
touch0Y: touch0.screenY,
touch1X: touch1.screenX,
touch1Y: touch1.screenY
};
}
#onTouchMove(evt) {
if (!this.#touchInfo || evt.touches.length !== 2) {
return;
}
let [touch0, touch1] = evt.touches;
if (touch0.identifier > touch1.identifier) {
[touch0, touch1] = [touch1, touch0];
}
const {
screenX: screen0X,
screenY: screen0Y
} = touch0;
const {
screenX: screen1X,
screenY: screen1Y
} = touch1;
const touchInfo = this.#touchInfo;
const {
touch0X: pTouch0X,
touch0Y: pTouch0Y,
touch1X: pTouch1X,
touch1Y: pTouch1Y
} = touchInfo;
const prevGapX = pTouch1X - pTouch0X;
const prevGapY = pTouch1Y - pTouch0Y;
const currGapX = screen1X - screen0X;
const currGapY = screen1Y - screen0Y;
const distance = Math.hypot(currGapX, currGapY) || 1;
const pDistance = Math.hypot(prevGapX, prevGapY) || 1;
if (!this.#isPinching && Math.abs(pDistance - distance) <= TouchManager.MIN_TOUCH_DISTANCE_TO_PINCH) {
return;
}
touchInfo.touch0X = screen0X;
touchInfo.touch0Y = screen0Y;
touchInfo.touch1X = screen1X;
touchInfo.touch1Y = screen1Y;
evt.preventDefault();
if (!this.#isPinching) {
this.#isPinching = true;
return;
}
const origin = [(screen0X + screen1X) / 2, (screen0Y + screen1Y) / 2];
this.#onPinching?.(origin, pDistance, distance);
}
#onTouchEnd(evt) {
this.#touchMoveAC.abort();
this.#touchMoveAC = null;
this.#onPinchEnd?.();
if (!this.#touchInfo) {
return;
}
evt.preventDefault();
this.#touchInfo = null;
this.#isPinching = false;
}
destroy() {
this.#touchManagerAC?.abort();
this.#touchManagerAC = null;
}
}
/***/ }),
/***/ 164:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ GlobalWorkerOptions: () => (/* binding */ GlobalWorkerOptions)
/* harmony export */ });
class GlobalWorkerOptions {
static #port = null;
static #src = "";
static get workerPort() {
return this.#port;
}
static set workerPort(val) {
if (!(typeof Worker !== "undefined" && val instanceof Worker) && val !== null) {
throw new Error("Invalid `workerPort` type.");
}
this.#port = val;
}
static get workerSrc() {
return this.#src;
}
static set workerSrc(val) {
if (typeof val !== "string") {
throw new Error("Invalid `workerSrc` type.");
}
this.#src = val;
}
}
/***/ }),
/***/ 50:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ XfaText: () => (/* binding */ XfaText)
/* harmony export */ });
class XfaText {
static textContent(xfa) {
const items = [];
const output = {
items,
styles: Object.create(null)
};
function walk(node) {
if (!node) {
return;
}
let str = null;
const name = node.name;
if (name === "#text") {
str = node.value;
} else if (!XfaText.shouldBuildText(name)) {
return;
} else if (node?.attributes?.textContent) {
str = node.attributes.textContent;
} else if (node.value) {
str = node.value;
}
if (str !== null) {
items.push({
str
});
}
if (!node.children) {
return;
}
for (const child of node.children) {
walk(child);
}
}
walk(xfa);
return output;
}
static shouldBuildText(name) {
return !(name === "textarea" || name === "input" || name === "option" || name === "select");
}
}
/***/ }),
/***/ 909:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
AbortException: () => (/* reexport */ util.AbortException),
AnnotationEditorLayer: () => (/* reexport */ AnnotationEditorLayer),
AnnotationEditorParamsType: () => (/* reexport */ util.AnnotationEditorParamsType),
AnnotationEditorType: () => (/* reexport */ util.AnnotationEditorType),
AnnotationEditorUIManager: () => (/* reexport */ tools.AnnotationEditorUIManager),
AnnotationLayer: () => (/* reexport */ AnnotationLayer),
AnnotationMode: () => (/* reexport */ util.AnnotationMode),
ColorPicker: () => (/* reexport */ ColorPicker),
DOMSVGFactory: () => (/* reexport */ DOMSVGFactory),
DrawLayer: () => (/* reexport */ DrawLayer),
FeatureTest: () => (/* reexport */ util.FeatureTest),
GlobalWorkerOptions: () => (/* reexport */ worker_options.GlobalWorkerOptions),
ImageKind: () => (/* reexport */ util.ImageKind),
InvalidPDFException: () => (/* reexport */ util.InvalidPDFException),
MissingPDFException: () => (/* reexport */ util.MissingPDFException),
OPS: () => (/* reexport */ util.OPS),
OutputScale: () => (/* reexport */ display_utils.OutputScale),
PDFDataRangeTransport: () => (/* reexport */ api.PDFDataRangeTransport),
PDFDateString: () => (/* reexport */ display_utils.PDFDateString),
PDFWorker: () => (/* reexport */ api.PDFWorker),
PasswordResponses: () => (/* reexport */ util.PasswordResponses),
PermissionFlag: () => (/* reexport */ util.PermissionFlag),
PixelsPerInch: () => (/* reexport */ display_utils.PixelsPerInch),
RenderingCancelledException: () => (/* reexport */ display_utils.RenderingCancelledException),
TextLayer: () => (/* reexport */ text_layer.TextLayer),
TouchManager: () => (/* reexport */ touch_manager.TouchManager),
UnexpectedResponseException: () => (/* reexport */ util.UnexpectedResponseException),
Util: () => (/* reexport */ util.Util),
VerbosityLevel: () => (/* reexport */ util.VerbosityLevel),
XfaLayer: () => (/* reexport */ XfaLayer),
build: () => (/* reexport */ api.build),
createValidAbsoluteUrl: () => (/* reexport */ util.createValidAbsoluteUrl),
fetchData: () => (/* reexport */ display_utils.fetchData),
getDocument: () => (/* reexport */ api.getDocument),
getFilenameFromUrl: () => (/* reexport */ display_utils.getFilenameFromUrl),
getPdfFilenameFromUrl: () => (/* reexport */ display_utils.getPdfFilenameFromUrl),
getXfaPageViewport: () => (/* reexport */ display_utils.getXfaPageViewport),
isDataScheme: () => (/* reexport */ display_utils.isDataScheme),
isPdfFile: () => (/* reexport */ display_utils.isPdfFile),
noContextMenu: () => (/* reexport */ display_utils.noContextMenu),
normalizeUnicode: () => (/* reexport */ util.normalizeUnicode),
setLayerDimensions: () => (/* reexport */ display_utils.setLayerDimensions),
shadow: () => (/* reexport */ util.shadow),
stopEvent: () => (/* reexport */ display_utils.stopEvent),
version: () => (/* reexport */ api.version)
});
// EXTERNAL MODULE: ./src/shared/util.js
var util = __webpack_require__(292);
// EXTERNAL MODULE: ./src/display/api.js + 18 modules
var api = __webpack_require__(925);
// EXTERNAL MODULE: ./src/display/display_utils.js
var display_utils = __webpack_require__(419);
// EXTERNAL MODULE: ./src/display/editor/editor.js + 1 modules
var editor_editor = __webpack_require__(310);
// EXTERNAL MODULE: ./src/display/editor/tools.js
var tools = __webpack_require__(830);
// EXTERNAL MODULE: ./src/display/annotation_storage.js
var annotation_storage = __webpack_require__(792);
;// ./src/shared/scripting_utils.js
function makeColorComp(n) {
return Math.floor(Math.max(0, Math.min(1, n)) * 255).toString(16).padStart(2, "0");
}
function scaleAndClamp(x) {
return Math.max(0, Math.min(255, 255 * x));
}
class ColorConverters {
static CMYK_G([c, y, m, k]) {
return ["G", 1 - Math.min(1, 0.3 * c + 0.59 * m + 0.11 * y + k)];
}
static G_CMYK([g]) {
return ["CMYK", 0, 0, 0, 1 - g];
}
static G_RGB([g]) {
return ["RGB", g, g, g];
}
static G_rgb([g]) {
g = scaleAndClamp(g);
return [g, g, g];
}
static G_HTML([g]) {
const G = makeColorComp(g);
return `#${G}${G}${G}`;
}
static RGB_G([r, g, b]) {
return ["G", 0.3 * r + 0.59 * g + 0.11 * b];
}
static RGB_rgb(color) {
return color.map(scaleAndClamp);
}
static RGB_HTML(color) {
return `#${color.map(makeColorComp).join("")}`;
}
static T_HTML() {
return "#00000000";
}
static T_rgb() {
return [null];
}
static CMYK_RGB([c, y, m, k]) {
return ["RGB", 1 - Math.min(1, c + k), 1 - Math.min(1, m + k), 1 - Math.min(1, y + k)];
}
static CMYK_rgb([c, y, m, k]) {
return [scaleAndClamp(1 - Math.min(1, c + k)), scaleAndClamp(1 - Math.min(1, m + k)), scaleAndClamp(1 - Math.min(1, y + k))];
}
static CMYK_HTML(components) {
const rgb = this.CMYK_RGB(components).slice(1);
return this.RGB_HTML(rgb);
}
static RGB_CMYK([r, g, b]) {
const c = 1 - r;
const m = 1 - g;
const y = 1 - b;
const k = Math.min(c, m, y);
return ["CMYK", c, m, y, k];
}
}
;// ./src/display/svg_factory.js
class BaseSVGFactory {
create(width, height, skipDimensions = false) {
if (width <= 0 || height <= 0) {
throw new Error("Invalid SVG dimensions");
}
const svg = this._createSVG("svg:svg");
svg.setAttribute("version", "1.1");
if (!skipDimensions) {
svg.setAttribute("width", `${width}px`);
svg.setAttribute("height", `${height}px`);
}
svg.setAttribute("preserveAspectRatio", "none");
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
return svg;
}
createElement(type) {
if (typeof type !== "string") {
throw new Error("Invalid SVG element type");
}
return this._createSVG(type);
}
_createSVG(type) {
(0,util.unreachable)("Abstract method `_createSVG` called.");
}
}
class DOMSVGFactory extends BaseSVGFactory {
_createSVG(type) {
return document.createElementNS(display_utils.SVG_NS, type);
}
}
// EXTERNAL MODULE: ./src/display/xfa_text.js
var xfa_text = __webpack_require__(50);
;// ./src/display/xfa_layer.js
class XfaLayer {
static setupStorage(html, id, element, storage, intent) {
const angularData = window.getFormValueFromAngular(html);
if (angularData.value) {
storage.setValue(id, angularData);
}
const initialValue = storage.getValue(id, {
value: null
});
const storedData = angularData.value ? angularData : initialValue;
window.registerXFAField(html, storedData, initialValue);
html.addEventListener("updateFromAngular", value => storage.setValue(id, {
value: value.detail
}));
switch (element.name) {
case "textarea":
if (storedData.value !== null) {
html.textContent = storedData.value;
}
if (intent === "print") {
break;
}
html.addEventListener("input", event => {
window.updateAngularFormValue(html, {
value: event.target.value
});
storage.setValue(id, {
value: event.target.value
});
});
break;
case "input":
if (element.attributes.type === "radio" || element.attributes.type === "checkbox") {
if (storedData.value === element.attributes.xfaOn) {
html.setAttribute("checked", true);
} else {
html.removeAttribute("checked");
}
if (intent === "print") {
break;
}
html.addEventListener("change", event => {
window.updateAngularFormValue(html, {
value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff")
});
storage.setValue(id, {
value: event.target.checked ? event.target.getAttribute("xfaOn") : event.target.getAttribute("xfaOff")
});
});
} else {
if (storedData.value !== null) {
html.setAttribute("value", storedData.value);
}
if (intent === "print") {
break;
}
html.addEventListener("input", event => {
window.updateAngularFormValue(html, {
value: event.target.value
});
storage.setValue(id, {
value: event.target.value
});
});
}
break;
case "select":
if (storedData.value !== null) {
html.setAttribute("value", storedData.value);
for (const option of element.children) {
if (option.attributes.value === storedData.value) {
option.attributes.selected = true;
} else if (option.attributes.hasOwnProperty("selected")) {
delete option.attributes.selected;
}
}
}
html.addEventListener("input", event => {
const options = event.target.options;
const value = options.selectedIndex === -1 ? "" : options[options.selectedIndex].value;
window.updateAngularFormValue(html, {
value
});
storage.setValue(id, {
value
});
});
break;
}
}
static setAttributes({
html,
element,
storage = null,
intent,
linkService
}) {
const {
attributes
} = element;
const isHTMLAnchorElement = html instanceof HTMLAnchorElement;
if (attributes.type === "radio") {
attributes.name = `${attributes.name}-${intent}`;
}
for (const [key, value] of Object.entries(attributes)) {
if (value === null || value === undefined) {
continue;
}
switch (key) {
case "class":
if (value.length) {
html.setAttribute(key, value.join(" "));
}
break;
case "dataId":
break;
case "id":
html.setAttribute("data-element-id", value);
break;
case "style":
Object.assign(html.style, value);
break;
case "textContent":
html.textContent = value;
break;
default:
if (!isHTMLAnchorElement || key !== "href" && key !== "newWindow") {
html.setAttribute(key, value);
}
}
}
if (isHTMLAnchorElement) {
linkService.addLinkAttributes(html, attributes.href, attributes.newWindow);
}
if (storage && attributes.dataId) {
this.setupStorage(html, attributes.dataId, element, storage);
}
}
static render(parameters) {
const storage = parameters.annotationStorage;
const linkService = parameters.linkService;
const root = parameters.xfaHtml;
const intent = parameters.intent || "display";
const rootHtml = document.createElement(root.name);
if (root.attributes) {
this.setAttributes({
html: rootHtml,
element: root,
intent,
linkService
});
}
const isNotForRichText = intent !== "richText";
const rootDiv = parameters.div;
rootDiv.append(rootHtml);
if (parameters.viewport) {
const transform = `matrix(${parameters.viewport.transform.join(",")})`;
rootDiv.style.transform = transform;
}
if (isNotForRichText) {
rootDiv.setAttribute("class", "xfaLayer xfaFont");
}
const textDivs = [];
if (root.children.length === 0) {
if (root.value) {
const node = document.createTextNode(root.value);
rootHtml.append(node);
if (isNotForRichText && xfa_text.XfaText.shouldBuildText(root.name)) {
textDivs.push(node);
}
}
return {
textDivs
};
}
const stack = [[root, -1, rootHtml]];
while (stack.length > 0) {
const [parent, i, html] = stack.at(-1);
if (i + 1 === parent.children.length) {
stack.pop();
continue;
}
const child = parent.children[++stack.at(-1)[1]];
if (child === null) {
continue;
}
const {
name
} = child;
if (name === "#text") {
const node = document.createTextNode(child.value);
textDivs.push(node);
html.append(node);
continue;
}
const childHtml = child?.attributes?.xmlns ? document.createElementNS(child.attributes.xmlns, name) : document.createElement(name);
html.append(childHtml);
if (child.attributes) {
this.setAttributes({
html: childHtml,
element: child,
storage,
intent,
linkService
});
}
if (child.children?.length > 0) {
stack.push([child, -1, childHtml]);
} else if (child.value) {
const node = document.createTextNode(child.value);
if (isNotForRichText && xfa_text.XfaText.shouldBuildText(name)) {
textDivs.push(node);
}
childHtml.append(node);
}
}
for (const el of rootDiv.querySelectorAll(".xfaNonInteractive input, .xfaNonInteractive textarea")) {
el.setAttribute("readOnly", true);
}
return {
textDivs
};
}
static update(parameters) {
const transform = `matrix(${parameters.viewport.transform.join(",")})`;
parameters.div.style.transform = transform;
parameters.div.hidden = false;
}
}
;// ./src/display/annotation_layer.js
const DEFAULT_TAB_INDEX = 1000;
const DEFAULT_FONT_SIZE = 9;
const GetElementsByNameSet = new WeakSet();
function getRectDims(rect) {
return {
width: rect[2] - rect[0],
height: rect[3] - rect[1]
};
}
class AnnotationElementFactory {
static create(parameters) {
const subtype = parameters.data.annotationType;
switch (subtype) {
case util.AnnotationType.LINK:
return new LinkAnnotationElement(parameters);
case util.AnnotationType.TEXT:
return new TextAnnotationElement(parameters);
case util.AnnotationType.WIDGET:
const fieldType = parameters.data.fieldType;
switch (fieldType) {
case "Tx":
return new TextWidgetAnnotationElement(parameters);
case "Btn":
if (parameters.data.radioButton) {
return new RadioButtonWidgetAnnotationElement(parameters);
} else if (parameters.data.checkBox) {
return new CheckboxWidgetAnnotationElement(parameters);
}
return new PushButtonWidgetAnnotationElement(parameters);
case "Ch":
return new ChoiceWidgetAnnotationElement(parameters);
case "Sig":
return new SignatureWidgetAnnotationElement(parameters);
}
return new WidgetAnnotationElement(parameters);
case util.AnnotationType.POPUP:
return new PopupAnnotationElement(parameters);
case util.AnnotationType.FREETEXT:
return new FreeTextAnnotationElement(parameters);
case util.AnnotationType.LINE:
return new LineAnnotationElement(parameters);
case util.AnnotationType.SQUARE:
return new SquareAnnotationElement(parameters);
case util.AnnotationType.CIRCLE:
return new CircleAnnotationElement(parameters);
case util.AnnotationType.POLYLINE:
return new PolylineAnnotationElement(parameters);
case util.AnnotationType.CARET:
return new CaretAnnotationElement(parameters);
case util.AnnotationType.INK:
return new InkAnnotationElement(parameters);
case util.AnnotationType.POLYGON:
return new PolygonAnnotationElement(parameters);
case util.AnnotationType.HIGHLIGHT:
return new HighlightAnnotationElement(parameters);
case util.AnnotationType.UNDERLINE:
return new UnderlineAnnotationElement(parameters);
case util.AnnotationType.SQUIGGLY:
return new SquigglyAnnotationElement(parameters);
case util.AnnotationType.STRIKEOUT:
return new StrikeOutAnnotationElement(parameters);
case util.AnnotationType.STAMP:
return new StampAnnotationElement(parameters);
case util.AnnotationType.FILEATTACHMENT:
return new FileAttachmentAnnotationElement(parameters);
default:
return new AnnotationElement(parameters);
}
}
}
class AnnotationElement {
#updates = null;
#hasBorder = false;
#popupElement = null;
constructor(parameters, {
isRenderable = false,
ignoreBorder = false,
createQuadrilaterals = false
} = {}) {
this.isRenderable = isRenderable;
this.data = parameters.data;
this.layer = parameters.layer;
this.linkService = parameters.linkService;
this.downloadManager = parameters.downloadManager;
this.imageResourcesPath = parameters.imageResourcesPath;
this.renderForms = parameters.renderForms;
this.svgFactory = parameters.svgFactory;
this.annotationStorage = parameters.annotationStorage;
this.enableScripting = parameters.enableScripting;
this.hasJSActions = parameters.hasJSActions;
this._fieldObjects = parameters.fieldObjects;
this.parent = parameters.parent;
if (isRenderable) {
this.container = this._createContainer(ignoreBorder);
}
if (createQuadrilaterals) {
this._createQuadrilaterals();
}
}
static _hasPopupData({
titleObj,
contentsObj,
richText
}) {
return !!(titleObj?.str || contentsObj?.str || richText?.str);
}
get _isEditable() {
return this.data.isEditable;
}
get hasPopupData() {
return AnnotationElement._hasPopupData(this.data);
}
updateEdited(params) {
if (!this.container) {
return;
}
this.#updates ||= {
rect: this.data.rect.slice(0)
};
const {
rect
} = params;
if (rect) {
this.#setRectEdited(rect);
}
this.#popupElement?.popup.updateEdited(params);
}
resetEdited() {
if (!this.#updates) {
return;
}
this.#setRectEdited(this.#updates.rect);
this.#popupElement?.popup.resetEdited();
this.#updates = null;
}
#setRectEdited(rect) {
const {
container: {
style
},
data: {
rect: currentRect,
rotation
},
parent: {
viewport: {
rawDims: {
pageWidth,
pageHeight,
pageX,
pageY
}
}
}
} = this;
currentRect?.splice(0, 4, ...rect);
const {
width,
height
} = getRectDims(rect);
style.left = `${100 * (rect[0] - pageX) / pageWidth}%`;
style.top = `${100 * (pageHeight - rect[3] + pageY) / pageHeight}%`;
if (rotation === 0) {
style.width = `${100 * width / pageWidth}%`;
style.height = `${100 * height / pageHeight}%`;
} else {
this.setRotation(rotation);
}
}
_createContainer(ignoreBorder) {
const {
data,
parent: {
page,
viewport
}
} = this;
const container = document.createElement("section");
container.setAttribute("data-annotation-id", data.id);
if (!(this instanceof WidgetAnnotationElement)) {
container.tabIndex = DEFAULT_TAB_INDEX;
}
const {
style
} = container;
style.zIndex = this.parent.zIndex++;
if (data.alternativeText) {
container.title = data.alternativeText;
}
if (data.noRotate) {
container.classList.add("norotate");
}
if (!data.rect || this instanceof PopupAnnotationElement) {
const {
rotation
} = data;
if (!data.hasOwnCanvas && rotation !== 0) {
this.setRotation(rotation, container);
}
return container;
}
const {
width,
height
} = getRectDims(data.rect);
if (!ignoreBorder && data.borderStyle.width > 0) {
style.borderWidth = `${data.borderStyle.width}px`;
const horizontalRadius = data.borderStyle.horizontalCornerRadius;
const verticalRadius = data.borderStyle.verticalCornerRadius;
if (horizontalRadius > 0 || verticalRadius > 0) {
const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`;
style.borderRadius = radius;
} else if (this instanceof RadioButtonWidgetAnnotationElement) {
const radius = `calc(${width}px * var(--scale-factor)) / calc(${height}px * var(--scale-factor))`;
style.borderRadius = radius;
}
switch (data.borderStyle.style) {
case util.AnnotationBorderStyleType.SOLID:
style.borderStyle = "solid";
break;
case util.AnnotationBorderStyleType.DASHED:
style.borderStyle = "dashed";
break;
case util.AnnotationBorderStyleType.BEVELED:
(0,util.warn)("Unimplemented border style: beveled");
break;
case util.AnnotationBorderStyleType.INSET:
(0,util.warn)("Unimplemented border style: inset");
break;
case util.AnnotationBorderStyleType.UNDERLINE:
style.borderBottomStyle = "solid";
break;
default:
break;
}
const borderColor = data.borderColor || null;
if (borderColor) {
this.#hasBorder = true;
style.borderColor = util.Util.makeHexColor(borderColor[0] | 0, borderColor[1] | 0, borderColor[2] | 0);
} else {
style.borderWidth = 0;
}
}
const rect = util.Util.normalizeRect([data.rect[0], page.view[3] - data.rect[1] + page.view[1], data.rect[2], page.view[3] - data.rect[3] + page.view[1]]);
const {
pageWidth,
pageHeight,
pageX,
pageY
} = viewport.rawDims;
style.left = `${100 * (rect[0] - pageX) / pageWidth}%`;
style.top = `${100 * (rect[1] - pageY) / pageHeight}%`;
const {
rotation
} = data;
if (data.hasOwnCanvas || rotation === 0) {
style.width = `${100 * width / pageWidth}%`;
style.height = `${100 * height / pageHeight}%`;
} else {
this.setRotation(rotation, container);
}
return container;
}
setRotation(angle, container = this.container) {
if (!this.data.rect) {
return;
}
const {
pageWidth,
pageHeight
} = this.parent.viewport.rawDims;
const {
width,
height
} = getRectDims(this.data.rect);
let elementWidth, elementHeight;
if (angle % 180 === 0) {
elementWidth = 100 * width / pageWidth;
elementHeight = 100 * height / pageHeight;
} else {
elementWidth = 100 * height / pageWidth;
elementHeight = 100 * width / pageHeight;
}
container.style.width = `${elementWidth}%`;
container.style.height = `${elementHeight}%`;
container.setAttribute("data-main-rotation", (360 - angle) % 360);
}
get _commonActions() {
const setColor = (jsName, styleName, event) => {
const color = event.detail[jsName];
const colorType = color[0];
const colorArray = color.slice(1);
event.target.style[styleName] = ColorConverters[`${colorType}_HTML`](colorArray);
this.annotationStorage.setValue(this.data.id, {
[styleName]: ColorConverters[`${colorType}_rgb`](colorArray)
});
};
return (0,util.shadow)(this, "_commonActions", {
display: event => {
const {
display
} = event.detail;
const hidden = display % 2 === 1;
this.container.style.visibility = hidden ? "hidden" : "visible";
this.annotationStorage.setValue(this.data.id, {
noView: hidden,
noPrint: display === 1 || display === 2
});
},
print: event => {
this.annotationStorage.setValue(this.data.id, {
noPrint: !event.detail.print
});
},
hidden: event => {
const {
hidden
} = event.detail;
this.container.style.visibility = hidden ? "hidden" : "visible";
this.annotationStorage.setValue(this.data.id, {
noPrint: hidden,
noView: hidden
});
},
focus: event => {
setTimeout(() => event.target.focus({
preventScroll: false
}), 0);
},
userName: event => {
event.target.title = event.detail.userName;
},
readonly: event => {
event.target.disabled = event.detail.readonly;
},
required: event => {
this._setRequired(event.target, event.detail.required);
},
bgColor: event => {
setColor("bgColor", "backgroundColor", event);
},
fillColor: event => {
setColor("fillColor", "backgroundColor", event);
},
fgColor: event => {
setColor("fgColor", "color", event);
},
textColor: event => {
setColor("textColor", "color", event);
},
borderColor: event => {
setColor("borderColor", "borderColor", event);
},
strokeColor: event => {
setColor("strokeColor", "borderColor", event);
},
rotation: event => {
const angle = event.detail.rotation;
this.setRotation(angle);
this.annotationStorage.setValue(this.data.id, {
rotation: angle
});
}
});
}
_dispatchEventFromSandbox(actions, jsEvent) {
const commonActions = this._commonActions;
for (const name of Object.keys(jsEvent.detail)) {
const action = actions[name] || commonActions[name];
action?.(jsEvent);
}
}
_setDefaultPropertiesFromJS(element) {
if (!this.enableScripting) {
return;
}
const storedData = this.annotationStorage.getRawValue(this.data.id);
if (!storedData) {
return;
}
const commonActions = this._commonActions;
for (const [actionName, detail] of Object.entries(storedData)) {
const action = commonActions[actionName];
if (action) {
const eventProxy = {
detail: {
[actionName]: detail
},
target: element
};
action(eventProxy);
delete storedData[actionName];
}
}
}
_createQuadrilaterals() {
if (!this.container) {
return;
}
const {
quadPoints
} = this.data;
if (!quadPoints) {
return;
}
const [rectBlX, rectBlY, rectTrX, rectTrY] = this.data.rect.map(x => Math.fround(x));
if (quadPoints.length === 8) {
const [trX, trY, blX, blY] = quadPoints.subarray(2, 6);
if (rectTrX === trX && rectTrY === trY && rectBlX === blX && rectBlY === blY) {
return;
}
}
const {
style
} = this.container;
let svgBuffer;
if (this.#hasBorder) {
const {
borderColor,
borderWidth
} = style;
style.borderWidth = 0;
svgBuffer = ["url('data:image/svg+xml;utf8,", `<svg xmlns="http://www.w3.org/2000/svg"`, ` preserveAspectRatio="none" viewBox="0 0 1 1">`, `<g fill="transparent" stroke="${borderColor}" stroke-width="${borderWidth}">`];
this.container.classList.add("hasBorder");
}
const width = rectTrX - rectBlX;
const height = rectTrY - rectBlY;
const {
svgFactory
} = this;
const svg = svgFactory.createElement("svg");
svg.classList.add("quadrilateralsContainer");
svg.setAttribute("width", 0);
svg.setAttribute("height", 0);
const defs = svgFactory.createElement("defs");
svg.append(defs);
const clipPath = svgFactory.createElement("clipPath");
const id = `clippath_${this.data.id}`;
clipPath.setAttribute("id", id);
clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
defs.append(clipPath);
for (let i = 2, ii = quadPoints.length; i < ii; i += 8) {
const trX = quadPoints[i];
const trY = quadPoints[i + 1];
const blX = quadPoints[i + 2];
const blY = quadPoints[i + 3];
const rect = svgFactory.createElement("rect");
const x = (blX - rectBlX) / width;
const y = (rectTrY - trY) / height;
const rectWidth = (trX - blX) / width;
const rectHeight = (trY - blY) / height;
rect.setAttribute("x", x);
rect.setAttribute("y", y);
rect.setAttribute("width", rectWidth);
rect.setAttribute("height", rectHeight);
clipPath.append(rect);
svgBuffer?.push(`<rect vector-effect="non-scaling-stroke" x="${x}" y="${y}" width="${rectWidth}" height="${rectHeight}"/>`);
}
if (this.#hasBorder) {
svgBuffer.push(`</g></svg>')`);
style.backgroundImage = svgBuffer.join("");
}
this.container.append(svg);
this.container.style.clipPath = `url(#${id})`;
}
_createPopup() {
const {
data
} = this;
const popup = this.#popupElement = new PopupAnnotationElement({
data: {
color: data.color,
titleObj: data.titleObj,
modificationDate: data.modificationDate,
contentsObj: data.contentsObj,
richText: data.richText,
parentRect: data.rect,
borderStyle: 0,
id: `popup_${data.id}`,
rotation: data.rotation
},
parent: this.parent,
elements: [this]
});
this.parent.div.append(popup.render());
}
render() {
(0,util.unreachable)("Abstract method `AnnotationElement.render` called");
}
_getElementsByName(name, skipId = null) {
const fields = [];
if (this._fieldObjects) {
const fieldObj = this._fieldObjects[name];
if (fieldObj) {
for (const {
page,
id,
exportValues
} of fieldObj) {
if (page === -1) {
continue;
}
if (id === skipId) {
continue;
}
const exportValue = typeof exportValues === "string" ? exportValues : null;
const domElement = document.querySelector(`[data-element-id="${id}"]`);
if (domElement && !GetElementsByNameSet.has(domElement)) {
(0,util.warn)(`_getElementsByName - element not allowed: ${id}`);
continue;
}
fields.push({
id,
exportValue,
domElement
});
}
}
return fields;
}
for (const domElement of document.getElementsByName(name)) {
const {
exportValue
} = domElement;
const id = domElement.getAttribute("data-element-id");
if (id === skipId) {
continue;
}
if (!GetElementsByNameSet.has(domElement)) {
continue;
}
fields.push({
id,
exportValue,
domElement
});
}
return fields;
}
show() {
if (this.container) {
this.container.hidden = false;
}
this.popup?.maybeShow();
}
hide() {
if (this.container) {
this.container.hidden = true;
}
this.popup?.forceHide();
}
getElementsToTriggerPopup() {
return this.container;
}
addHighlightArea() {
const triggers = this.getElementsToTriggerPopup();
if (Array.isArray(triggers)) {
for (const element of triggers) {
element.classList.add("highlightArea");
}
} else {
triggers.classList.add("highlightArea");
}
}
_editOnDoubleClick() {
if (!this._isEditable) {
return;
}
const {
annotationEditorType: mode,
data: {
id: editId
}
} = this;
this.container.addEventListener("dblclick", () => {
this.linkService.eventBus?.dispatch("switchannotationeditormode", {
source: this,
mode,
editId
});
});
}
}
class LinkAnnotationElement extends AnnotationElement {
constructor(parameters, options = null) {
super(parameters, {
isRenderable: true,
ignoreBorder: !!options?.ignoreBorder,
createQuadrilaterals: true
});
this.isTooltipOnly = parameters.data.isTooltipOnly;
}
render() {
const {
data,
linkService
} = this;
const link = document.createElement("a");
link.setAttribute("data-element-id", data.id);
let isBound = false;
if (data.url) {
linkService.addLinkAttributes(link, data.url, data.newWindow);
isBound = true;
} else if (data.action) {
this._bindNamedAction(link, data.action);
isBound = true;
} else if (data.attachment) {
this.#bindAttachment(link, data.attachment, data.attachmentDest);
isBound = true;
} else if (data.setOCGState) {
this.#bindSetOCGState(link, data.setOCGState);
isBound = true;
} else if (data.dest) {
this._bindLink(link, data.dest);
isBound = true;
} else {
if (data.actions && (data.actions.Action || data.actions["Mouse Up"] || data.actions["Mouse Down"]) && this.enableScripting && this.hasJSActions) {
this._bindJSAction(link, data);
isBound = true;
}
if (data.resetForm) {
this._bindResetFormAction(link, data.resetForm);
isBound = true;
} else if (this.isTooltipOnly && !isBound) {
this._bindLink(link, "");
isBound = true;
}
}
this.container.classList.add("linkAnnotation");
if (isBound) {
this.container.append(link);
}
return this.container;
}
#setInternalLink() {
this.container.setAttribute("data-internal-link", "");
}
_bindLink(link, destination) {
link.href = this.linkService.getDestinationHash(destination);
link.onclick = () => {
if (destination) {
this.linkService.goToDestination(destination);
}
return false;
};
if (destination || destination === "") {
this.#setInternalLink();
}
}
_bindNamedAction(link, action) {
link.href = this.linkService.getAnchorUrl("");
link.onclick = () => {
this.linkService.executeNamedAction(action);
return false;
};
this.#setInternalLink();
}
#bindAttachment(link, attachment, dest = null) {
link.href = this.linkService.getAnchorUrl("");
if (attachment.description) {
link.title = attachment.description;
}
link.onclick = () => {
this.downloadManager?.openOrDownloadData(attachment.content, attachment.filename, dest);
return false;
};
this.#setInternalLink();
}
#bindSetOCGState(link, action) {
link.href = this.linkService.getAnchorUrl("");
link.onclick = () => {
this.linkService.executeSetOCGState(action);
return false;
};
this.#setInternalLink();
}
_bindJSAction(link, data) {
link.href = this.linkService.getAnchorUrl("");
const map = new Map([["Action", "onclick"], ["Mouse Up", "onmouseup"], ["Mouse Down", "onmousedown"]]);
for (const name of Object.keys(data.actions)) {
const jsName = map.get(name);
if (!jsName) {
continue;
}
link[jsName] = () => {
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id: data.id,
name
}
});
return false;
};
}
if (!link.onclick) {
link.onclick = () => false;
}
this.#setInternalLink();
}
_bindResetFormAction(link, resetForm) {
const otherClickAction = link.onclick;
if (!otherClickAction) {
link.href = this.linkService.getAnchorUrl("");
}
this.#setInternalLink();
if (!this._fieldObjects) {
(0,util.warn)(`_bindResetFormAction - "resetForm" action not supported, ` + "ensure that the `fieldObjects` parameter is provided.");
if (!otherClickAction) {
link.onclick = () => false;
}
return;
}
link.onclick = () => {
otherClickAction?.();
const {
fields: resetFormFields,
refs: resetFormRefs,
include
} = resetForm;
const allFields = [];
if (resetFormFields.length !== 0 || resetFormRefs.length !== 0) {
const fieldIds = new Set(resetFormRefs);
for (const fieldName of resetFormFields) {
const fields = this._fieldObjects[fieldName] || [];
for (const {
id
} of fields) {
fieldIds.add(id);
}
}
for (const fields of Object.values(this._fieldObjects)) {
for (const field of fields) {
if (fieldIds.has(field.id) === include) {
allFields.push(field);
}
}
}
} else {
for (const fields of Object.values(this._fieldObjects)) {
allFields.push(...fields);
}
}
const storage = this.annotationStorage;
const allIds = [];
for (const field of allFields) {
const {
id
} = field;
allIds.push(id);
switch (field.type) {
case "text":
{
const value = field.defaultValue || "";
storage.setValue(id, {
value
});
window.updateAngularFormValue(id, {
value
});
break;
}
case "checkbox":
case "radiobutton":
{
const value = field.defaultValue === field.exportValues;
storage.setValue(id, {
value
});
window.updateAngularFormValue(id, {
value
});
break;
}
case "combobox":
case "listbox":
{
const value = field.defaultValue || "";
storage.setValue(id, {
value
});
window.updateAngularFormValue(id, {
value
});
break;
}
default:
continue;
}
const domElement = document.querySelector(`[data-element-id="${id}"]`);
if (!domElement) {
continue;
} else if (!GetElementsByNameSet.has(domElement)) {
(0,util.warn)(`_bindResetFormAction - element not allowed: ${id}`);
continue;
}
domElement.dispatchEvent(new Event("resetform"));
}
if (this.enableScripting) {
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id: "app",
ids: allIds,
name: "ResetForm"
}
});
}
return false;
};
}
}
class TextAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true
});
}
render() {
this.container.classList.add("textAnnotation");
const image = document.createElement("img");
image.src = this.imageResourcesPath + "annotation-" + this.data.name.toLowerCase() + ".svg";
image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type");
image.setAttribute("data-l10n-args", JSON.stringify({
type: this.data.name
}));
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this.container.append(image);
return this.container;
}
}
class WidgetAnnotationElement extends AnnotationElement {
render() {
return this.container;
}
showElementAndHideCanvas(element) {
if (this.data.hasOwnCanvas) {
if (element.previousSibling?.nodeName === "CANVAS") {
element.previousSibling.hidden = true;
}
element.hidden = false;
}
}
_getKeyModifier(event) {
return util.FeatureTest.platform.isMac ? event.metaKey : event.ctrlKey;
}
_setEventListener(element, elementData, baseName, eventName, valueGetter) {
if (baseName.includes("mouse")) {
element.addEventListener(baseName, event => {
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id: this.data.id,
name: eventName,
value: valueGetter(event),
shift: event.shiftKey,
modifier: this._getKeyModifier(event)
}
});
});
} else {
element.addEventListener(baseName, event => {
if (baseName === "blur") {
if (!elementData.focused) {
return;
}
elementData.focused = false;
} else if (baseName === "focus") {
if (elementData.focused) {
return;
}
elementData.focused = true;
}
if (!valueGetter) {
return;
}
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id: this.data.id,
name: eventName,
value: valueGetter(event)
}
});
});
}
}
_setEventListeners(element, elementData, names, getter) {
for (const [baseName, eventName] of names) {
if (eventName === "Action" || this.data.actions?.[eventName]) {
if (eventName === "Focus" || eventName === "Blur") {
elementData ||= {
focused: false
};
}
this._setEventListener(element, elementData, baseName, eventName, getter);
if (eventName === "Focus" && !this.data.actions?.Blur) {
this._setEventListener(element, elementData, "blur", "Blur", null);
} else if (eventName === "Blur" && !this.data.actions?.Focus) {
this._setEventListener(element, elementData, "focus", "Focus", null);
}
}
}
}
_setBackgroundColor(element) {
const color = this.data.backgroundColor || null;
element.style.backgroundColor = color === null ? "transparent" : util.Util.makeHexColor(color[0], color[1], color[2]);
}
_setTextStyle(element) {
const TEXT_ALIGNMENT = ["left", "center", "right"];
const {
fontColor
} = this.data.defaultAppearanceData;
const fontSize = this.data.defaultAppearanceData.fontSize || DEFAULT_FONT_SIZE;
const style = element.style;
let computedFontSize;
const BORDER_SIZE = 2;
const roundToOneDecimal = x => Math.round(10 * x) / 10;
if (this.data.multiLine) {
const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE);
const numberOfLines = Math.round(height / (util.LINE_FACTOR * fontSize)) || 1;
const lineHeight = height / numberOfLines;
computedFontSize = Math.min(fontSize, roundToOneDecimal(lineHeight / util.LINE_FACTOR));
} else {
const height = Math.abs(this.data.rect[3] - this.data.rect[1] - BORDER_SIZE);
computedFontSize = Math.min(fontSize, roundToOneDecimal(height / util.LINE_FACTOR));
}
style.fontSize = `calc(${computedFontSize}px * var(--scale-factor))`;
style.color = util.Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]);
if (this.data.textAlignment !== null) {
style.textAlign = TEXT_ALIGNMENT[this.data.textAlignment];
}
}
_setRequired(element, isRequired) {
if (isRequired) {
element.setAttribute("required", true);
} else {
element.removeAttribute("required");
}
element.setAttribute("aria-required", isRequired);
}
}
class TextWidgetAnnotationElement extends WidgetAnnotationElement {
constructor(parameters) {
const isRenderable = parameters.renderForms || parameters.data.hasOwnCanvas || !parameters.data.hasAppearance && !!parameters.data.fieldValue;
super(parameters, {
isRenderable
});
}
setPropertyOnSiblings(base, key, value, keyInStorage) {
const storage = this.annotationStorage;
for (const element of this._getElementsByName(base.name, base.id)) {
if (element.domElement) {
element.domElement[key] = value;
}
storage.setValue(element.id, {
[keyInStorage]: value
});
}
}
render() {
const storage = this.annotationStorage;
const id = this.data.id;
this.container.classList.add("textWidgetAnnotation");
let element = null;
if (this.renderForms) {
const angularData = window.getFormValueFromAngular(this.data.fieldName);
const formData = storage.getValue(id, {
value: this.data.fieldValue
});
const storedData = angularData.value ? angularData : formData;
if (angularData !== formData) {
storage.setValue(id, {
value: angularData.value
});
storedData.formattedValue = angularData.value;
}
let textContent = storedData.value || "";
const maxLen = storage.getValue(id, {
charLimit: this.data.maxLen
}).charLimit;
if (maxLen && textContent.length > maxLen) {
textContent = textContent.slice(0, maxLen);
}
let fieldFormattedValues = storedData.formattedValue || this.data.textContent?.join("\n") || null;
if (fieldFormattedValues && this.data.comb) {
fieldFormattedValues = fieldFormattedValues.replaceAll(/\s+/g, "");
}
const elementData = {
userValue: textContent,
formattedValue: fieldFormattedValues,
lastCommittedValue: null,
commitKey: 1,
focused: false
};
if (this.data.multiLine) {
element = document.createElement("textarea");
element.textContent = fieldFormattedValues ?? textContent;
if (this.data.doNotScroll) {
element.style.overflowY = "hidden";
}
} else {
element = document.createElement("input");
element.type = "text";
element.setAttribute("value", fieldFormattedValues ?? textContent);
if (this.data.doNotScroll) {
element.style.overflowX = "hidden";
}
}
if (this.data.hasOwnCanvas) {
element.hidden = true;
}
GetElementsByNameSet.add(element);
element.setAttribute("data-element-id", id);
element.disabled = this.data.readOnly;
element.name = this.data.fieldName;
element.tabIndex = DEFAULT_TAB_INDEX;
this._setRequired(element, this.data.required);
if (maxLen) {
element.maxLength = maxLen;
}
element.addEventListener("input", event => {
storage.setValue(id, {
value: event.target.value
});
window.updateAngularFormValue(id, {
value: event.target.value
});
this.setPropertyOnSiblings(element, "value", event.target.value, "value");
elementData.formattedValue = null;
});
element.addEventListener("resetform", event => {
const defaultValue = this.data.defaultFieldValue ?? "";
element.value = elementData.userValue = defaultValue;
elementData.formattedValue = null;
});
let blurListener = event => {
const {
formattedValue
} = elementData;
if (formattedValue !== null && formattedValue !== undefined) {
event.target.value = formattedValue;
}
event.target.scrollLeft = 0;
};
window.registerAcroformField(id, element, storedData.value, undefined, this.data.fieldValue);
element.addEventListener("updateFromAngular", newvalue => {
elementData.formattedValue = null;
storage.setValue(id, {
value: newvalue.detail
});
});
if (this.enableScripting && this.hasJSActions) {
element.addEventListener("focus", event => {
if (elementData.focused) {
return;
}
const {
target
} = event;
if (elementData.userValue) {
target.value = elementData.userValue;
}
elementData.lastCommittedValue = target.value;
elementData.commitKey = 1;
if (!this.data.actions?.Focus) {
elementData.focused = true;
}
});
element.addEventListener("updatefromsandbox", jsEvent => {
this.showElementAndHideCanvas(jsEvent.target);
const actions = {
value(event) {
elementData.userValue = event.detail.value ?? "";
storage.setValue(id, {
value: elementData.userValue.toString()
});
window.updateAngularFormValue(id, {
value: elementData.userValue.toString()
});
event.target.value = elementData.userValue;
},
formattedValue(event) {
const {
formattedValue
} = event.detail;
elementData.formattedValue = formattedValue;
if (formattedValue !== null && formattedValue !== undefined && event.target !== document.activeElement) {
event.target.value = formattedValue;
}
storage.setValue(id, {
formattedValue
});
window.updateAngularFormValue(id, {
formattedValue
});
},
selRange(event) {
event.target.setSelectionRange(...event.detail.selRange);
},
charLimit: event => {
const {
charLimit
} = event.detail;
const {
target
} = event;
if (charLimit === 0) {
target.removeAttribute("maxLength");
return;
}
target.setAttribute("maxLength", charLimit);
let value = elementData.userValue;
if (!value || value.length <= charLimit) {
return;
}
value = value.slice(0, charLimit);
target.value = elementData.userValue = value;
storage.setValue(id, {
value
});
window.updateAngularFormValue(id, {
value
});
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id,
name: "Keystroke",
value,
willCommit: true,
commitKey: 1,
selStart: target.selectionStart,
selEnd: target.selectionEnd
}
});
}
};
this._dispatchEventFromSandbox(actions, jsEvent);
});
element.addEventListener("keydown", event => {
elementData.commitKey = 1;
let commitKey = -1;
if (event.key === "Escape") {
commitKey = 0;
} else if (event.key === "Enter" && !this.data.multiLine) {
commitKey = 2;
} else if (event.key === "Tab") {
elementData.commitKey = 3;
}
if (commitKey === -1) {
return;
}
const {
value
} = event.target;
if (elementData.lastCommittedValue === value) {
return;
}
elementData.lastCommittedValue = value;
elementData.userValue = value;
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id,
name: "Keystroke",
value,
willCommit: true,
commitKey,
selStart: event.target.selectionStart,
selEnd: event.target.selectionEnd
}
});
});
const _blurListener = blurListener;
blurListener = null;
element.addEventListener("blur", event => {
if (!elementData.focused) {
return;
}
if (!this.data.actions?.Blur) {
elementData.focused = false;
}
const {
value
} = event.target;
elementData.userValue = value;
if (elementData.lastCommittedValue !== value) {
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id,
name: "Keystroke",
value,
willCommit: true,
commitKey: elementData.commitKey,
selStart: event.target.selectionStart,
selEnd: event.target.selectionEnd
}
});
}
_blurListener(event);
});
if (this.data.actions?.Keystroke) {
element.addEventListener("beforeinput", event => {
elementData.lastCommittedValue = null;
const {
data,
target
} = event;
const {
value,
selectionStart,
selectionEnd
} = target;
let selStart = selectionStart,
selEnd = selectionEnd;
switch (event.inputType) {
case "deleteWordBackward":
{
const match = value.substring(0, selectionStart).match(/\w*[^\w]*$/);
if (match) {
selStart -= match[0].length;
}
break;
}
case "deleteWordForward":
{
const match = value.substring(selectionStart).match(/^[^\w]*\w*/);
if (match) {
selEnd += match[0].length;
}
break;
}
case "deleteContentBackward":
if (selectionStart === selectionEnd) {
selStart -= 1;
}
break;
case "deleteContentForward":
if (selectionStart === selectionEnd) {
selEnd += 1;
}
break;
}
event.preventDefault();
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id,
name: "Keystroke",
value,
change: data || "",
willCommit: false,
selStart,
selEnd
}
});
});
}
this._setEventListeners(element, elementData, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.value);
}
if (blurListener) {
element.addEventListener("blur", blurListener);
}
if (this.data.comb) {
const fieldWidth = this.data.rect[2] - this.data.rect[0];
const combWidth = fieldWidth / maxLen;
element.classList.add("comb");
element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`;
}
} else {
element = document.createElement("div");
element.textContent = this.data.fieldValue;
element.style.verticalAlign = "middle";
element.style.display = "table-cell";
if (this.data.hasOwnCanvas) {
element.hidden = true;
}
}
this._setTextStyle(element);
this._setBackgroundColor(element);
this._setDefaultPropertiesFromJS(element);
this.container.append(element);
return this.container;
}
}
class SignatureWidgetAnnotationElement extends WidgetAnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: !!parameters.data.hasOwnCanvas
});
}
}
class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: parameters.renderForms
});
}
render() {
const storage = this.annotationStorage;
const data = this.data;
const id = data.id;
const angularData = window.getFormValueFromAngular(this.data.fieldName);
const formValue = storage.getValue(id, {
value: data.exportValue === data.fieldValue
}).value;
let angularValue = undefined;
if (angularData.value) {
angularValue = angularData.value === true || angularData.value === data.exportValue;
}
let value = angularValue !== undefined ? angularValue : formValue;
let updateAngularValueNecessary = false;
if (typeof value === "string") {
value = value === data.exportValue;
storage.setValue(id, {
value
});
updateAngularValueNecessary = true;
} else if (angularData?.value !== undefined && angularData.value !== formValue) {
const isChecked = angularData.value === true || angularData.value === data.exportValue;
value = isChecked ? data.exportValue : undefined;
storage.setValue(id, {
value
});
}
this.container.classList.add("buttonWidgetAnnotation", "checkBox");
const element = document.createElement("input");
GetElementsByNameSet.add(element);
element.setAttribute("data-element-id", id);
element.disabled = data.readOnly;
this._setRequired(element, this.data.required);
element.type = "checkbox";
element.name = data.fieldName;
if (value) {
element.setAttribute("checked", true);
}
element.setAttribute("exportValue", data.exportValue);
element.tabIndex = DEFAULT_TAB_INDEX;
element.addEventListener("change", event => {
const {
name,
checked
} = event.target;
for (const checkbox of this._getElementsByName(name, id)) {
const curChecked = checked && checkbox.exportValue === data.exportValue;
if (checkbox.domElement) {
checkbox.domElement.checked = curChecked;
}
storage.setValue(checkbox.id, {
value: curChecked
});
window.updateAngularFormValue(id, {
value: curChecked
});
}
storage.setValue(id, {
value: checked
});
window.updateAngularFormValue(id, {
value: checked
});
});
element.addEventListener("resetform", event => {
const defaultValue = data.defaultFieldValue || "Off";
event.target.checked = defaultValue === data.exportValue;
});
window.registerAcroformField(id, element, value ? data.exportValue : undefined, undefined, this.data.fieldValue);
element.addEventListener("updateFromAngular", newvalue => storage.setValue(id, {
value: newvalue.detail
}));
if (updateAngularValueNecessary) {
window.updateAngularFormValue(id, {
value
});
}
if (this.enableScripting && this.hasJSActions) {
element.addEventListener("updatefromsandbox", jsEvent => {
const actions = {
value(event) {
event.target.checked = event.detail.value !== "Off";
storage.setValue(id, {
value: event.target.checked
});
window.updateAngularFormValue(id, {
value: event.target.value
});
}
};
this._dispatchEventFromSandbox(actions, jsEvent);
});
this._setEventListeners(element, null, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked);
}
this._setBackgroundColor(element);
this._setDefaultPropertiesFromJS(element);
this.container.append(element);
return this.container;
}
}
class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: parameters.renderForms
});
}
render() {
this.container.classList.add("buttonWidgetAnnotation", "radioButton");
const storage = this.annotationStorage;
const data = this.data;
const id = data.id;
const angularData = window.getFormValueFromAngular(this.data.fieldName);
const defaultValue = data.fieldValue === data.buttonValue ? data.buttonValue : undefined;
const formValue = storage.getValue(id, {
value: defaultValue
}).value;
let value = angularData.value ?? formValue;
if (typeof value === "string" || angularData !== formValue) {
value = value === data.buttonValue;
storage.setValue(id, {
value
});
} else if (value) {
window.updateAngularFormValue(id, {
value: data.buttonValue
});
}
if (value) {
for (const radio of this._getElementsByName(data.fieldName, id)) {
storage.setValue(radio.id, {
value: false
});
}
}
const element = document.createElement("input");
GetElementsByNameSet.add(element);
element.setAttribute("data-element-id", id);
element.disabled = data.readOnly;
this._setRequired(element, this.data.required);
element.type = "radio";
element.name = data.fieldName;
if (value) {
element.setAttribute("checked", true);
}
element.tabIndex = DEFAULT_TAB_INDEX;
element.addEventListener("change", event => {
const {
name,
checked
} = event.target;
for (const radio of this._getElementsByName(name, id)) {
storage.setValue(radio.id, {
value: false
});
window.updateAngularFormValue(radio.id, {
value: false
});
}
storage.setValue(id, {
value: checked
});
window.updateAngularFormValue(id, {
value: checked
});
});
element.addEventListener("resetform", event => {
const defaultValue = data.defaultFieldValue;
event.target.checked = defaultValue !== null && defaultValue !== undefined && defaultValue === data.buttonValue;
});
window.registerAcroformField(id, element, value ? data.buttonValue : undefined, data.buttonValue, this.data.fieldValue);
element.addEventListener("updateFromAngular", newvalue => storage.setValue(id, {
value: newvalue.detail
}));
if (this.enableScripting && this.hasJSActions) {
const pdfButtonValue = data.buttonValue;
element.addEventListener("updatefromsandbox", jsEvent => {
const actions = {
value: event => {
const checked = pdfButtonValue === event.detail.value;
for (const radio of this._getElementsByName(event.target.name)) {
const curChecked = checked && radio.id === id;
if (radio.domElement) {
radio.domElement.checked = curChecked;
}
storage.setValue(radio.id, {
value: curChecked
});
window.updateAngularFormValue(id, {
value: curChecked
});
}
}
};
this._dispatchEventFromSandbox(actions, jsEvent);
});
this._setEventListeners(element, null, [["change", "Validate"], ["change", "Action"], ["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"]], event => event.target.checked);
}
this._setBackgroundColor(element);
this._setDefaultPropertiesFromJS(element);
this.container.append(element);
return this.container;
}
}
class PushButtonWidgetAnnotationElement extends LinkAnnotationElement {
constructor(parameters) {
super(parameters, {
ignoreBorder: parameters.data.hasAppearance
});
}
render() {
const container = super.render();
container.classList.add("buttonWidgetAnnotation", "pushButton");
const linkElement = container.lastChild;
if (this.enableScripting && this.hasJSActions && linkElement) {
this._setDefaultPropertiesFromJS(linkElement);
linkElement.addEventListener("updatefromsandbox", jsEvent => {
this._dispatchEventFromSandbox({}, jsEvent);
});
}
return container;
}
}
class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: parameters.renderForms
});
}
render() {
this.container.classList.add("choiceWidgetAnnotation");
const storage = this.annotationStorage;
const id = this.data.id;
const angularData = window.getFormValueFromAngular(this.data.fieldName);
const formData = storage.getValue(id, {
value: this.data.fieldValue
});
const storedData = angularData.value ? angularData : formData;
if (angularData !== formData) {
storage.setValue(id, {
value: angularData.value
});
}
const selectElement = document.createElement("select");
GetElementsByNameSet.add(selectElement);
selectElement.setAttribute("data-element-id", id);
selectElement.disabled = this.data.readOnly;
this._setRequired(selectElement, this.data.required);
selectElement.name = this.data.fieldName;
selectElement.tabIndex = DEFAULT_TAB_INDEX;
let addAnEmptyEntry = this.data.combo && this.data.options.length > 0;
if (!this.data.combo) {
selectElement.size = this.data.options.length;
if (this.data.multiSelect) {
selectElement.multiple = true;
}
}
selectElement.addEventListener("resetform", event => {
const defaultValue = this.data.defaultFieldValue;
for (const option of selectElement.options) {
option.selected = option.value === defaultValue;
}
});
for (const option of this.data.options) {
const optionElement = document.createElement("option");
optionElement.textContent = option.displayValue;
optionElement.value = option.exportValue;
if (storedData.value.includes(option.exportValue)) {
optionElement.setAttribute("selected", true);
addAnEmptyEntry = false;
}
selectElement.append(optionElement);
}
let removeEmptyEntry = null;
if (addAnEmptyEntry) {
const noneOptionElement = document.createElement("option");
noneOptionElement.value = " ";
noneOptionElement.setAttribute("hidden", true);
noneOptionElement.setAttribute("selected", true);
selectElement.prepend(noneOptionElement);
removeEmptyEntry = () => {
noneOptionElement.remove();
selectElement.removeEventListener("input", removeEmptyEntry);
removeEmptyEntry = null;
};
selectElement.addEventListener("input", removeEmptyEntry);
}
const getValue = isExport => {
const name = isExport ? "value" : "textContent";
const {
options,
multiple
} = selectElement;
if (!multiple) {
return options.selectedIndex === -1 ? null : options[options.selectedIndex][name];
}
return Array.prototype.filter.call(options, option => option.selected).map(option => option[name]);
};
let selectedValues = getValue(false);
const getItems = event => {
const options = event.target.options;
return Array.prototype.map.call(options, option => ({
displayValue: option.textContent,
exportValue: option.value
}));
};
window.registerAcroformField(id, selectElement, selectedValues, undefined, this.data.fieldValue);
selectElement.addEventListener("updateFromAngular", newvalue => storage.setValue(id, {
value: newvalue.detail
}));
if (this.enableScripting && this.hasJSActions) {
selectElement.addEventListener("updatefromsandbox", jsEvent => {
const actions = {
value(event) {
removeEmptyEntry?.();
const value = event.detail.value;
const values = new Set(Array.isArray(value) ? value : [value]);
for (const option of selectElement.options) {
option.selected = values.has(option.value);
}
storage.setValue(id, {
value: getValue(true)
});
selectedValues = getValue(false);
window.updateAngularFormValue(id, {
value: selectedValues
});
},
multipleSelection(event) {
selectElement.multiple = true;
},
remove(event) {
const options = selectElement.options;
const index = event.detail.remove;
options[index].selected = false;
selectElement.remove(index);
if (options.length > 0) {
const i = Array.prototype.findIndex.call(options, option => option.selected);
if (i === -1) {
options[0].selected = true;
}
}
storage.setValue(id, {
value: getValue(true),
items: getItems(event)
});
selectedValues = getValue(false);
},
clear(event) {
while (selectElement.length !== 0) {
selectElement.remove(0);
}
storage.setValue(id, {
value: null,
items: []
});
selectedValues = getValue(false);
window.updateAngularFormValue(id, {
value: selectedValues
});
},
insert(event) {
const {
index,
displayValue,
exportValue
} = event.detail.insert;
const selectChild = selectElement.children[index];
const optionElement = document.createElement("option");
optionElement.textContent = displayValue;
optionElement.value = exportValue;
if (selectChild) {
selectChild.before(optionElement);
} else {
selectElement.append(optionElement);
}
storage.setValue(id, {
value: getValue(true),
items: getItems(event)
});
selectedValues = getValue(false);
window.updateAngularFormValue(id, {
value: selectedValues
});
},
items(event) {
const {
items
} = event.detail;
while (selectElement.length !== 0) {
selectElement.remove(0);
}
for (const item of items) {
const {
displayValue,
exportValue
} = item;
const optionElement = document.createElement("option");
optionElement.textContent = displayValue;
optionElement.value = exportValue;
selectElement.append(optionElement);
}
if (selectElement.options.length > 0) {
selectElement.options[0].selected = true;
}
storage.setValue(id, {
value: getValue(true),
items: getItems(event)
});
selectedValues = getValue(false);
window.updateAngularFormValue(id, {
value: selectedValues
});
},
indices(event) {
const indices = new Set(event.detail.indices);
for (const option of event.target.options) {
option.selected = indices.has(option.index);
}
storage.setValue(id, {
value: getValue(true)
});
selectedValues = getValue(false);
window.updateAngularFormValue(id, {
value: selectedValues
});
},
editable(event) {
event.target.disabled = !event.detail.editable;
}
};
this._dispatchEventFromSandbox(actions, jsEvent);
});
selectElement.addEventListener("input", event => {
const exportValue = getValue(true);
const change = getValue(false);
storage.setValue(id, {
value: exportValue
});
window.updateAngularFormValue(id, {
value: exportValue
});
event.preventDefault();
this.linkService.eventBus?.dispatch("dispatcheventinsandbox", {
source: this,
detail: {
id,
name: "Keystroke",
value: selectedValues,
change,
changeEx: exportValue,
willCommit: false,
commitKey: 1,
keyDown: false
}
});
});
this._setEventListeners(selectElement, null, [["focus", "Focus"], ["blur", "Blur"], ["mousedown", "Mouse Down"], ["mouseenter", "Mouse Enter"], ["mouseleave", "Mouse Exit"], ["mouseup", "Mouse Up"], ["input", "Action"], ["input", "Validate"]], event => event.target.value);
} else {
selectElement.addEventListener("input", function (event) {
storage.setValue(id, {
value: getValue(true)
});
window.updateAngularFormValue(id, {
value: getValue(true)
});
});
}
if (this.data.combo) {
this._setTextStyle(selectElement);
} else {}
this._setBackgroundColor(selectElement);
this._setDefaultPropertiesFromJS(selectElement);
this.container.append(selectElement);
return this.container;
}
}
class PopupAnnotationElement extends AnnotationElement {
constructor(parameters) {
const {
data,
elements
} = parameters;
super(parameters, {
isRenderable: AnnotationElement._hasPopupData(data)
});
this.elements = elements;
this.popup = null;
}
render() {
this.container.classList.add("popupAnnotation");
const popup = this.popup = new PopupElement({
container: this.container,
color: this.data.color,
titleObj: this.data.titleObj,
modificationDate: this.data.modificationDate,
contentsObj: this.data.contentsObj,
richText: this.data.richText,
rect: this.data.rect,
parentRect: this.data.parentRect || null,
parent: this.parent,
elements: this.elements,
open: this.data.open
});
const elementIds = [];
for (const element of this.elements) {
element.popup = popup;
element.container.ariaHasPopup = "dialog";
elementIds.push(element.data.id);
element.addHighlightArea();
}
this.container.setAttribute("aria-controls", elementIds.map(id => `${util.AnnotationPrefix}${id}`).join(","));
return this.container;
}
}
class PopupElement {
#boundKeyDown = this.#keyDown.bind(this);
#boundHide = this.#hide.bind(this);
#boundShow = this.#show.bind(this);
#boundToggle = this.#toggle.bind(this);
#color = null;
#container = null;
#contentsObj = null;
#dateObj = null;
#elements = null;
#parent = null;
#parentRect = null;
#pinned = false;
#popup = null;
#position = null;
#rect = null;
#richText = null;
#titleObj = null;
#updates = null;
#wasVisible = false;
constructor({
container,
color,
elements,
titleObj,
modificationDate,
contentsObj,
richText,
parent,
rect,
parentRect,
open
}) {
this.#container = container;
this.#titleObj = titleObj;
this.#contentsObj = contentsObj;
this.#richText = richText;
this.#parent = parent;
this.#color = color;
this.#rect = rect;
this.#parentRect = parentRect;
this.#elements = elements;
this.#dateObj = display_utils.PDFDateString.toDateObject(modificationDate);
this.trigger = elements.flatMap(e => e.getElementsToTriggerPopup());
for (const element of this.trigger) {
element.addEventListener("click", this.#boundToggle);
element.addEventListener("mouseenter", this.#boundShow);
element.addEventListener("mouseleave", this.#boundHide);
element.classList.add("popupTriggerArea");
}
for (const element of elements) {
element.container?.addEventListener("keydown", this.#boundKeyDown);
}
this.#container.hidden = true;
if (open) {
this.#toggle();
}
}
render() {
if (this.#popup) {
return;
}
const popup = this.#popup = document.createElement("div");
popup.className = "popup";
if (this.#color) {
const baseColor = popup.style.outlineColor = util.Util.makeHexColor(...this.#color);
if (CSS.supports("background-color", "color-mix(in srgb, red 30%, white)")) {
popup.style.backgroundColor = `color-mix(in srgb, ${baseColor} 30%, white)`;
} else {
const BACKGROUND_ENLIGHT = 0.7;
popup.style.backgroundColor = util.Util.makeHexColor(...this.#color.map(c => Math.floor(BACKGROUND_ENLIGHT * (255 - c) + c)));
}
}
const header = document.createElement("span");
header.className = "header";
const title = document.createElement("h1");
header.append(title);
({
dir: title.dir,
str: title.textContent
} = this.#titleObj);
popup.append(header);
if (this.#dateObj) {
const modificationDate = document.createElement("span");
modificationDate.classList.add("popupDate");
modificationDate.setAttribute("data-l10n-id", "pdfjs-annotation-date-time-string");
modificationDate.setAttribute("data-l10n-args", JSON.stringify({
dateObj: this.#dateObj.valueOf()
}));
header.append(modificationDate);
}
const html = this.#html;
if (html) {
XfaLayer.render({
xfaHtml: html,
intent: "richText",
div: popup
});
popup.lastChild.classList.add("richText", "popupContent");
} else {
const contents = this._formatContents(this.#contentsObj);
popup.append(contents);
}
this.#container.append(popup);
}
get #html() {
const richText = this.#richText;
const contentsObj = this.#contentsObj;
if (richText?.str && (!contentsObj?.str || contentsObj.str === richText.str)) {
return this.#richText.html || null;
}
return null;
}
get #fontSize() {
return this.#html?.attributes?.style?.fontSize || 0;
}
get #fontColor() {
return this.#html?.attributes?.style?.color || null;
}
#makePopupContent(text) {
const popupLines = [];
const popupContent = {
str: text,
html: {
name: "div",
attributes: {
dir: "auto"
},
children: [{
name: "p",
children: popupLines
}]
}
};
const lineAttributes = {
style: {
color: this.#fontColor,
fontSize: this.#fontSize ? `calc(${this.#fontSize}px * var(--scale-factor))` : ""
}
};
for (const line of text.split("\n")) {
popupLines.push({
name: "span",
value: line,
attributes: lineAttributes
});
}
return popupContent;
}
_formatContents({
str,
dir
}) {
const p = document.createElement("p");
p.classList.add("popupContent");
p.dir = dir;
const lines = str.split(/(?:\r\n?|\n)/);
for (let i = 0, ii = lines.length; i < ii; ++i) {
const line = lines[i];
p.append(document.createTextNode(line));
if (i < ii - 1) {
p.append(document.createElement("br"));
}
}
return p;
}
#keyDown(event) {
if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
return;
}
if (event.key === "Enter" || event.key === "Escape" && this.#pinned) {
this.#toggle();
}
}
updateEdited({
rect,
popupContent
}) {
this.#updates ||= {
contentsObj: this.#contentsObj,
richText: this.#richText
};
if (rect) {
this.#position = null;
}
if (popupContent) {
this.#richText = this.#makePopupContent(popupContent);
this.#contentsObj = null;
}
this.#popup?.remove();
this.#popup = null;
}
resetEdited() {
if (!this.#updates) {
return;
}
({
contentsObj: this.#contentsObj,
richText: this.#richText
} = this.#updates);
this.#updates = null;
this.#popup?.remove();
this.#popup = null;
this.#position = null;
}
#setPosition() {
if (this.#position !== null) {
return;
}
const {
page: {
view
},
viewport: {
rawDims: {
pageWidth,
pageHeight,
pageX,
pageY
}
}
} = this.#parent;
let useParentRect = !!this.#parentRect;
let rect = useParentRect ? this.#parentRect : this.#rect;
for (const element of this.#elements) {
if (!rect || util.Util.intersect(element.data.rect, rect) !== null) {
rect = element.data.rect;
useParentRect = true;
break;
}
}
const normalizedRect = util.Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]);
const HORIZONTAL_SPACE_AFTER_ANNOTATION = 5;
const parentWidth = useParentRect ? rect[2] - rect[0] + HORIZONTAL_SPACE_AFTER_ANNOTATION : 0;
const popupLeft = normalizedRect[0] + parentWidth;
const popupTop = normalizedRect[1];
this.#position = [100 * (popupLeft - pageX) / pageWidth, 100 * (popupTop - pageY) / pageHeight];
const {
style
} = this.#container;
style.left = `${this.#position[0]}%`;
style.top = `${this.#position[1]}%`;
}
#toggle() {
this.#pinned = !this.#pinned;
if (this.#pinned) {
this.#show();
this.#container.addEventListener("click", this.#boundToggle);
this.#container.addEventListener("keydown", this.#boundKeyDown);
} else {
this.#hide();
this.#container.removeEventListener("click", this.#boundToggle);
this.#container.removeEventListener("keydown", this.#boundKeyDown);
}
}
#show() {
if (!this.#popup) {
this.render();
}
if (!this.isVisible) {
this.#setPosition();
this.#container.hidden = false;
this.#container.style.zIndex = parseInt(this.#container.style.zIndex) + 1000;
const page = this.#container.closest(".page");
if (page) {
page.style.overflow = "visible";
}
} else if (this.#pinned) {
this.#container.classList.add("focused");
}
}
#hide() {
this.#container.classList.remove("focused");
if (this.#pinned || !this.isVisible) {
return;
}
this.#container.hidden = true;
this.#container.style.zIndex = parseInt(this.#container.style.zIndex) - 1000;
const page = this.#container.closest(".page");
if (page) {
page.style.overflow = "";
}
}
forceHide() {
this.#wasVisible = this.isVisible;
if (!this.#wasVisible) {
return;
}
this.#container.hidden = true;
}
maybeShow() {
if (!this.#wasVisible) {
return;
}
if (!this.#popup) {
this.#show();
}
this.#wasVisible = false;
this.#container.hidden = false;
}
get isVisible() {
return this.#container.hidden === false;
}
}
class FreeTextAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
this.textContent = parameters.data.textContent;
this.textPosition = parameters.data.textPosition;
this.annotationEditorType = util.AnnotationEditorType.FREETEXT;
}
render() {
this.container.classList.add("freeTextAnnotation");
if (this.textContent) {
const content = document.createElement("div");
content.classList.add("annotationTextContent");
content.setAttribute("role", "comment");
for (const line of this.textContent) {
const lineSpan = document.createElement("span");
lineSpan.textContent = line;
content.append(lineSpan);
}
this.container.append(content);
}
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this._editOnDoubleClick();
return this.container;
}
}
class LineAnnotationElement extends AnnotationElement {
#line = null;
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
}
render() {
this.container.classList.add("lineAnnotation");
const data = this.data;
const {
width,
height
} = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height, true);
const line = this.#line = this.svgFactory.createElement("svg:line");
line.setAttribute("x1", data.rect[2] - data.lineCoordinates[0]);
line.setAttribute("y1", data.rect[3] - data.lineCoordinates[1]);
line.setAttribute("x2", data.rect[2] - data.lineCoordinates[2]);
line.setAttribute("y2", data.rect[3] - data.lineCoordinates[3]);
line.setAttribute("stroke-width", data.borderStyle.width || 1);
line.setAttribute("stroke", "transparent");
line.setAttribute("fill", "transparent");
svg.append(line);
this.container.append(svg);
if (!data.popupRef && this.hasPopupData) {
this._createPopup();
}
return this.container;
}
getElementsToTriggerPopup() {
return this.#line;
}
addHighlightArea() {
this.container.classList.add("highlightArea");
}
}
class SquareAnnotationElement extends AnnotationElement {
#square = null;
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
}
render() {
this.container.classList.add("squareAnnotation");
const data = this.data;
const {
width,
height
} = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height, true);
const borderWidth = data.borderStyle.width;
const square = this.#square = this.svgFactory.createElement("svg:rect");
square.setAttribute("x", borderWidth / 2);
square.setAttribute("y", borderWidth / 2);
square.setAttribute("width", width - borderWidth);
square.setAttribute("height", height - borderWidth);
square.setAttribute("stroke-width", borderWidth || 1);
square.setAttribute("stroke", "transparent");
square.setAttribute("fill", "transparent");
svg.append(square);
this.container.append(svg);
if (!data.popupRef && this.hasPopupData) {
this._createPopup();
}
return this.container;
}
getElementsToTriggerPopup() {
return this.#square;
}
addHighlightArea() {
this.container.classList.add("highlightArea");
}
}
class CircleAnnotationElement extends AnnotationElement {
#circle = null;
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
}
render() {
this.container.classList.add("circleAnnotation");
const data = this.data;
const {
width,
height
} = getRectDims(data.rect);
const svg = this.svgFactory.create(width, height, true);
const borderWidth = data.borderStyle.width;
const circle = this.#circle = this.svgFactory.createElement("svg:ellipse");
circle.setAttribute("cx", width / 2);
circle.setAttribute("cy", height / 2);
circle.setAttribute("rx", width / 2 - borderWidth / 2);
circle.setAttribute("ry", height / 2 - borderWidth / 2);
circle.setAttribute("stroke-width", borderWidth || 1);
circle.setAttribute("stroke", "transparent");
circle.setAttribute("fill", "transparent");
svg.append(circle);
this.container.append(svg);
if (!data.popupRef && this.hasPopupData) {
this._createPopup();
}
return this.container;
}
getElementsToTriggerPopup() {
return this.#circle;
}
addHighlightArea() {
this.container.classList.add("highlightArea");
}
}
class PolylineAnnotationElement extends AnnotationElement {
#polyline = null;
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
this.containerClassName = "polylineAnnotation";
this.svgElementName = "svg:polyline";
}
render() {
this.container.classList.add(this.containerClassName);
const {
data: {
rect,
vertices,
borderStyle,
popupRef
}
} = this;
if (!vertices) {
return this.container;
}
const {
width,
height
} = getRectDims(rect);
const svg = this.svgFactory.create(width, height, true);
let points = [];
for (let i = 0, ii = vertices.length; i < ii; i += 2) {
const x = vertices[i] - rect[0];
const y = rect[3] - vertices[i + 1];
points.push(`${x},${y}`);
}
points = points.join(" ");
const polyline = this.#polyline = this.svgFactory.createElement(this.svgElementName);
polyline.setAttribute("points", points);
polyline.setAttribute("stroke-width", borderStyle.width || 1);
polyline.setAttribute("stroke", "transparent");
polyline.setAttribute("fill", "transparent");
svg.append(polyline);
this.container.append(svg);
if (!popupRef && this.hasPopupData) {
this._createPopup();
}
return this.container;
}
getElementsToTriggerPopup() {
return this.#polyline;
}
addHighlightArea() {
this.container.classList.add("highlightArea");
}
}
class PolygonAnnotationElement extends PolylineAnnotationElement {
constructor(parameters) {
super(parameters);
this.containerClassName = "polygonAnnotation";
this.svgElementName = "svg:polygon";
}
}
class CaretAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
}
render() {
this.container.classList.add("caretAnnotation");
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
return this.container;
}
}
class InkAnnotationElement extends AnnotationElement {
#polylinesGroupElement = null;
#polylines = [];
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
this.containerClassName = "inkAnnotation";
this.svgElementName = "svg:polyline";
this.annotationEditorType = this.data.it === "InkHighlight" ? util.AnnotationEditorType.HIGHLIGHT : util.AnnotationEditorType.INK;
}
#getTransform(rotation, rect) {
switch (rotation) {
case 90:
return {
transform: `rotate(90) translate(${-rect[0]},${rect[1]}) scale(1,-1)`,
width: rect[3] - rect[1],
height: rect[2] - rect[0]
};
case 180:
return {
transform: `rotate(180) translate(${-rect[2]},${rect[1]}) scale(1,-1)`,
width: rect[2] - rect[0],
height: rect[3] - rect[1]
};
case 270:
return {
transform: `rotate(270) translate(${-rect[2]},${rect[3]}) scale(1,-1)`,
width: rect[3] - rect[1],
height: rect[2] - rect[0]
};
default:
return {
transform: `translate(${-rect[0]},${rect[3]}) scale(1,-1)`,
width: rect[2] - rect[0],
height: rect[3] - rect[1]
};
}
}
render() {
this.container.classList.add(this.containerClassName);
const {
data: {
rect,
rotation,
inkLists,
borderStyle,
popupRef
}
} = this;
const {
transform,
width,
height
} = this.#getTransform(rotation, rect);
const svg = this.svgFactory.create(width, height, true);
const g = this.#polylinesGroupElement = this.svgFactory.createElement("svg:g");
svg.append(g);
g.setAttribute("stroke-width", borderStyle.width || 1);
g.setAttribute("stroke-linecap", "round");
g.setAttribute("stroke-linejoin", "round");
g.setAttribute("stroke-miterlimit", 10);
g.setAttribute("stroke", "transparent");
g.setAttribute("fill", "transparent");
g.setAttribute("transform", transform);
for (let i = 0, ii = inkLists.length; i < ii; i++) {
const polyline = this.svgFactory.createElement(this.svgElementName);
this.#polylines.push(polyline);
polyline.setAttribute("points", inkLists[i].join(","));
g.append(polyline);
}
if (!popupRef && this.hasPopupData) {
this._createPopup();
}
this.container.append(svg);
this._editOnDoubleClick();
return this.container;
}
updateEdited(params) {
super.updateEdited(params);
const {
thickness,
points,
rect
} = params;
const g = this.#polylinesGroupElement;
if (thickness >= 0) {
g.setAttribute("stroke-width", thickness || 1);
}
if (points) {
for (let i = 0, ii = this.#polylines.length; i < ii; i++) {
this.#polylines[i].setAttribute("points", points[i].join(","));
}
}
if (rect) {
const {
transform,
width,
height
} = this.#getTransform(this.data.rotation, rect);
const root = g.parentElement;
root.setAttribute("viewBox", `0 0 ${width} ${height}`);
g.setAttribute("transform", transform);
}
}
getElementsToTriggerPopup() {
return this.#polylines;
}
addHighlightArea() {
this.container.classList.add("highlightArea");
}
}
class HighlightAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true,
createQuadrilaterals: true
});
this.annotationEditorType = util.AnnotationEditorType.HIGHLIGHT;
}
render() {
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this.container.classList.add("highlightAnnotation");
this._editOnDoubleClick();
return this.container;
}
}
class UnderlineAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true,
createQuadrilaterals: true
});
}
render() {
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this.container.classList.add("underlineAnnotation");
return this.container;
}
}
class SquigglyAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true,
createQuadrilaterals: true
});
}
render() {
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this.container.classList.add("squigglyAnnotation");
return this.container;
}
}
class StrikeOutAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true,
createQuadrilaterals: true
});
}
render() {
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this.container.classList.add("strikeoutAnnotation");
return this.container;
}
}
class StampAnnotationElement extends AnnotationElement {
constructor(parameters) {
super(parameters, {
isRenderable: true,
ignoreBorder: true
});
this.annotationEditorType = util.AnnotationEditorType.STAMP;
}
render() {
this.container.classList.add("stampAnnotation");
this.container.setAttribute("role", "img");
if (!this.data.popupRef && this.hasPopupData) {
this._createPopup();
}
this._editOnDoubleClick();
return this.container;
}
}
class FileAttachmentAnnotationElement extends AnnotationElement {
#trigger = null;
constructor(parameters) {
super(parameters, {
isRenderable: true
});
const {
file
} = this.data;
this.filename = file.filename;
this.content = file.content;
this.linkService.eventBus?.dispatch("fileattachmentannotation", {
source: this,
...file
});
}
render() {
this.container.classList.add("fileAttachmentAnnotation");
const {
container,
data
} = this;
let trigger;
if (data.hasAppearance || data.fillAlpha === 0) {
trigger = document.createElement("div");
} else {
trigger = document.createElement("img");
trigger.src = `${this.imageResourcesPath}annotation-${/paperclip/i.test(data.name) ? "paperclip" : "pushpin"}.svg`;
if (data.fillAlpha && data.fillAlpha < 1) {
trigger.style = `filter: opacity(${Math.round(data.fillAlpha * 100)}%);`;
}
}
trigger.addEventListener("dblclick", this.#download.bind(this));
this.#trigger = trigger;
const {
isMac
} = util.FeatureTest.platform;
container.addEventListener("keydown", evt => {
if (evt.key === "Enter" && (isMac ? evt.metaKey : evt.ctrlKey)) {
this.#download();
}
});
if (!data.popupRef && this.hasPopupData) {
this._createPopup();
} else {
trigger.classList.add("popupTriggerArea");
}
container.append(trigger);
return container;
}
getElementsToTriggerPopup() {
return this.#trigger;
}
addHighlightArea() {
this.container.classList.add("highlightArea");
}
#download() {
this.downloadManager?.openOrDownloadData(this.content, this.filename);
}
}
class AnnotationLayer {
#accessibilityManager = null;
#annotationCanvasMap = null;
#editableAnnotations = new Map();
#structTreeLayer = null;
constructor({
div,
accessibilityManager,
annotationCanvasMap,
annotationEditorUIManager,
page,
viewport,
structTreeLayer
}) {
this.div = div;
this.#accessibilityManager = accessibilityManager;
this.#annotationCanvasMap = annotationCanvasMap;
this.#structTreeLayer = structTreeLayer || null;
this.page = page;
this.viewport = viewport;
this.zIndex = 0;
this._annotationEditorUIManager = annotationEditorUIManager;
}
hasEditableAnnotations() {
return this.#editableAnnotations.size > 0;
}
async #appendElement(element, id) {
const contentElement = element.firstChild || element;
const annotationId = contentElement.id = `${util.AnnotationPrefix}${id}`;
const ariaAttributes = await this.#structTreeLayer?.getAriaAttributes(annotationId);
if (ariaAttributes) {
for (const [key, value] of ariaAttributes) {
contentElement.setAttribute(key, value);
}
}
this.div.append(element);
this.#accessibilityManager?.moveElementInDOM(this.div, element, contentElement, false);
}
async render(params) {
const {
annotations
} = params;
const layer = this.div;
(0,display_utils.setLayerDimensions)(layer, this.viewport);
const popupToElements = new Map();
const elementParams = {
data: null,
layer,
linkService: params.linkService,
downloadManager: params.downloadManager,
imageResourcesPath: params.imageResourcesPath || "",
renderForms: params.renderForms !== false,
svgFactory: new DOMSVGFactory(),
annotationStorage: params.annotationStorage || new annotation_storage.AnnotationStorage(),
enableScripting: params.enableScripting === true,
hasJSActions: params.hasJSActions,
fieldObjects: params.fieldObjects,
parent: this,
elements: null
};
for (const data of annotations) {
if (data.noHTML) {
continue;
}
const isPopupAnnotation = data.annotationType === util.AnnotationType.POPUP;
if (!isPopupAnnotation) {
const {
width,
height
} = getRectDims(data.rect);
if (width <= 0 || height <= 0) {
continue;
}
} else {
const elements = popupToElements.get(data.id);
if (!elements) {
continue;
}
elementParams.elements = elements;
}
elementParams.data = data;
const element = AnnotationElementFactory.create(elementParams);
if (!element.isRenderable) {
continue;
}
if (!isPopupAnnotation && data.popupRef) {
const elements = popupToElements.get(data.popupRef);
if (!elements) {
popupToElements.set(data.popupRef, [element]);
} else {
elements.push(element);
}
}
const rendered = element.render();
if (data.hidden) {
rendered.style.visibility = "hidden";
}
await this.#appendElement(rendered, data.id);
if (element._isEditable) {
this.#editableAnnotations.set(element.data.id, element);
this._annotationEditorUIManager?.renderAnnotationElement(element);
}
}
this.#setAnnotationCanvasMap();
}
update({
viewport
}) {
const layer = this.div;
this.viewport = viewport;
(0,display_utils.setLayerDimensions)(layer, {
rotation: viewport.rotation
});
this.#setAnnotationCanvasMap();
layer.hidden = false;
}
#setAnnotationCanvasMap() {
if (!this.#annotationCanvasMap) {
return;
}
const layer = this.div;
for (const [id, canvas] of this.#annotationCanvasMap) {
const element = layer.querySelector(`[data-annotation-id="${id}"]`);
if (!element) {
continue;
}
canvas.className = "annotationContent";
const {
firstChild
} = element;
if (!firstChild) {
element.append(canvas);
} else if (firstChild.nodeName === "CANVAS") {
firstChild.replaceWith(canvas);
} else if (!firstChild.classList.contains("annotationContent")) {
firstChild.before(canvas);
} else {
firstChild.after(canvas);
}
}
this.#annotationCanvasMap.clear();
}
getEditableAnnotations() {
return Array.from(this.#editableAnnotations.values());
}
getEditableAnnotation(id) {
return this.#editableAnnotations.get(id);
}
}
;// ./src/display/editor/freetext.js
const EOL_PATTERN = /\r\n?|\n/g;
class FreeTextEditor extends editor_editor.AnnotationEditor {
#color;
#content = "";
#editorDivId = `${this.id}-editor`;
#editModeAC = null;
#fontSize;
static _freeTextDefaultContent = "";
static _internalPadding = 0;
static _defaultColor = null;
static _defaultFontSize = 10;
static get _keyboardManager() {
const proto = FreeTextEditor.prototype;
const arrowChecker = self => self.isEmpty();
const small = tools.AnnotationEditorUIManager.TRANSLATE_SMALL;
const big = tools.AnnotationEditorUIManager.TRANSLATE_BIG;
return (0,util.shadow)(this, "_keyboardManager", new tools.KeyboardManager([[["ctrl+s", "mac+meta+s", "ctrl+p", "mac+meta+p"], proto.commitOrRemove, {
bubbles: true
}], [["ctrl+Enter", "mac+meta+Enter", "Escape", "mac+Escape"], proto.commitOrRemove], [["ArrowLeft", "mac+ArrowLeft"], proto._translateEmpty, {
args: [-small, 0],
checker: arrowChecker
}], [["ctrl+ArrowLeft", "mac+shift+ArrowLeft"], proto._translateEmpty, {
args: [-big, 0],
checker: arrowChecker
}], [["ArrowRight", "mac+ArrowRight"], proto._translateEmpty, {
args: [small, 0],
checker: arrowChecker
}], [["ctrl+ArrowRight", "mac+shift+ArrowRight"], proto._translateEmpty, {
args: [big, 0],
checker: arrowChecker
}], [["ArrowUp", "mac+ArrowUp"], proto._translateEmpty, {
args: [0, -small],
checker: arrowChecker
}], [["ctrl+ArrowUp", "mac+shift+ArrowUp"], proto._translateEmpty, {
args: [0, -big],
checker: arrowChecker
}], [["ArrowDown", "mac+ArrowDown"], proto._translateEmpty, {
args: [0, small],
checker: arrowChecker
}], [["ctrl+ArrowDown", "mac+shift+ArrowDown"], proto._translateEmpty, {
args: [0, big],
checker: arrowChecker
}]]));
}
static _type = "freetext";
static _editorType = util.AnnotationEditorType.FREETEXT;
constructor(params) {
super({
...params,
name: "freeTextEditor"
});
this.#color = params.color || FreeTextEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor;
this.#fontSize = params.fontSize || FreeTextEditor._defaultFontSize;
}
static initialize(l10n, uiManager) {
editor_editor.AnnotationEditor.initialize(l10n, uiManager);
const style = getComputedStyle(document.documentElement);
this._internalPadding = parseFloat(style.getPropertyValue("--freetext-padding"));
}
static updateDefaultParams(type, value) {
switch (type) {
case util.AnnotationEditorParamsType.FREETEXT_SIZE:
FreeTextEditor._defaultFontSize = value;
break;
case util.AnnotationEditorParamsType.FREETEXT_COLOR:
FreeTextEditor._defaultColor = value;
break;
}
}
updateParams(type, value) {
switch (type) {
case util.AnnotationEditorParamsType.FREETEXT_SIZE:
this.#updateFontSize(value);
break;
case util.AnnotationEditorParamsType.FREETEXT_COLOR:
this.#updateColor(value);
break;
}
}
static get defaultPropertiesToUpdate() {
return [[util.AnnotationEditorParamsType.FREETEXT_SIZE, FreeTextEditor._defaultFontSize], [util.AnnotationEditorParamsType.FREETEXT_COLOR, FreeTextEditor._defaultColor || editor_editor.AnnotationEditor._defaultLineColor]];
}
get propertiesToUpdate() {
return [[util.AnnotationEditorParamsType.FREETEXT_SIZE, this.#fontSize], [util.AnnotationEditorParamsType.FREETEXT_COLOR, this.#color]];
}
#updateFontSize(fontSize) {
const setFontsize = size => {
this.editorDiv.style.fontSize = `calc(${size}px * var(--scale-factor))`;
this.translate(0, -(size - this.#fontSize) * this.parentScale);
this.#fontSize = size;
this.#setEditorDimensions();
};
const savedFontsize = this.#fontSize;
this.addCommands({
cmd: setFontsize.bind(this, fontSize),
undo: setFontsize.bind(this, savedFontsize),
post: this._uiManager.updateUI.bind(this._uiManager, this),
mustExec: true,
type: util.AnnotationEditorParamsType.FREETEXT_SIZE,
overwriteIfSameType: true,
keepUndo: true
});
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "fontSizeChanged",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: fontSize,
previousValue: this.#fontSize
});
}
#updateColor(color) {
const setColor = col => {
this.#color = this.editorDiv.style.color = col;
};
const savedColor = this.#color;
this.addCommands({
cmd: setColor.bind(this, color),
undo: setColor.bind(this, savedColor),
post: this._uiManager.updateUI.bind(this._uiManager, this),
mustExec: true,
type: util.AnnotationEditorParamsType.FREETEXT_COLOR,
overwriteIfSameType: true,
keepUndo: true
});
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "colorChanged",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: color,
previousValue: this.#color
});
}
_translateEmpty(x, y) {
this._uiManager.translateSelectedEditors(x, y, true);
}
getInitialTranslation() {
const scale = this.parentScale;
return [-FreeTextEditor._internalPadding * scale, -(FreeTextEditor._internalPadding + this.#fontSize) * scale];
}
rebuild() {
if (!this.parent) {
return;
}
super.rebuild();
if (this.div === null) {
return;
}
if (!this.isAttachedToDOM) {
this.parent.add(this);
}
}
enableEditMode() {
if (this.isInEditMode()) {
return;
}
this.parent.setEditingState(false);
this.parent.updateToolbar(util.AnnotationEditorType.FREETEXT);
super.enableEditMode();
this.overlayDiv.classList.remove("enabled");
this.editorDiv.contentEditable = true;
this._isDraggable = false;
this.div.removeAttribute("aria-activedescendant");
this.#editModeAC = new AbortController();
const signal = this._uiManager.combinedSignal(this.#editModeAC);
this.editorDiv.addEventListener("keydown", this.editorDivKeydown.bind(this), {
signal
});
this.editorDiv.addEventListener("focus", this.editorDivFocus.bind(this), {
signal
});
this.editorDiv.addEventListener("blur", this.editorDivBlur.bind(this), {
signal
});
this.editorDiv.addEventListener("input", this.editorDivInput.bind(this), {
signal
});
this.editorDiv.addEventListener("paste", this.editorDivPaste.bind(this), {
signal
});
}
disableEditMode() {
if (!this.isInEditMode()) {
return;
}
this.parent.setEditingState(true);
super.disableEditMode();
this.overlayDiv.classList.add("enabled");
this.editorDiv.contentEditable = false;
this.div.setAttribute("aria-activedescendant", this.#editorDivId);
this._isDraggable = true;
this.#editModeAC?.abort();
this.#editModeAC = null;
this.div.focus({
preventScroll: true
});
this.isEditing = false;
this.parent.div.classList.add("freetextEditing");
}
focusin(event) {
if (!this._focusEventsAllowed) {
return;
}
super.focusin(event);
if (event.target !== this.editorDiv) {
this.editorDiv.focus();
}
}
onceAdded(focus) {
if (this.width) {
return;
}
this.enableEditMode();
if (focus) {
this.editorDiv.focus();
}
if (this._initialOptions?.isCentered) {
this.center();
}
this._initialOptions = null;
}
isEmpty() {
return !this.editorDiv || this.editorDiv.innerText.trim() === "";
}
remove() {
this.isEditing = false;
if (this.parent) {
this.parent.setEditingState(true);
this.parent.div.classList.add("freetextEditing");
}
super.remove();
}
#extractText() {
const buffer = [];
this.editorDiv.normalize();
let prevChild = null;
for (const child of this.editorDiv.childNodes) {
if (prevChild?.nodeType === Node.TEXT_NODE && child.nodeName === "BR") {
continue;
}
buffer.push(FreeTextEditor.#getNodeContent(child));
prevChild = child;
}
return buffer.join("\n");
}
#setEditorDimensions() {
const [parentWidth, parentHeight] = this.parentDimensions;
let rect;
if (this.isAttachedToDOM) {
rect = this.div.getBoundingClientRect();
} else {
const {
currentLayer,
div
} = this;
const savedDisplay = div.style.display;
const savedVisibility = div.classList.contains("hidden");
div.classList.remove("hidden");
div.style.display = "hidden";
currentLayer.div.append(this.div);
rect = div.getBoundingClientRect();
div.remove();
div.style.display = savedDisplay;
div.classList.toggle("hidden", savedVisibility);
}
if (this.rotation % 180 === this.parentRotation % 180) {
this.width = rect.width / parentWidth;
this.height = rect.height / parentHeight;
} else {
this.width = rect.height / parentWidth;
this.height = rect.width / parentHeight;
}
this.fixAndSetPosition();
}
commit() {
if (!this.isInEditMode()) {
return;
}
super.commit();
this.disableEditMode();
const savedText = this.#content;
const newText = this.#content = this.#extractText().trimEnd();
if (savedText === newText) {
return;
}
const setText = text => {
this.#content = text;
if (!text) {
this.remove();
return;
}
this.#setContent();
this._uiManager.rebuild(this);
this.#setEditorDimensions();
};
this.addCommands({
cmd: () => {
setText(newText);
},
undo: () => {
setText(savedText);
},
mustExec: false
});
this.#setEditorDimensions();
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "commit",
page: this.pageIndex + 1,
value: newText,
previousValue: savedText
});
}
shouldGetKeyboardEvents() {
return this.isInEditMode();
}
enterInEditMode() {
this.enableEditMode();
this.editorDiv.focus();
}
dblclick(event) {
this.enterInEditMode();
}
keydown(event) {
if (event.target === this.div && event.key === "Enter") {
this.enterInEditMode();
event.preventDefault();
}
}
editorDivKeydown(event) {
FreeTextEditor._keyboardManager.exec(this, event);
}
editorDivFocus(event) {
this.isEditing = true;
}
editorDivBlur(event) {
this.isEditing = false;
}
editorDivInput(event) {
this.parent.div.classList.toggle("freetextEditing", this.isEmpty());
}
disableEditing() {
this.editorDiv.setAttribute("role", "comment");
this.editorDiv.removeAttribute("aria-multiline");
}
enableEditing() {
this.editorDiv.setAttribute("role", "textbox");
this.editorDiv.setAttribute("aria-multiline", true);
}
render() {
if (this.div) {
return this.div;
}
let baseX, baseY;
if (this.width) {
baseX = this.x;
baseY = this.y;
}
super.render();
this.editorDiv = document.createElement("div");
this.editorDiv.className = "internal";
this.editorDiv.setAttribute("id", this.#editorDivId);
this.editorDiv.setAttribute("data-l10n-id", "pdfjs-free-text2");
this.editorDiv.setAttribute("data-l10n-attrs", "default-content");
this.enableEditing();
this.editorDiv.contentEditable = true;
const {
style
} = this.editorDiv;
style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
style.color = this.#color;
this.div.append(this.editorDiv);
this.overlayDiv = document.createElement("div");
this.overlayDiv.classList.add("overlay", "enabled");
this.div.append(this.overlayDiv);
(0,tools.bindEvents)(this, this.div, ["dblclick", "keydown"]);
if (this.width) {
const [parentWidth, parentHeight] = this.parentDimensions;
if (this.annotationElementId) {
const {
position
} = this._initialData;
let [tx, ty] = this.getInitialTranslation();
[tx, ty] = this.pageTranslationToScreen(tx, ty);
const [pageWidth, pageHeight] = this.pageDimensions;
const [pageX, pageY] = this.pageTranslation;
let posX, posY;
switch (this.rotation) {
case 0:
posX = baseX + (position[0] - pageX) / pageWidth;
posY = baseY + this.height - (position[1] - pageY) / pageHeight;
break;
case 90:
posX = baseX + (position[0] - pageX) / pageWidth;
posY = baseY - (position[1] - pageY) / pageHeight;
[tx, ty] = [ty, -tx];
break;
case 180:
posX = baseX - this.width + (position[0] - pageX) / pageWidth;
posY = baseY - (position[1] - pageY) / pageHeight;
[tx, ty] = [-tx, -ty];
break;
case 270:
posX = baseX + (position[0] - pageX - this.height * pageHeight) / pageWidth;
posY = baseY + (position[1] - pageY - this.width * pageWidth) / pageHeight;
[tx, ty] = [-ty, tx];
break;
}
this.setAt(posX * parentWidth, posY * parentHeight, tx, ty);
} else {
this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
}
this.#setContent();
this._isDraggable = true;
this.editorDiv.contentEditable = false;
} else {
this._isDraggable = false;
this.editorDiv.contentEditable = true;
}
return this.div;
}
static #getNodeContent(node) {
return (node.nodeType === Node.TEXT_NODE ? node.nodeValue : node.innerText).replaceAll(EOL_PATTERN, "");
}
editorDivPaste(event) {
const clipboardData = event.clipboardData || window.clipboardData;
const {
types
} = clipboardData;
if (types.length === 1 && types[0] === "text/plain") {
return;
}
event.preventDefault();
const paste = FreeTextEditor.#deserializeContent(clipboardData.getData("text") || "").replaceAll(EOL_PATTERN, "\n");
if (!paste) {
return;
}
const selection = window.getSelection();
if (!selection.rangeCount) {
return;
}
this.editorDiv.normalize();
selection.deleteFromDocument();
const range = selection.getRangeAt(0);
if (!paste.includes("\n")) {
range.insertNode(document.createTextNode(paste));
this.editorDiv.normalize();
selection.collapseToStart();
return;
}
const {
startContainer,
startOffset
} = range;
const bufferBefore = [];
const bufferAfter = [];
if (startContainer.nodeType === Node.TEXT_NODE) {
const parent = startContainer.parentElement;
bufferAfter.push(startContainer.nodeValue.slice(startOffset).replaceAll(EOL_PATTERN, ""));
if (parent !== this.editorDiv) {
let buffer = bufferBefore;
for (const child of this.editorDiv.childNodes) {
if (child === parent) {
buffer = bufferAfter;
continue;
}
buffer.push(FreeTextEditor.#getNodeContent(child));
}
}
bufferBefore.push(startContainer.nodeValue.slice(0, startOffset).replaceAll(EOL_PATTERN, ""));
} else if (startContainer === this.editorDiv) {
let buffer = bufferBefore;
let i = 0;
for (const child of this.editorDiv.childNodes) {
if (i++ === startOffset) {
buffer = bufferAfter;
}
buffer.push(FreeTextEditor.#getNodeContent(child));
}
}
this.#content = `${bufferBefore.join("\n")}${paste}${bufferAfter.join("\n")}`;
this.#setContent();
const newRange = new Range();
let beforeLength = bufferBefore.reduce((acc, line) => acc + line.length, 0);
for (const {
firstChild
} of this.editorDiv.childNodes) {
if (firstChild.nodeType === Node.TEXT_NODE) {
const length = firstChild.nodeValue.length;
if (beforeLength <= length) {
newRange.setStart(firstChild, beforeLength);
newRange.setEnd(firstChild, beforeLength);
break;
}
beforeLength -= length;
}
}
selection.removeAllRanges();
selection.addRange(newRange);
}
#setContent() {
this.editorDiv.replaceChildren();
if (!this.#content) {
return;
}
for (const line of this.#content.split("\n")) {
const div = document.createElement("div");
div.append(line ? document.createTextNode(line) : document.createElement("br"));
this.editorDiv.append(div);
}
}
#serializeContent() {
return this.#content.replaceAll("\xa0", " ");
}
static #deserializeContent(content) {
return content.replaceAll(" ", "\xa0");
}
get contentDiv() {
return this.editorDiv;
}
static async deserialize(data, parent, uiManager) {
let initialData = null;
if (data instanceof FreeTextAnnotationElement) {
const {
data: {
defaultAppearanceData: {
fontSize,
fontColor
},
rect,
rotation,
id,
popupRef
},
textContent,
textPosition,
parent: {
page: {
pageNumber
}
}
} = data;
if (!textContent || textContent.length === 0) {
return null;
}
initialData = data = {
annotationType: util.AnnotationEditorType.FREETEXT,
color: Array.from(fontColor),
fontSize,
value: textContent.join("\n"),
position: textPosition,
pageIndex: pageNumber - 1,
rect: rect.slice(0),
rotation,
id,
deleted: false,
popupRef
};
}
const editor = await super.deserialize(data, parent, uiManager);
editor.#fontSize = data.fontSize;
editor.#color = util.Util.makeHexColor(...data.color);
editor.#content = FreeTextEditor.#deserializeContent(data.value);
editor.annotationElementId = data.id || null;
editor._initialData = initialData;
return editor;
}
serialize(isForCopying = false) {
if (this.isEmpty()) {
return null;
}
if (this.deleted) {
return this.serializeDeleted();
}
const padding = FreeTextEditor._internalPadding * this.parentScale;
const rect = this.getRect(padding, padding);
const color = editor_editor.AnnotationEditor._colorManager.convert(this.isAttachedToDOM ? getComputedStyle(this.editorDiv).color : this.#color);
const serialized = {
annotationType: util.AnnotationEditorType.FREETEXT,
color,
fontSize: this.#fontSize,
value: this.#serializeContent(),
pageIndex: this.pageIndex,
rect,
rotation: this.rotation,
structTreeParentId: this._structTreeParentId
};
if (isForCopying) {
return serialized;
}
if (this.annotationElementId && !this.#hasElementChanged(serialized)) {
return null;
}
serialized.id = this.annotationElementId;
return serialized;
}
#hasElementChanged(serialized) {
const {
value,
fontSize,
color,
pageIndex
} = this._initialData;
return this._hasBeenMoved || serialized.value !== value || serialized.fontSize !== fontSize || serialized.color.some((c, i) => c !== color[i]) || serialized.pageIndex !== pageIndex;
}
renderAnnotationElement(annotation) {
const content = super.renderAnnotationElement(annotation);
if (this.deleted) {
return content;
}
const {
style
} = content;
style.fontSize = `calc(${this.#fontSize}px * var(--scale-factor))`;
style.color = this.#color;
content.replaceChildren();
for (const line of this.#content.split("\n")) {
const div = document.createElement("div");
div.append(line ? document.createTextNode(line) : document.createElement("br"));
content.append(div);
}
const padding = FreeTextEditor._internalPadding * this.parentScale;
annotation.updateEdited({
rect: this.getRect(padding, padding),
popupContent: this.#content
});
return content;
}
resetAnnotationElement(annotation) {
super.resetAnnotationElement(annotation);
annotation.resetEdited();
}
}
;// ./src/display/editor/drawers/outline.js
class Outline {
static PRECISION = 1e-4;
toSVGPath() {
(0,util.unreachable)("Abstract method `toSVGPath` must be implemented.");
}
get box() {
(0,util.unreachable)("Abstract getter `box` must be implemented.");
}
serialize(_bbox, _rotation) {
(0,util.unreachable)("Abstract method `serialize` must be implemented.");
}
static _rescale(src, tx, ty, sx, sy, dest) {
dest ||= new Float32Array(src.length);
for (let i = 0, ii = src.length; i < ii; i += 2) {
dest[i] = tx + src[i] * sx;
dest[i + 1] = ty + src[i + 1] * sy;
}
return dest;
}
static _rescaleAndSwap(src, tx, ty, sx, sy, dest) {
dest ||= new Float32Array(src.length);
for (let i = 0, ii = src.length; i < ii; i += 2) {
dest[i] = tx + src[i + 1] * sx;
dest[i + 1] = ty + src[i] * sy;
}
return dest;
}
static _translate(src, tx, ty, dest) {
dest ||= new Float32Array(src.length);
for (let i = 0, ii = src.length; i < ii; i += 2) {
dest[i] = tx + src[i];
dest[i + 1] = ty + src[i + 1];
}
return dest;
}
static svgRound(x) {
return Math.round(x * 10000);
}
static _normalizePoint(x, y, parentWidth, parentHeight, rotation) {
switch (rotation) {
case 90:
return [1 - y / parentWidth, x / parentHeight];
case 180:
return [1 - x / parentWidth, 1 - y / parentHeight];
case 270:
return [y / parentWidth, 1 - x / parentHeight];
default:
return [x / parentWidth, y / parentHeight];
}
}
static _normalizePagePoint(x, y, rotation) {
switch (rotation) {
case 90:
return [1 - y, x];
case 180:
return [1 - x, 1 - y];
case 270:
return [y, 1 - x];
default:
return [x, y];
}
}
static createBezierPoints(x1, y1, x2, y2, x3, y3) {
return [(x1 + 5 * x2) / 6, (y1 + 5 * y2) / 6, (5 * x2 + x3) / 6, (5 * y2 + y3) / 6, (x2 + x3) / 2, (y2 + y3) / 2];
}
}
;// ./src/display/editor/drawers/freedraw.js
class FreeDrawOutliner {
#box;
#bottom = [];
#innerMargin;
#isLTR;
#top = [];
#last = new Float32Array(18);
#lastX;
#lastY;
#min;
#min_dist;
#scaleFactor;
#thickness;
#points = [];
static #MIN_DIST = 8;
static #MIN_DIFF = 2;
static #MIN = FreeDrawOutliner.#MIN_DIST + FreeDrawOutliner.#MIN_DIFF;
constructor({
x,
y
}, box, scaleFactor, thickness, isLTR, innerMargin = 0) {
this.#box = box;
this.#thickness = thickness * scaleFactor;
this.#isLTR = isLTR;
this.#last.set([NaN, NaN, NaN, NaN, x, y], 6);
this.#innerMargin = innerMargin;
this.#min_dist = FreeDrawOutliner.#MIN_DIST * scaleFactor;
this.#min = FreeDrawOutliner.#MIN * scaleFactor;
this.#scaleFactor = scaleFactor;
this.#points.push(x, y);
}
isEmpty() {
return isNaN(this.#last[8]);
}
#getLastCoords() {
const lastTop = this.#last.subarray(4, 6);
const lastBottom = this.#last.subarray(16, 18);
const [x, y, width, height] = this.#box;
return [(this.#lastX + (lastTop[0] - lastBottom[0]) / 2 - x) / width, (this.#lastY + (lastTop[1] - lastBottom[1]) / 2 - y) / height, (this.#lastX + (lastBottom[0] - lastTop[0]) / 2 - x) / width, (this.#lastY + (lastBottom[1] - lastTop[1]) / 2 - y) / height];
}
add({
x,
y
}) {
this.#lastX = x;
this.#lastY = y;
const [layerX, layerY, layerWidth, layerHeight] = this.#box;
let [x1, y1, x2, y2] = this.#last.subarray(8, 12);
const diffX = x - x2;
const diffY = y - y2;
const d = Math.hypot(diffX, diffY);
if (d < this.#min) {
return false;
}
const diffD = d - this.#min_dist;
const K = diffD / d;
const shiftX = K * diffX;
const shiftY = K * diffY;
let x0 = x1;
let y0 = y1;
x1 = x2;
y1 = y2;
x2 += shiftX;
y2 += shiftY;
this.#points?.push(x, y);
const nX = -shiftY / diffD;
const nY = shiftX / diffD;
const thX = nX * this.#thickness;
const thY = nY * this.#thickness;
this.#last.set(this.#last.subarray(2, 8), 0);
this.#last.set([x2 + thX, y2 + thY], 4);
this.#last.set(this.#last.subarray(14, 18), 12);
this.#last.set([x2 - thX, y2 - thY], 16);
if (isNaN(this.#last[6])) {
if (this.#top.length === 0) {
this.#last.set([x1 + thX, y1 + thY], 2);
this.#top.push(NaN, NaN, NaN, NaN, (x1 + thX - layerX) / layerWidth, (y1 + thY - layerY) / layerHeight);
this.#last.set([x1 - thX, y1 - thY], 14);
this.#bottom.push(NaN, NaN, NaN, NaN, (x1 - thX - layerX) / layerWidth, (y1 - thY - layerY) / layerHeight);
}
this.#last.set([x0, y0, x1, y1, x2, y2], 6);
return !this.isEmpty();
}
this.#last.set([x0, y0, x1, y1, x2, y2], 6);
const angle = Math.abs(Math.atan2(y0 - y1, x0 - x1) - Math.atan2(shiftY, shiftX));
if (angle < Math.PI / 2) {
[x1, y1, x2, y2] = this.#last.subarray(2, 6);
this.#top.push(NaN, NaN, NaN, NaN, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight);
[x1, y1, x0, y0] = this.#last.subarray(14, 18);
this.#bottom.push(NaN, NaN, NaN, NaN, ((x0 + x1) / 2 - layerX) / layerWidth, ((y0 + y1) / 2 - layerY) / layerHeight);
return true;
}
[x0, y0, x1, y1, x2, y2] = this.#last.subarray(0, 6);
this.#top.push(((x0 + 5 * x1) / 6 - layerX) / layerWidth, ((y0 + 5 * y1) / 6 - layerY) / layerHeight, ((5 * x1 + x2) / 6 - layerX) / layerWidth, ((5 * y1 + y2) / 6 - layerY) / layerHeight, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight);
[x2, y2, x1, y1, x0, y0] = this.#last.subarray(12, 18);
this.#bottom.push(((x0 + 5 * x1) / 6 - layerX) / layerWidth, ((y0 + 5 * y1) / 6 - layerY) / layerHeight, ((5 * x1 + x2) / 6 - layerX) / layerWidth, ((5 * y1 + y2) / 6 - layerY) / layerHeight, ((x1 + x2) / 2 - layerX) / layerWidth, ((y1 + y2) / 2 - layerY) / layerHeight);
return true;
}
toSVGPath() {
if (this.isEmpty()) {
return "";
}
const top = this.#top;
const bottom = this.#bottom;
if (isNaN(this.#last[6]) && !this.isEmpty()) {
return this.#toSVGPathTwoPoints();
}
const buffer = [];
buffer.push(`M${top[4]} ${top[5]}`);
for (let i = 6; i < top.length; i += 6) {
if (isNaN(top[i])) {
buffer.push(`L${top[i + 4]} ${top[i + 5]}`);
} else {
buffer.push(`C${top[i]} ${top[i + 1]} ${top[i + 2]} ${top[i + 3]} ${top[i + 4]} ${top[i + 5]}`);
}
}
this.#toSVGPathEnd(buffer);
for (let i = bottom.length - 6; i >= 6; i -= 6) {
if (isNaN(bottom[i])) {
buffer.push(`L${bottom[i + 4]} ${bottom[i + 5]}`);
} else {
buffer.push(`C${bottom[i]} ${bottom[i + 1]} ${bottom[i + 2]} ${bottom[i + 3]} ${bottom[i + 4]} ${bottom[i + 5]}`);
}
}
this.#toSVGPathStart(buffer);
return buffer.join(" ");
}
#toSVGPathTwoPoints() {
const [x, y, width, height] = this.#box;
const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords();
return `M${(this.#last[2] - x) / width} ${(this.#last[3] - y) / height} L${(this.#last[4] - x) / width} ${(this.#last[5] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${(this.#last[16] - x) / width} ${(this.#last[17] - y) / height} L${(this.#last[14] - x) / width} ${(this.#last[15] - y) / height} Z`;
}
#toSVGPathStart(buffer) {
const bottom = this.#bottom;
buffer.push(`L${bottom[4]} ${bottom[5]} Z`);
}
#toSVGPathEnd(buffer) {
const [x, y, width, height] = this.#box;
const lastTop = this.#last.subarray(4, 6);
const lastBottom = this.#last.subarray(16, 18);
const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords();
buffer.push(`L${(lastTop[0] - x) / width} ${(lastTop[1] - y) / height} L${lastTopX} ${lastTopY} L${lastBottomX} ${lastBottomY} L${(lastBottom[0] - x) / width} ${(lastBottom[1] - y) / height}`);
}
newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) {
return new FreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR);
}
getOutlines() {
const top = this.#top;
const bottom = this.#bottom;
const last = this.#last;
const [layerX, layerY, layerWidth, layerHeight] = this.#box;
const points = new Float32Array((this.#points?.length ?? 0) + 2);
for (let i = 0, ii = points.length - 2; i < ii; i += 2) {
points[i] = (this.#points[i] - layerX) / layerWidth;
points[i + 1] = (this.#points[i + 1] - layerY) / layerHeight;
}
points[points.length - 2] = (this.#lastX - layerX) / layerWidth;
points[points.length - 1] = (this.#lastY - layerY) / layerHeight;
if (isNaN(last[6]) && !this.isEmpty()) {
return this.#getOutlineTwoPoints(points);
}
const outline = new Float32Array(this.#top.length + 24 + this.#bottom.length);
let N = top.length;
for (let i = 0; i < N; i += 2) {
if (isNaN(top[i])) {
outline[i] = outline[i + 1] = NaN;
continue;
}
outline[i] = top[i];
outline[i + 1] = top[i + 1];
}
N = this.#getOutlineEnd(outline, N);
for (let i = bottom.length - 6; i >= 6; i -= 6) {
for (let j = 0; j < 6; j += 2) {
if (isNaN(bottom[i + j])) {
outline[N] = outline[N + 1] = NaN;
N += 2;
continue;
}
outline[N] = bottom[i + j];
outline[N + 1] = bottom[i + j + 1];
N += 2;
}
}
this.#getOutlineStart(outline, N);
return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR);
}
#getOutlineTwoPoints(points) {
const last = this.#last;
const [layerX, layerY, layerWidth, layerHeight] = this.#box;
const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords();
const outline = new Float32Array(36);
outline.set([NaN, NaN, NaN, NaN, (last[2] - layerX) / layerWidth, (last[3] - layerY) / layerHeight, NaN, NaN, NaN, NaN, (last[4] - layerX) / layerWidth, (last[5] - layerY) / layerHeight, NaN, NaN, NaN, NaN, lastTopX, lastTopY, NaN, NaN, NaN, NaN, lastBottomX, lastBottomY, NaN, NaN, NaN, NaN, (last[16] - layerX) / layerWidth, (last[17] - layerY) / layerHeight, NaN, NaN, NaN, NaN, (last[14] - layerX) / layerWidth, (last[15] - layerY) / layerHeight], 0);
return this.newFreeDrawOutline(outline, points, this.#box, this.#scaleFactor, this.#innerMargin, this.#isLTR);
}
#getOutlineStart(outline, pos) {
const bottom = this.#bottom;
outline.set([NaN, NaN, NaN, NaN, bottom[4], bottom[5]], pos);
return pos += 6;
}
#getOutlineEnd(outline, pos) {
const lastTop = this.#last.subarray(4, 6);
const lastBottom = this.#last.subarray(16, 18);
const [layerX, layerY, layerWidth, layerHeight] = this.#box;
const [lastTopX, lastTopY, lastBottomX, lastBottomY] = this.#getLastCoords();
outline.set([NaN, NaN, NaN, NaN, (lastTop[0] - layerX) / layerWidth, (lastTop[1] - layerY) / layerHeight, NaN, NaN, NaN, NaN, lastTopX, lastTopY, NaN, NaN, NaN, NaN, lastBottomX, lastBottomY, NaN, NaN, NaN, NaN, (lastBottom[0] - layerX) / layerWidth, (lastBottom[1] - layerY) / layerHeight], pos);
return pos += 24;
}
}
class FreeDrawOutline extends Outline {
#box;
#bbox = new Float32Array(4);
#innerMargin;
#isLTR;
#points;
#scaleFactor;
#outline;
constructor(outline, points, box, scaleFactor, innerMargin, isLTR) {
super();
this.#outline = outline;
this.#points = points;
this.#box = box;
this.#scaleFactor = scaleFactor;
this.#innerMargin = innerMargin;
this.#isLTR = isLTR;
this.lastPoint = [NaN, NaN];
this.#computeMinMax(isLTR);
const [x, y, width, height] = this.#bbox;
for (let i = 0, ii = outline.length; i < ii; i += 2) {
outline[i] = (outline[i] - x) / width;
outline[i + 1] = (outline[i + 1] - y) / height;
}
for (let i = 0, ii = points.length; i < ii; i += 2) {
points[i] = (points[i] - x) / width;
points[i + 1] = (points[i + 1] - y) / height;
}
}
toSVGPath() {
const buffer = [`M${this.#outline[4]} ${this.#outline[5]}`];
for (let i = 6, ii = this.#outline.length; i < ii; i += 6) {
if (isNaN(this.#outline[i])) {
buffer.push(`L${this.#outline[i + 4]} ${this.#outline[i + 5]}`);
continue;
}
buffer.push(`C${this.#outline[i]} ${this.#outline[i + 1]} ${this.#outline[i + 2]} ${this.#outline[i + 3]} ${this.#outline[i + 4]} ${this.#outline[i + 5]}`);
}
buffer.push("Z");
return buffer.join(" ");
}
serialize([blX, blY, trX, trY], rotation) {
const width = trX - blX;
const height = trY - blY;
let outline;
let points;
switch (rotation) {
case 0:
outline = Outline._rescale(this.#outline, blX, trY, width, -height);
points = Outline._rescale(this.#points, blX, trY, width, -height);
break;
case 90:
outline = Outline._rescaleAndSwap(this.#outline, blX, blY, width, height);
points = Outline._rescaleAndSwap(this.#points, blX, blY, width, height);
break;
case 180:
outline = Outline._rescale(this.#outline, trX, blY, -width, height);
points = Outline._rescale(this.#points, trX, blY, -width, height);
break;
case 270:
outline = Outline._rescaleAndSwap(this.#outline, trX, trY, -width, -height);
points = Outline._rescaleAndSwap(this.#points, trX, trY, -width, -height);
break;
}
return {
outline: Array.from(outline),
points: [Array.from(points)]
};
}
#computeMinMax(isLTR) {
const outline = this.#outline;
let lastX = outline[4];
let lastY = outline[5];
let minX = lastX;
let minY = lastY;
let maxX = lastX;
let maxY = lastY;
let lastPointX = lastX;
let lastPointY = lastY;
const ltrCallback = isLTR ? Math.max : Math.min;
for (let i = 6, ii = outline.length; i < ii; i += 6) {
if (isNaN(outline[i])) {
minX = Math.min(minX, outline[i + 4]);
minY = Math.min(minY, outline[i + 5]);
maxX = Math.max(maxX, outline[i + 4]);
maxY = Math.max(maxY, outline[i + 5]);
if (lastPointY < outline[i + 5]) {
lastPointX = outline[i + 4];
lastPointY = outline[i + 5];
} else if (lastPointY === outline[i + 5]) {
lastPointX = ltrCallback(lastPointX, outline[i + 4]);
}
} else {
const bbox = util.Util.bezierBoundingBox(lastX, lastY, ...outline.slice(i, i + 6));
minX = Math.min(minX, bbox[0]);
minY = Math.min(minY, bbox[1]);
maxX = Math.max(maxX, bbox[2]);
maxY = Math.max(maxY, bbox[3]);
if (lastPointY < bbox[3]) {
lastPointX = bbox[2];
lastPointY = bbox[3];
} else if (lastPointY === bbox[3]) {
lastPointX = ltrCallback(lastPointX, bbox[2]);
}
}
lastX = outline[i + 4];
lastY = outline[i + 5];
}
const bbox = this.#bbox;
bbox[0] = minX - this.#innerMargin;
bbox[1] = minY - this.#innerMargin;
bbox[2] = maxX - minX + 2 * this.#innerMargin;
bbox[3] = maxY - minY + 2 * this.#innerMargin;
this.lastPoint = [lastPointX, lastPointY];
}
get box() {
return this.#bbox;
}
newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) {
return new FreeDrawOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin);
}
getNewOutline(thickness, innerMargin) {
const [x, y, width, height] = this.#bbox;
const [layerX, layerY, layerWidth, layerHeight] = this.#box;
const sx = width * layerWidth;
const sy = height * layerHeight;
const tx = x * layerWidth + layerX;
const ty = y * layerHeight + layerY;
const outliner = this.newOutliner({
x: this.#points[0] * sx + tx,
y: this.#points[1] * sy + ty
}, this.#box, this.#scaleFactor, thickness, this.#isLTR, innerMargin ?? this.#innerMargin);
for (let i = 2; i < this.#points.length; i += 2) {
outliner.add({
x: this.#points[i] * sx + tx,
y: this.#points[i + 1] * sy + ty
});
}
return outliner.getOutlines();
}
}
;// ./src/display/editor/drawers/highlight.js
class HighlightOutliner {
#box;
#lastPoint;
#verticalEdges = [];
#intervals = [];
constructor(boxes, borderWidth = 0, innerMargin = 0, isLTR = true) {
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
const NUMBER_OF_DIGITS = 4;
const EPSILON = 10 ** -NUMBER_OF_DIGITS;
for (const {
x,
y,
width,
height
} of boxes) {
const x1 = Math.floor((x - borderWidth) / EPSILON) * EPSILON;
const x2 = Math.ceil((x + width + borderWidth) / EPSILON) * EPSILON;
const y1 = Math.floor((y - borderWidth) / EPSILON) * EPSILON;
const y2 = Math.ceil((y + height + borderWidth) / EPSILON) * EPSILON;
const left = [x1, y1, y2, true];
const right = [x2, y1, y2, false];
this.#verticalEdges.push(left, right);
minX = Math.min(minX, x1);
maxX = Math.max(maxX, x2);
minY = Math.min(minY, y1);
maxY = Math.max(maxY, y2);
}
const bboxWidth = maxX - minX + 2 * innerMargin;
const bboxHeight = maxY - minY + 2 * innerMargin;
const shiftedMinX = minX - innerMargin;
const shiftedMinY = minY - innerMargin;
const lastEdge = this.#verticalEdges.at(isLTR ? -1 : -2);
const lastPoint = [lastEdge[0], lastEdge[2]];
for (const edge of this.#verticalEdges) {
const [x, y1, y2] = edge;
edge[0] = (x - shiftedMinX) / bboxWidth;
edge[1] = (y1 - shiftedMinY) / bboxHeight;
edge[2] = (y2 - shiftedMinY) / bboxHeight;
}
this.#box = new Float32Array([shiftedMinX, shiftedMinY, bboxWidth, bboxHeight]);
this.#lastPoint = lastPoint;
}
getOutlines() {
this.#verticalEdges.sort((a, b) => a[0] - b[0] || a[1] - b[1] || a[2] - b[2]);
const outlineVerticalEdges = [];
for (const edge of this.#verticalEdges) {
if (edge[3]) {
outlineVerticalEdges.push(...this.#breakEdge(edge));
this.#insert(edge);
} else {
this.#remove(edge);
outlineVerticalEdges.push(...this.#breakEdge(edge));
}
}
return this.#getOutlines(outlineVerticalEdges);
}
#getOutlines(outlineVerticalEdges) {
const edges = [];
const allEdges = new Set();
for (const edge of outlineVerticalEdges) {
const [x, y1, y2] = edge;
edges.push([x, y1, edge], [x, y2, edge]);
}
edges.sort((a, b) => a[1] - b[1] || a[0] - b[0]);
for (let i = 0, ii = edges.length; i < ii; i += 2) {
const edge1 = edges[i][2];
const edge2 = edges[i + 1][2];
edge1.push(edge2);
edge2.push(edge1);
allEdges.add(edge1);
allEdges.add(edge2);
}
const outlines = [];
let outline;
while (allEdges.size > 0) {
const edge = allEdges.values().next().value;
let [x, y1, y2, edge1, edge2] = edge;
allEdges.delete(edge);
let lastPointX = x;
let lastPointY = y1;
outline = [x, y2];
outlines.push(outline);
while (true) {
let e;
if (allEdges.has(edge1)) {
e = edge1;
} else if (allEdges.has(edge2)) {
e = edge2;
} else {
break;
}
allEdges.delete(e);
[x, y1, y2, edge1, edge2] = e;
if (lastPointX !== x) {
outline.push(lastPointX, lastPointY, x, lastPointY === y1 ? y1 : y2);
lastPointX = x;
}
lastPointY = lastPointY === y1 ? y2 : y1;
}
outline.push(lastPointX, lastPointY);
}
return new HighlightOutline(outlines, this.#box, this.#lastPoint);
}
#binarySearch(y) {
const array = this.#intervals;
let start = 0;
let end = array.length - 1;
while (start <= end) {
const middle = start + end >> 1;
const y1 = array[middle][0];
if (y1 === y) {
return middle;
}
if (y1 < y) {
start = middle + 1;
} else {
end = middle - 1;
}
}
return end + 1;
}
#insert([, y1, y2]) {
const index = this.#binarySearch(y1);
this.#intervals.splice(index, 0, [y1, y2]);
}
#remove([, y1, y2]) {
const index = this.#binarySearch(y1);
for (let i = index; i < this.#intervals.length; i++) {
const [start, end] = this.#intervals[i];
if (start !== y1) {
break;
}
if (start === y1 && end === y2) {
this.#intervals.splice(i, 1);
return;
}
}
for (let i = index - 1; i >= 0; i--) {
const [start, end] = this.#intervals[i];
if (start !== y1) {
break;
}
if (start === y1 && end === y2) {
this.#intervals.splice(i, 1);
return;
}
}
}
#breakEdge(edge) {
const [x, y1, y2] = edge;
const results = [[x, y1, y2]];
const index = this.#binarySearch(y2);
for (let i = 0; i < index; i++) {
const [start, end] = this.#intervals[i];
for (let j = 0, jj = results.length; j < jj; j++) {
const [, y3, y4] = results[j];
if (end <= y3 || y4 <= start) {
continue;
}
if (y3 >= start) {
if (y4 > end) {
results[j][1] = end;
} else {
if (jj === 1) {
return [];
}
results.splice(j, 1);
j--;
jj--;
}
continue;
}
results[j][2] = start;
if (y4 > end) {
results.push([x, end, y4]);
}
}
}
return results;
}
}
class HighlightOutline extends Outline {
#box;
#outlines;
constructor(outlines, box, lastPoint) {
super();
this.#outlines = outlines;
this.#box = box;
this.lastPoint = lastPoint;
}
toSVGPath() {
const buffer = [];
for (const polygon of this.#outlines) {
let [prevX, prevY] = polygon;
buffer.push(`M${prevX} ${prevY}`);
for (let i = 2; i < polygon.length; i += 2) {
const x = polygon[i];
const y = polygon[i + 1];
if (x === prevX) {
buffer.push(`V${y}`);
prevY = y;
} else if (y === prevY) {
buffer.push(`H${x}`);
prevX = x;
}
}
buffer.push("Z");
}
return buffer.join(" ");
}
serialize([blX, blY, trX, trY], _rotation) {
const outlines = [];
const width = trX - blX;
const height = trY - blY;
for (const outline of this.#outlines) {
const points = new Array(outline.length);
for (let i = 0; i < outline.length; i += 2) {
points[i] = blX + outline[i] * width;
points[i + 1] = trY - outline[i + 1] * height;
}
outlines.push(points);
}
return outlines;
}
get box() {
return this.#box;
}
get classNamesForOutlining() {
return ["highlightOutline"];
}
}
class FreeHighlightOutliner extends FreeDrawOutliner {
newFreeDrawOutline(outline, points, box, scaleFactor, innerMargin, isLTR) {
return new FreeHighlightOutline(outline, points, box, scaleFactor, innerMargin, isLTR);
}
}
class FreeHighlightOutline extends FreeDrawOutline {
newOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin = 0) {
return new FreeHighlightOutliner(point, box, scaleFactor, thickness, isLTR, innerMargin);
}
}
;// ./src/display/editor/color_picker.js
class ColorPicker {
#button = null;
#buttonSwatch = null;
#defaultColor;
#dropdown = null;
#dropdownWasFromKeyboard = false;
#isMainColorPicker = false;
#editor = null;
#eventBus;
#openDropdownAC = null;
#uiManager = null;
#type;
static #l10nColor = null;
static get _keyboardManager() {
return (0,util.shadow)(this, "_keyboardManager", new tools.KeyboardManager([[["Escape", "mac+Escape"], ColorPicker.prototype._hideDropdownFromKeyboard], [[" ", "mac+ "], ColorPicker.prototype._colorSelectFromKeyboard], [["ArrowDown", "ArrowRight", "mac+ArrowDown", "mac+ArrowRight"], ColorPicker.prototype._moveToNext], [["ArrowUp", "ArrowLeft", "mac+ArrowUp", "mac+ArrowLeft"], ColorPicker.prototype._moveToPrevious], [["Home", "mac+Home"], ColorPicker.prototype._moveToBeginning], [["End", "mac+End"], ColorPicker.prototype._moveToEnd]]));
}
constructor({
editor = null,
uiManager = null
}) {
if (editor) {
this.#isMainColorPicker = false;
this.#type = util.AnnotationEditorParamsType.HIGHLIGHT_COLOR;
this.#editor = editor;
} else {
this.#isMainColorPicker = true;
this.#type = util.AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR;
}
this.#uiManager = editor?._uiManager || uiManager;
this.#eventBus = this.#uiManager._eventBus;
this.#defaultColor = editor?.color || this.#uiManager?.highlightColors.values().next().value || "#FFFF98";
ColorPicker.#l10nColor ||= Object.freeze({
blue: "pdfjs-editor-colorpicker-blue",
green: "pdfjs-editor-colorpicker-green",
pink: "pdfjs-editor-colorpicker-pink",
red: "pdfjs-editor-colorpicker-red",
yellow: "pdfjs-editor-colorpicker-yellow"
});
}
renderButton() {
const button = this.#button = document.createElement("button");
button.className = "colorPicker";
button.tabIndex = "0";
button.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-button");
button.setAttribute("aria-haspopup", true);
const signal = this.#uiManager._signal;
button.addEventListener("click", this.#openDropdown.bind(this), {
signal
});
button.addEventListener("keydown", this.#keyDown.bind(this), {
signal
});
const swatch = this.#buttonSwatch = document.createElement("span");
swatch.className = "swatch";
swatch.setAttribute("aria-hidden", true);
swatch.style.backgroundColor = this.#defaultColor;
button.append(swatch);
return button;
}
renderMainDropdown() {
const dropdown = this.#dropdown = this.#getDropdownRoot();
dropdown.setAttribute("aria-orientation", "horizontal");
dropdown.setAttribute("aria-labelledby", "highlightColorPickerLabel");
return dropdown;
}
#getDropdownRoot() {
const div = document.createElement("div");
const signal = this.#uiManager._signal;
div.addEventListener("contextmenu", display_utils.noContextMenu, {
signal
});
div.className = "dropdown";
div.role = "listbox";
div.setAttribute("aria-multiselectable", false);
div.setAttribute("aria-orientation", "vertical");
div.setAttribute("data-l10n-id", "pdfjs-editor-colorpicker-dropdown");
for (const [name, color] of this.#uiManager.highlightColors) {
const button = document.createElement("button");
button.tabIndex = "0";
button.role = "option";
button.setAttribute("data-color", color);
button.title = name;
button.setAttribute("data-l10n-id", ColorPicker.#l10nColor[name]);
const swatch = document.createElement("span");
button.append(swatch);
swatch.className = "swatch";
swatch.style.backgroundColor = color;
button.setAttribute("aria-selected", color === this.#defaultColor);
button.addEventListener("click", this.#colorSelect.bind(this, color), {
signal
});
div.append(button);
}
div.addEventListener("keydown", this.#keyDown.bind(this), {
signal
});
return div;
}
#colorSelect(color, event) {
event.stopPropagation();
this.#eventBus.dispatch("switchannotationeditorparams", {
source: this,
type: this.#type,
value: color
});
}
_colorSelectFromKeyboard(event) {
if (event.target === this.#button) {
this.#openDropdown(event);
return;
}
const color = event.target.getAttribute("data-color");
if (!color) {
return;
}
this.#colorSelect(color, event);
}
_moveToNext(event) {
if (!this.#isDropdownVisible) {
this.#openDropdown(event);
return;
}
if (event.target === this.#button) {
this.#dropdown.firstChild?.focus();
return;
}
event.target.nextSibling?.focus();
}
_moveToPrevious(event) {
if (event.target === this.#dropdown?.firstChild || event.target === this.#button) {
if (this.#isDropdownVisible) {
this._hideDropdownFromKeyboard();
}
return;
}
if (!this.#isDropdownVisible) {
this.#openDropdown(event);
}
event.target.previousSibling?.focus();
}
_moveToBeginning(event) {
if (!this.#isDropdownVisible) {
this.#openDropdown(event);
return;
}
this.#dropdown.firstChild?.focus();
}
_moveToEnd(event) {
if (!this.#isDropdownVisible) {
this.#openDropdown(event);
return;
}
this.#dropdown.lastChild?.focus();
}
#keyDown(event) {
ColorPicker._keyboardManager.exec(this, event);
}
#openDropdown(event) {
if (this.#isDropdownVisible) {
this.hideDropdown();
return;
}
this.#dropdownWasFromKeyboard = event.detail === 0;
if (!this.#openDropdownAC) {
this.#openDropdownAC = new AbortController();
window.addEventListener("pointerdown", this.#pointerDown.bind(this), {
signal: this.#uiManager.combinedSignal(this.#openDropdownAC)
});
}
if (this.#dropdown) {
this.#dropdown.classList.remove("hidden");
return;
}
const root = this.#dropdown = this.#getDropdownRoot();
this.#button.append(root);
}
#pointerDown(event) {
if (this.#dropdown?.contains(event.target)) {
return;
}
this.hideDropdown();
}
hideDropdown() {
this.#dropdown?.classList.add("hidden");
this.#openDropdownAC?.abort();
this.#openDropdownAC = null;
}
get #isDropdownVisible() {
return this.#dropdown && !this.#dropdown.classList.contains("hidden");
}
_hideDropdownFromKeyboard() {
if (this.#isMainColorPicker) {
return;
}
if (!this.#isDropdownVisible) {
this.#editor?.unselect();
return;
}
this.hideDropdown();
this.#button.focus({
preventScroll: true,
focusVisible: this.#dropdownWasFromKeyboard
});
}
updateColor(color) {
if (this.#buttonSwatch) {
this.#buttonSwatch.style.backgroundColor = color;
}
if (!this.#dropdown) {
return;
}
const i = this.#uiManager.highlightColors.values();
for (const child of this.#dropdown.children) {
child.setAttribute("aria-selected", i.next().value === color);
}
}
destroy() {
this.#button?.remove();
this.#button = null;
this.#buttonSwatch = null;
this.#dropdown?.remove();
this.#dropdown = null;
}
}
;// ./src/display/editor/highlight.js
class HighlightEditor extends editor_editor.AnnotationEditor {
#anchorNode = null;
#anchorOffset = 0;
#boxes;
#clipPathId = null;
#colorPicker = null;
#focusOutlines = null;
#focusNode = null;
#focusOffset = 0;
#highlightDiv = null;
#highlightOutlines = null;
#id = null;
#isFreeHighlight = false;
#lastPoint = null;
#opacity;
#outlineId = null;
#text = "";
#thickness;
#methodOfCreation = "";
static _defaultColor = null;
static _defaultOpacity = 1;
static _defaultThickness = 12;
static _type = "highlight";
static _editorType = util.AnnotationEditorType.HIGHLIGHT;
static _freeHighlightId = -1;
static _freeHighlight = null;
static _freeHighlightClipId = "";
static get _keyboardManager() {
const proto = HighlightEditor.prototype;
return (0,util.shadow)(this, "_keyboardManager", new tools.KeyboardManager([[["ArrowLeft", "mac+ArrowLeft"], proto._moveCaret, {
args: [0]
}], [["ArrowRight", "mac+ArrowRight"], proto._moveCaret, {
args: [1]
}], [["ArrowUp", "mac+ArrowUp"], proto._moveCaret, {
args: [2]
}], [["ArrowDown", "mac+ArrowDown"], proto._moveCaret, {
args: [3]
}]]));
}
constructor(params) {
super({
...params,
name: "highlightEditor"
});
this.color = params.color || HighlightEditor._defaultColor;
this.#thickness = params.thickness || HighlightEditor._defaultThickness;
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
this.#boxes = params.boxes || null;
this.#methodOfCreation = params.methodOfCreation || "";
this.#text = params.text || "";
this._isDraggable = false;
if (params.highlightId > -1) {
this.#isFreeHighlight = true;
this.#createFreeOutlines(params);
this.#addToDrawLayer();
} else if (this.#boxes) {
this.#anchorNode = params.anchorNode;
this.#anchorOffset = params.anchorOffset;
this.#focusNode = params.focusNode;
this.#focusOffset = params.focusOffset;
this.#createOutlines();
this.#addToDrawLayer();
this.rotate(this.rotation);
}
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "added",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: {
color: this.color,
thickness: this.#thickness,
isFreeHighlight: this.#isFreeHighlight,
text: this.#text
}
});
}
get telemetryInitialData() {
return {
action: "added",
type: this.#isFreeHighlight ? "free_highlight" : "highlight",
color: this._uiManager.highlightColorNames.get(this.color),
thickness: this.#thickness,
methodOfCreation: this.#methodOfCreation
};
}
get telemetryFinalData() {
return {
type: "highlight",
color: this._uiManager.highlightColorNames.get(this.color)
};
}
static computeTelemetryFinalData(data) {
return {
numberOfColors: data.get("color").size
};
}
#createOutlines() {
const outliner = new HighlightOutliner(this.#boxes, 0.001);
this.#highlightOutlines = outliner.getOutlines();
[this.x, this.y, this.width, this.height] = this.#highlightOutlines.box;
const outlinerForOutline = new HighlightOutliner(this.#boxes, 0.0025, 0.001, this._uiManager.direction === "ltr");
this.#focusOutlines = outlinerForOutline.getOutlines();
const {
lastPoint
} = this.#focusOutlines;
this.#lastPoint = [(lastPoint[0] - this.x) / this.width, (lastPoint[1] - this.y) / this.height];
}
#createFreeOutlines({
highlightOutlines,
highlightId,
clipPathId
}) {
this.#highlightOutlines = highlightOutlines;
const extraThickness = 1.5;
this.#focusOutlines = highlightOutlines.getNewOutline(this.#thickness / 2 + extraThickness, 0.0025);
if (highlightId >= 0) {
this.#id = highlightId;
this.#clipPathId = clipPathId;
this.parent.drawLayer.finalizeDraw(highlightId, {
bbox: highlightOutlines.box,
path: {
d: highlightOutlines.toSVGPath()
}
});
this.#outlineId = this.parent.drawLayer.drawOutline({
rootClass: {
highlightOutline: true,
free: true
},
bbox: this.#focusOutlines.box,
path: {
d: this.#focusOutlines.toSVGPath()
}
}, true);
} else if (this.parent) {
const angle = this.parent.viewport.rotation;
this.parent.drawLayer.updateProperties(this.#id, {
bbox: HighlightEditor.#rotateBbox(this.#highlightOutlines.box, (angle - this.rotation + 360) % 360),
path: {
d: highlightOutlines.toSVGPath()
}
});
this.parent.drawLayer.updateProperties(this.#outlineId, {
bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle),
path: {
d: this.#focusOutlines.toSVGPath()
}
});
}
const [x, y, width, height] = highlightOutlines.box;
switch (this.rotation) {
case 0:
this.x = x;
this.y = y;
this.width = width;
this.height = height;
break;
case 90:
{
const [pageWidth, pageHeight] = this.parentDimensions;
this.x = y;
this.y = 1 - x;
this.width = width * pageHeight / pageWidth;
this.height = height * pageWidth / pageHeight;
break;
}
case 180:
this.x = 1 - x;
this.y = 1 - y;
this.width = width;
this.height = height;
break;
case 270:
{
const [pageWidth, pageHeight] = this.parentDimensions;
this.x = 1 - y;
this.y = x;
this.width = width * pageHeight / pageWidth;
this.height = height * pageWidth / pageHeight;
break;
}
}
const {
lastPoint
} = this.#focusOutlines;
this.#lastPoint = [(lastPoint[0] - x) / width, (lastPoint[1] - y) / height];
}
static initialize(l10n, uiManager) {
editor_editor.AnnotationEditor.initialize(l10n, uiManager);
HighlightEditor._defaultColor ||= uiManager.highlightColors?.values().next().value || "#fff066";
}
static updateDefaultParams(type, value) {
switch (type) {
case util.AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR:
HighlightEditor._defaultColor = value;
break;
case util.AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
HighlightEditor._defaultThickness = value;
break;
}
}
translateInPage(x, y) {}
get toolbarPosition() {
return this.#lastPoint;
}
updateParams(type, value) {
switch (type) {
case util.AnnotationEditorParamsType.HIGHLIGHT_COLOR:
this.#updateColor(value);
break;
case util.AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
this.#updateThickness(value);
break;
}
}
static get defaultPropertiesToUpdate() {
return [[util.AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR, HighlightEditor._defaultColor], [util.AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, HighlightEditor._defaultThickness]];
}
get propertiesToUpdate() {
return [[util.AnnotationEditorParamsType.HIGHLIGHT_COLOR, this.color || HighlightEditor._defaultColor], [util.AnnotationEditorParamsType.HIGHLIGHT_THICKNESS, this.#thickness || HighlightEditor._defaultThickness], [util.AnnotationEditorParamsType.HIGHLIGHT_FREE, this.#isFreeHighlight]];
}
#updateColor(color) {
const setColorAndOpacity = (col, opa) => {
this.color = col;
this.#opacity = opa;
this.parent?.drawLayer.updateProperties(this.#id, {
root: {
fill: col,
"fill-opacity": opa
}
});
this.#colorPicker?.updateColor(col);
};
const savedColor = this.color;
const savedOpacity = this.#opacity;
this.addCommands({
cmd: setColorAndOpacity.bind(this, color, HighlightEditor._defaultOpacity),
undo: setColorAndOpacity.bind(this, savedColor, savedOpacity),
post: this._uiManager.updateUI.bind(this._uiManager, this),
mustExec: true,
type: util.AnnotationEditorParamsType.HIGHLIGHT_COLOR,
overwriteIfSameType: true,
keepUndo: true
});
this._reportTelemetry({
action: "color_changed",
color: this._uiManager.highlightColorNames.get(color)
}, true);
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "colorChanged",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: color,
previousValue: savedColor
});
}
#updateThickness(thickness) {
const savedThickness = this.#thickness;
const setThickness = th => {
this.#thickness = th;
this.#changeThickness(th);
};
this.addCommands({
cmd: setThickness.bind(this, thickness),
undo: setThickness.bind(this, savedThickness),
post: this._uiManager.updateUI.bind(this._uiManager, this),
mustExec: true,
type: util.AnnotationEditorParamsType.INK_THICKNESS,
overwriteIfSameType: true,
keepUndo: true
});
this._reportTelemetry({
action: "thickness_changed",
thickness
}, true);
this.eventBus?.dispatch("annotation-editor-event", {
source: this,
type: "thicknessChanged",
page: this.pageIndex + 1,
editorType: this.constructor.name,
value: thickness,
previousValue: savedThickness
});
}
async addEditToolbar() {
const toolbar = await super.addEditToolbar();
if (!toolbar) {
return null;
}
if (this._uiManager.highlightColors) {
this.#colorPicker = new ColorPicker({
editor: this
});
toolbar.addColorPicker(this.#colorPicker);
}
return toolbar;
}
disableEditing() {
super.disableEditing();
this.div.classList.toggle("disabled", true);
}
enableEditing() {
super.enableEditing();
this.div.classList.toggle("disabled", false);
}
fixAndSetPosition() {
return super.fixAndSetPosition(this.#getRotation());
}
getBaseTranslation() {
return [0, 0];
}
getRect(tx, ty) {
return super.getRect(tx, ty, this.#getRotation());
}
onceAdded(focus) {
if (!this.annotationElementId) {
this.parent.addUndoableEditor(this);
}
if (focus) {
this.div.focus();
}
}
remove() {
this.#cleanDrawLayer();
this._reportTelemetry({
action: "deleted"
});
super.remove();
}
rebuild() {
if (!this.parent) {
return;
}
super.rebuild();
if (this.div === null) {
return;
}
this.#addToDrawLayer();
if (!this.isAttachedToDOM) {
this.parent.add(this);
}
}
setParent(parent) {
let mustBeSelected = false;
if (this.parent && !parent) {
this.#cleanDrawLayer();
} else if (parent) {
this.#addToDrawLayer(parent);
mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor");
}
super.setParent(parent);
this.show(this._isVisible);
if (mustBeSelected) {
this.select();
}
}
#changeThickness(thickness) {
if (!this.#isFreeHighlight) {
return;
}
this.#createFreeOutlines({
highlightOutlines: this.#highlightOutlines.getNewOutline(thickness / 2)
});
this.fixAndSetPosition();
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(this.width * parentWidth, this.height * parentHeight);
}
#cleanDrawLayer() {
if (this.#id === null || !this.parent) {
return;
}
this.parent.drawLayer.remove(this.#id);
this.#id = null;
this.parent.drawLayer.remove(this.#outlineId);
this.#outlineId = null;
}
#addToDrawLayer(parent = this.parent) {
if (this.#id !== null) {
return;
}
({
id: this.#id,
clipPathId: this.#clipPathId
} = parent.drawLayer.draw({
bbox: this.#highlightOutlines.box,
root: {
viewBox: "0 0 1 1",
fill: this.color,
"fill-opacity": this.#opacity
},
rootClass: {
highlight: true,
free: this.#isFreeHighlight
},
path: {
d: this.#highlightOutlines.toSVGPath()
}
}, false, true));
this.#outlineId = parent.drawLayer.drawOutline({
rootClass: {
highlightOutline: true,
free: this.#isFreeHighlight
},
bbox: this.#focusOutlines.box,
path: {
d: this.#focusOutlines.toSVGPath()
}
}, this.#isFreeHighlight);
if (this.#highlightDiv) {
this.#highlightDiv.style.clipPath = this.#clipPathId;
}
}
static #rotateBbox([x, y, width, height], angle) {
switch (angle) {
case 90:
return [1 - y - height, x, height, width];
case 180:
return [1 - x - width, 1 - y - height, width, height];
case 270:
return [y, 1 - x - width, height, width];
}
return [x, y, width, height];
}
rotate(angle) {
const {
drawLayer
} = this.parent;
let box;
if (this.#isFreeHighlight) {
angle = (angle - this.rotation + 360) % 360;
box = HighlightEditor.#rotateBbox(this.#highlightOutlines.box, angle);
} else {
box = HighlightEditor.#rotateBbox([this.x, this.y, this.width, this.height], angle);
}
drawLayer.updateProperties(this.#id, {
bbox: box,
root: {
"data-main-rotation": angle
}
});
drawLayer.updateProperties(this.#outlineId, {
bbox: HighlightEditor.#rotateBbox(this.#focusOutlines.box, angle),
root: {
"data-main-rotation": angle
}
});
}
render() {
if (this.div) {
return this.div;
}
const div = super.render();
if (this.#text) {
div.setAttribute("aria-label", this.#text);
div.setAttribute("role", "mark");
}
if (this.#isFreeHighlight) {
div.classList.add("free");
} else {
this.div.addEventListener("keydown", this.#keydown.bind(this), {
signal: this._uiManager._signal
});
}
const highlightDiv = this.#highlightDiv = document.createElement("div");
div.append(highlightDiv);
highlightDiv.setAttribute("aria-hidden", "true");
highlightDiv.className = "internal";
highlightDiv.style.clipPath = this.#clipPathId;
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(this.width * parentWidth, this.height * parentHeight);
(0,tools.bindEvents)(this, this.#highlightDiv, ["pointerover", "pointerleave"]);
this.enableEditing();
return div;
}
pointerover() {
if (!this.isSelected) {
this.parent?.drawLayer.updateProperties(this.#outlineId, {
rootClass: {
hovered: true
}
});
}
}
pointerleave() {
if (!this.isSelected) {
this.parent?.drawLayer.updateProperties(this.#outlineId, {
rootClass: {
hovered: false
}
});
}
}
#keydown(event) {
HighlightEditor._keyboardManager.exec(this, event);
}
_moveCaret(direction) {
this.parent.unselect(this);
switch (direction) {
case 0:
case 2:
this.#setCaret(true);
break;
case 1:
case 3:
this.#setCaret(false);
break;
}
}
#setCaret(start) {
if (!this.#anchorNode) {
return;
}
const selection = window.getSelection();
if (start) {
selection.setPosition(this.#anchorNode, this.#anchorOffset);
} else {
selection.setPosition(this.#focusNode, this.#focusOffset);
}
}
select() {
super.select();
if (!this.#outlineId) {
return;
}
this.parent?.drawLayer.updateProperties(this.#outlineId, {
rootClass: {
hovered: false,
selected: true
}
});
}
unselect() {
super.unselect();
if (!this.#outlineId) {
return;
}
this.parent?.drawLayer.updateProperties(this.#outlineId, {
rootClass: {
selected: false
}
});
if (!this.#isFreeHighlight) {
this.#setCaret(false);
}
}
get _mustFixPosition() {
return !this.#isFreeHighlight;
}
show(visible = this._isVisible) {
super.show(visible);
if (this.parent) {
this.parent.drawLayer.updateProperties(this.#id, {
rootClass: {
hidden: !visible
}
});
this.parent.drawLayer.updateProperties(this.#outlineId, {
rootClass: {
hidden: !visible
}
});
}
}
#getRotation() {
return this.#isFreeHighlight ? this.rotation : 0;
}
#serializeBoxes() {
if (this.#isFreeHighlight) {
return null;
}
const [pageWidth, pageHeight] = this.pageDimensions;
const [pageX, pageY] = this.pageTranslation;
const boxes = this.#boxes;
const quadPoints = new Float32Array(boxes.length * 8);
let i = 0;
for (const {
x,
y,
width,
height
} of boxes) {
const sx = x * pageWidth + pageX;
const sy = (1 - y) * pageHeight + pageY;
quadPoints[i] = quadPoints[i + 4] = sx;
quadPoints[i + 1] = quadPoints[i + 3] = sy;
quadPoints[i + 2] = quadPoints[i + 6] = sx + width * pageWidth;
quadPoints[i + 5] = quadPoints[i + 7] = sy - height * pageHeight;
i += 8;
}
return quadPoints;
}
#serializeOutlines(rect) {
return this.#highlightOutlines.serialize(rect, this.#getRotation());
}
static startHighlighting(parent, isLTR, {
target: textLayer,
x,
y
}) {
const {
x: layerX,
y: layerY,
width: parentWidth,
height: parentHeight
} = textLayer.getBoundingClientRect();
const ac = new AbortController();
const signal = parent.combinedSignal(ac);
const pointerUpCallback = e => {
ac.abort();
this.#endHighlight(parent, e);
};
window.addEventListener("blur", pointerUpCallback, {
signal
});
window.addEventListener("pointerup", pointerUpCallback, {
signal
});
window.addEventListener("pointerdown", display_utils.stopEvent, {
capture: true,
passive: false,
signal
});
window.addEventListener("contextmenu", display_utils.noContextMenu, {
signal
});
textLayer.addEventListener("pointermove", this.#highlightMove.bind(this, parent), {
signal
});
this._freeHighlight = new FreeHighlightOutliner({
x,
y
}, [layerX, layerY, parentWidth, parentHeight], parent.scale, this._defaultThickness / 2, isLTR, 0.001);
({
id: this._freeHighlightId,
clipPathId: this._freeHighlightClipId
} = parent.drawLayer.draw({
bbox: [0, 0, 1, 1],
root: {
viewBox: "0 0 1 1",
fill: this._defaultColor,
"fill-opacity": this._defaultOpacity
},
rootClass: {
highlight: true,
free: true
},
path: {
d: this._freeHighlight.toSVGPath()
}
}, true, true));
}
static #highlightMove(parent, event) {
if (this._freeHighlight.add(event)) {
parent.drawLayer.updateProperties(this._freeHighlightId, {
path: {
d: this._freeHighlight.toSVGPath()
}
});
}
}
static #endHighlight(parent, event) {
if (!this._freeHighlight.isEmpty()) {
parent.createAndAddNewEditor(event, false, {
highlightId: this._freeHighlightId,
highlightOutlines: this._freeHighlight.getOutlines(),
clipPathId: this._freeHighlightClipId,
methodOfCreation: "main_toolbar"
});
} else {
parent.drawLayer.remove(this._freeHighlightId);
}
this._freeHighlightId = -1;
this._freeHighlight = null;
this._freeHighlightClipId = "";
}
static async deserialize(data, parent, uiManager) {
let initialData = null;
if (data instanceof HighlightAnnotationElement) {
const {
data: {
quadPoints,
rect,
rotation,
id,
color,
opacity,
popupRef
},
parent: {
page: {
pageNumber
}
}
} = data;
initialData = data = {
annotationType: util.AnnotationEditorType.HIGHLIGHT,
color: Array.from(color),
opacity,
quadPoints,
boxes: null,
pageIndex: pageNumber - 1,
rect: rect.slice(0),
rotation,
id,
deleted: false,
popupRef
};
} else if (data.annotationType && data.annotationType === util.AnnotationEditorType.HIGHLIGHT) {
let {
quadPoints,
outlines,
rect,
rotation,
id,
color,
opacity,
popupRef,
pageIndex,
thickness
} = data;
if (quadPoints) {
if (!Array.isArray(quadPoints)) {
quadPoints = Object.values(quadPoints);
}
}
let inkLists;
if (!quadPoints && outlines) {
if (Array.isArray(outlines)) {
inkLists = outlines.map(subArray => subArray);
} else if (typeof outlines === "object" && outlines.points) {
inkLists = [outlines.points.flat()];
} else {
console.error("Unexpected outlines format");
return null;
}
thickness = thickness || data.thickness || 1;
}
initialData = data = {
annotationType: util.AnnotationEditorType.HIGHLIGHT,
color: Array.from(color),
opacity,
quadPoints,
inkLists,
thickness,
boxes: null,
pageIndex,
rect: rect.slice(0),
rotation,
id,
deleted: false,
popupRef: popupRef || null
};
} else if (data instanceof InkAnnotationElement) {
const {
data: {
inkLists,
rect,
rotation,
id,
color,
borderStyle: {
rawWidth: thickness
},
popupRef
},
parent: {
page: {
pageNumber
}
}
} = data;
initialData = data = {
annotationType: util.AnnotationEditorType.HIGHLIGHT,
color: Array.from(color),
thickness,
inkLists,
boxes: null,
pageIndex: pageNumber - 1,
rect: rect.slice(0),
rotation,
id,
deleted: false,
popupRef
};
}
const {
color,
quadPoints,
inkLists,
opacity
} = data;
const editor = await super.deserialize(data, parent, uiManager);
editor.color = util.Util.makeHexColor(...color);
editor.#opacity = opacity || 1;
if (inkLists) {
editor.#thickness = data.thickness;
}
editor.annotationElementId = data.id || null;
editor._initialData = initialData;
const [pageWidth, pageHeight] = editor.pageDimensions;
const [pageX, pageY] = editor.pageTranslation;
if (quadPoints) {
const boxes = editor.#boxes = [];
for (let i = 0; i < quadPoints.length; i += 8) {
boxes.push({
x: (quadPoints[i] - pageX) / pageWidth,
y: 1 - (quadPoints[i + 1] - pageY) / pageHeight,
width: (quadPoints[i + 2] - quadPoints[i]) / pageWidth,
height: (quadPoints[i + 1] - quadPoints[i + 5]) / pageHeight
});
}
editor.#createOutlines();
editor.#addToDrawLayer();
editor.rotate(editor.rotation);
} else if (inkLists) {
editor.#isFreeHighlight = true;
const points = inkLists[0];
const point = {
x: points[0] - pageX,
y: pageHeight - (points[1] - pageY)
};
const outliner = new FreeHighlightOutliner(point, [0, 0, pageWidth, pageHeight], 1, editor.#thickness / 2, true, 0.001);
for (let i = 0, ii = points.length; i < ii; i += 2) {
point.x = points[i] - pageX;
point.y = pageHeight - (points[i + 1] - pageY);
outliner.add(point);
}
const {
id,
clipPathId
} = parent.drawLayer.draw({
bbox: [0, 0, 1, 1],
root: {
viewBox: "0 0 1 1",
fill: editor.color,
"fill-opacity": editor._defaultOpacity
},
rootClass: {
highlight: true,
free: true
},
path: {
d: outliner.toSVGPath()
}
}, true, true);
editor.#createFreeOutlines({
highlightOutlines: outliner.getOutlines(),
highlightId: id,
clipPathId
});
editor.#addToDrawLayer();
}
return editor;
}
serialize(isForCopying = false) {
if (this.isEmpty() || isForCopying) {
return null;
}
if (this.deleted) {
return this.serializeDeleted();
}
const rect = this.getRect(0, 0);
const color = editor_editor.AnnotationEditor._colorManager.convert(this.color);
const serialized = {
annotationType: util.AnnotationEditorType.HIGHLIGHT,
color,
opacity: this.#opacity,
thickness: this.#thickness,
quadPoints: this.#serializeBoxes(),
outlines: this.#serializeOutlines(rect),
pageIndex: this.pageIndex,
rect,
rotation: this.#getRotation(),
structTreeParentId: this._structTreeParentId
};
if (this.annotationElementId && !this.#hasElementChanged(serialized)) {
return null;
}
serialized.id = this.annotationElementId;
return serialized;
}
#hasElementChanged(serialized) {
const {
color
} = this._initialData;
return serialized.color.some((c, i) => c !== color[i]);
}
renderAnnotationElement(annotation) {
annotation.updateEdited({
rect: this.getRect(0, 0)
});
return null;
}
static canCreateNewEmptyEditor() {
return false;
}
}
;// ./src/display/editor/draw.js
class DrawingOptions {
#svgProperties = Object.create(null);
updateProperty(name, value) {
this[name] = value;
this.updateSVGProperty(name, value);
}
updateProperties(properties) {
if (!properties) {
return;
}
for (const [name, value] of Object.entries(properties)) {
this.updateProperty(name, value);
}
}
updateSVGProperty(name, value) {
this.#svgProperties[name] = value;
}
toSVGProperties() {
const root = this.#svgProperties;
this.#svgProperties = Object.create(null);
return {
root
};
}
reset() {
this.#svgProperties = Object.create(null);
}
updateAll(options = this) {
this.updateProperties(options);
}
clone() {
(0,util.unreachable)("Not implemented");
}
}
class DrawingEditor extends editor_editor.AnnotationEditor {
#drawOutlines = null;
#mustBeCommitted;
_drawId = null;
static _currentDrawId = -1;
static _currentParent = null;
static #currentDraw = null;
static #currentDrawingAC = null;
static #currentDrawingOptions = null;
static #currentPointerId = NaN;
static #currentPointerType = null;
static #currentPointerIds = null;
static #currentMoveTimestamp = NaN;
static _INNER_MARGIN = 3;
constructor(params) {
super(params);
this.#mustBeCommitted = params.mustBeCommitted || false;
if (params.drawOutlines) {
this.#createDrawOutlines(params);
this.#addToDrawLayer();
}
}
#createDrawOutlines({
drawOutlines,
drawId,
drawingOptions
}) {
this.#drawOutlines = drawOutlines;
this._drawingOptions ||= drawingOptions;
if (drawId >= 0) {
this._drawId = drawId;
this.parent.drawLayer.finalizeDraw(drawId, drawOutlines.defaultProperties);
} else {
this._drawId = this.#createDrawing(drawOutlines, this.parent);
}
this.#updateBbox(drawOutlines.box);
}
#createDrawing(drawOutlines, parent) {
const {
id
} = parent.drawLayer.draw(DrawingEditor._mergeSVGProperties(this._drawingOptions.toSVGProperties(), drawOutlines.defaultSVGProperties), false, false);
return id;
}
static _mergeSVGProperties(p1, p2) {
const p1Keys = new Set(Object.keys(p1));
for (const [key, value] of Object.entries(p2)) {
if (p1Keys.has(key)) {
Object.assign(p1[key], value);
} else {
p1[key] = value;
}
}
return p1;
}
static getDefaultDrawingOptions(_options) {
(0,util.unreachable)("Not implemented");
}
static get typesMap() {
(0,util.unreachable)("Not implemented");
}
static get isDrawer() {
return true;
}
static get supportMultipleDrawings() {
return false;
}
static updateDefaultParams(type, value) {
const propertyName = this.typesMap.get(type);
if (propertyName) {
this._defaultDrawingOptions.updateProperty(propertyName, value);
}
if (this._currentParent) {
DrawingEditor.#currentDraw.updateProperty(propertyName, value);
this._currentParent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties());
}
}
updateParams(type, value) {
const propertyName = this.constructor.typesMap.get(type);
if (propertyName) {
this._updateProperty(type, propertyName, value);
}
}
static get defaultPropertiesToUpdate() {
const properties = [];
const options = this._defaultDrawingOptions;
for (const [type, name] of this.typesMap) {
properties.push([type, options[name]]);
}
return properties;
}
get propertiesToUpdate() {
const properties = [];
const {
_drawingOptions
} = this;
for (const [type, name] of this.constructor.typesMap) {
properties.push([type, _drawingOptions[name]]);
}
return properties;
}
_updateProperty(type, name, value) {
const options = this._drawingOptions;
const savedValue = options[name];
const setter = val => {
options.updateProperty(name, val);
const bbox = this.#drawOutlines.updateProperty(name, val);
if (bbox) {
this.#updateBbox(bbox);
}
this.parent?.drawLayer.updateProperties(this._drawId, options.toSVGProperties());
};
this.addCommands({
cmd: setter.bind(this, value),
undo: setter.bind(this, savedValue),
post: this._uiManager.updateUI.bind(this._uiManager, this),
mustExec: true,
type,
overwriteIfSameType: true,
keepUndo: true
});
}
_onResizing() {
this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizingSVGProperties(this.#convertToDrawSpace()), {
bbox: this.#rotateBox()
}));
}
_onResized() {
this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathResizedSVGProperties(this.#convertToDrawSpace()), {
bbox: this.#rotateBox()
}));
}
_onTranslating(x, y) {
this.parent?.drawLayer.updateProperties(this._drawId, {
bbox: this.#rotateBox(x, y)
});
}
_onTranslated() {
this.parent?.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties(this.#drawOutlines.getPathTranslatedSVGProperties(this.#convertToDrawSpace(), this.parentDimensions), {
bbox: this.#rotateBox()
}));
}
_onStartDragging() {
this.parent?.drawLayer.updateProperties(this._drawId, {
rootClass: {
moving: true
}
});
}
_onStopDragging() {
this.parent?.drawLayer.updateProperties(this._drawId, {
rootClass: {
moving: false
}
});
}
commit() {
super.commit();
this.disableEditMode();
this.disableEditing();
}
disableEditing() {
super.disableEditing();
this.div.classList.toggle("disabled", true);
}
enableEditing() {
super.enableEditing();
this.div.classList.toggle("disabled", false);
}
getBaseTranslation() {
return [0, 0];
}
get isResizable() {
return true;
}
onceAdded(focus) {
if (!this.annotationElementId) {
this.parent.addUndoableEditor(this);
}
this._isDraggable = true;
if (this.#mustBeCommitted) {
this.#mustBeCommitted = false;
this.commit();
this.parent.setSelected(this);
if (focus && this.isOnScreen) {
this.div.focus();
}
}
}
remove() {
this.#cleanDrawLayer();
super.remove();
}
rebuild() {
if (!this.parent) {
return;
}
super.rebuild();
if (this.div === null) {
return;
}
this.#addToDrawLayer();
this.#updateBbox(this.#drawOutlines.box);
if (!this.isAttachedToDOM) {
this.parent.add(this);
}
}
setParent(parent) {
let mustBeSelected = false;
if (this.parent && !parent) {
this._uiManager.removeShouldRescale(this);
this.#cleanDrawLayer();
} else if (parent) {
this._uiManager.addShouldRescale(this);
this.#addToDrawLayer(parent);
mustBeSelected = !this.parent && this.div?.classList.contains("selectedEditor");
}
super.setParent(parent);
if (mustBeSelected) {
this.select();
}
}
#cleanDrawLayer() {
if (this._drawId === null || !this.parent) {
return;
}
this.parent.drawLayer.remove(this._drawId);
this._drawId = null;
this._drawingOptions.reset();
}
#addToDrawLayer(parent = this.parent) {
if (this._drawId !== null && this.parent === parent) {
return;
}
if (this._drawId !== null) {
this.parent.drawLayer.updateParent(this._drawId, parent.drawLayer);
return;
}
this._drawingOptions.updateAll();
this._drawId = this.#createDrawing(this.#drawOutlines, parent);
}
#convertToParentSpace([x, y, width, height]) {
const {
parentDimensions: [pW, pH],
rotation
} = this;
switch (rotation) {
case 90:
return [y, 1 - x, width * (pH / pW), height * (pW / pH)];
case 180:
return [1 - x, 1 - y, width, height];
case 270:
return [1 - y, x, width * (pH / pW), height * (pW / pH)];
default:
return [x, y, width, height];
}
}
#convertToDrawSpace() {
const {
x,
y,
width,
height,
parentDimensions: [pW, pH],
rotation
} = this;
switch (rotation) {
case 90:
return [1 - y, x, width * (pW / pH), height * (pH / pW)];
case 180:
return [1 - x, 1 - y, width, height];
case 270:
return [y, 1 - x, width * (pW / pH), height * (pH / pW)];
default:
return [x, y, width, height];
}
}
#updateBbox(bbox) {
[this.x, this.y, this.width, this.height] = this.#convertToParentSpace(bbox);
if (this.div) {
this.fixAndSetPosition();
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(this.width * parentWidth, this.height * parentHeight);
}
this._onResized();
}
#rotateBox() {
const {
x,
y,
width,
height,
rotation,
parentRotation,
parentDimensions: [pW, pH]
} = this;
switch ((rotation * 4 + parentRotation) / 90) {
case 1:
return [1 - y - height, x, height, width];
case 2:
return [1 - x - width, 1 - y - height, width, height];
case 3:
return [y, 1 - x - width, height, width];
case 4:
return [x, y - width * (pW / pH), height * (pH / pW), width * (pW / pH)];
case 5:
return [1 - y, x, width * (pW / pH), height * (pH / pW)];
case 6:
return [1 - x - height * (pH / pW), 1 - y, height * (pH / pW), width * (pW / pH)];
case 7:
return [y - width * (pW / pH), 1 - x - height * (pH / pW), width * (pW / pH), height * (pH / pW)];
case 8:
return [x - width, y - height, width, height];
case 9:
return [1 - y, x - width, height, width];
case 10:
return [1 - x, 1 - y, width, height];
case 11:
return [y - height, 1 - x, height, width];
case 12:
return [x - height * (pH / pW), y, height * (pH / pW), width * (pW / pH)];
case 13:
return [1 - y - width * (pW / pH), x - height * (pH / pW), width * (pW / pH), height * (pH / pW)];
case 14:
return [1 - x, 1 - y - width * (pW / pH), height * (pH / pW), width * (pW / pH)];
case 15:
return [y, 1 - x, width * (pW / pH), height * (pH / pW)];
default:
return [x, y, width, height];
}
}
rotate() {
if (!this.parent) {
return;
}
this.parent.drawLayer.updateProperties(this._drawId, DrawingEditor._mergeSVGProperties({
bbox: this.#rotateBox()
}, this.#drawOutlines.updateRotation((this.parentRotation - this.rotation + 360) % 360)));
}
onScaleChanging() {
if (!this.parent) {
return;
}
this.#updateBbox(this.#drawOutlines.updateParentDimensions(this.parentDimensions, this.parent.scale));
}
static onScaleChangingWhenDrawing() {}
render() {
if (this.div) {
return this.div;
}
const div = super.render();
div.classList.add("draw");
const drawDiv = document.createElement("div");
div.append(drawDiv);
drawDiv.setAttribute("aria-hidden", "true");
drawDiv.className = "internal";
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(this.width * parentWidth, this.height * parentHeight);
this._uiManager.addShouldRescale(this);
this.disableEditing();
return div;
}
static createDrawerInstance(_x, _y, _parentWidth, _parentHeight, _rotation) {
(0,util.unreachable)("Not implemented");
}
static startDrawing(parent, uiManager, _isLTR, event) {
const {
target,
offsetX: x,
offsetY: y,
pointerId,
pointerType
} = event;
if (DrawingEditor.#currentPointerType && DrawingEditor.#currentPointerType !== pointerType) {
return;
}
const {
viewport: {
rotation
}
} = parent;
const {
width: parentWidth,
height: parentHeight
} = target.getBoundingClientRect();
const ac = DrawingEditor.#currentDrawingAC = new AbortController();
const signal = parent.combinedSignal(ac);
DrawingEditor.#currentPointerId ||= pointerId;
DrawingEditor.#currentPointerType ??= pointerType;
window.addEventListener("pointerup", e => {
if (DrawingEditor.#currentPointerId === e.pointerId) {
this._endDraw(e);
} else {
DrawingEditor.#currentPointerIds?.delete(e.pointerId);
}
}, {
signal
});
window.addEventListener("pointercancel", e => {
if (DrawingEditor.#currentPointerId === e.pointerId) {
this._currentParent.endDrawingSession();
} else {
DrawingEditor.#currentPointerIds?.delete(e.pointerId);
}
}, {
signal
});
window.addEventListener("pointerdown", e => {
if (DrawingEditor.#currentPointerType !== e.pointerType) {
return;
}
(DrawingEditor.#currentPointerIds ||= new Set()).add(e.pointerId);
if (DrawingEditor.#currentDraw.isCancellable()) {
DrawingEditor.#currentDraw.removeLastElement();
if (DrawingEditor.#currentDraw.isEmpty()) {
this._currentParent.endDrawingSession(true);
} else {
this._endDraw(null);
}
}
}, {
capture: true,
passive: false,
signal
});
window.addEventListener("contextmenu", display_utils.noContextMenu, {
signal
});
target.addEventListener("pointermove", this._drawMove.bind(this), {
signal
});
target.addEventListener("touchmove", e => {
if (e.timeStamp === DrawingEditor.#currentMoveTimestamp) {
(0,display_utils.stopEvent)(e);
}
}, {
signal
});
parent.toggleDrawing();
uiManager._editorUndoBar?.hide();
if (DrawingEditor.#currentDraw) {
parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.startNew(x, y, parentWidth, parentHeight, rotation));
return;
}
uiManager.updateUIForDefaultProperties(this);
DrawingEditor.#currentDraw = this.createDrawerInstance(x, y, parentWidth, parentHeight, rotation);
DrawingEditor.#currentDrawingOptions = this.getDefaultDrawingOptions();
this._currentParent = parent;
({
id: this._currentDrawId
} = parent.drawLayer.draw(this._mergeSVGProperties(DrawingEditor.#currentDrawingOptions.toSVGProperties(), DrawingEditor.#currentDraw.defaultSVGProperties), true, false));
}
static _drawMove(event) {
DrawingEditor.#currentMoveTimestamp = -1;
if (!DrawingEditor.#currentDraw) {
return;
}
const {
offsetX,
offsetY,
pointerId
} = event;
if (DrawingEditor.#currentPointerId !== pointerId) {
return;
}
if (DrawingEditor.#currentPointerIds?.size >= 1) {
this._endDraw(event);
return;
}
this._currentParent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.add(offsetX, offsetY));
DrawingEditor.#currentMoveTimestamp = event.timeStamp;
(0,display_utils.stopEvent)(event);
}
static _cleanup(all) {
if (all) {
this._currentDrawId = -1;
this._currentParent = null;
DrawingEditor.#currentDraw = null;
DrawingEditor.#currentDrawingOptions = null;
DrawingEditor.#currentPointerType = null;
DrawingEditor.#currentMoveTimestamp = NaN;
}
if (DrawingEditor.#currentDrawingAC) {
DrawingEditor.#currentDrawingAC.abort();
DrawingEditor.#currentDrawingAC = null;
DrawingEditor.#currentPointerId = NaN;
DrawingEditor.#currentPointerIds = null;
}
}
static _endDraw(event) {
const parent = this._currentParent;
if (!parent) {
return;
}
parent.toggleDrawing(true);
this._cleanup(false);
if (event) {
parent.drawLayer.updateProperties(this._currentDrawId, DrawingEditor.#currentDraw.end(event.offsetX, event.offsetY));
}
if (this.supportMultipleDrawings) {
const draw = DrawingEditor.#currentDraw;
const drawId = this._currentDrawId;
const lastElement = draw.getLastElement();
parent.addCommands({
cmd: () => {
parent.drawLayer.updateProperties(drawId, draw.setLastElement(lastElement));
},
undo: () => {
parent.drawLayer.updateProperties(drawId, draw.removeLastElement());
},
mustExec: false,
type: util.AnnotationEditorParamsType.DRAW_STEP
});
return;
}
this.endDrawing(false);
}
static endDrawing(isAborted) {
const parent = this._currentParent;
if (!parent) {
return null;
}
parent.toggleDrawing(true);
parent.cleanUndoStack(util.AnnotationEditorParamsType.DRAW_STEP);
if (!DrawingEditor.#currentDraw.isEmpty()) {
const {
pageDimensions: [pageWidth, pageHeight],
scale
} = parent;
const editor = parent.createAndAddNewEditor({
offsetX: 0,
offsetY: 0
}, false, {
drawId: this._currentDrawId,
drawOutlines: DrawingEditor.#currentDraw.getOutlines(pageWidth * scale, pageHeight * scale, scale, this._INNER_MARGIN),
drawingOptions: DrawingEditor.#currentDrawingOptions,
mustBeCommitted: !isAborted
});
this._cleanup(true);
return editor;
}
parent.drawLayer.remove(this._currentDrawId);
this._cleanup(true);
return null;
}
createDrawingOptions(_data) {}
static deserializeDraw(_pageX, _pageY, _pageWidth, _pageHeight, _innerWidth, _data) {
(0,util.unreachable)("Not implemented");
}
static async deserialize(data, parent, uiManager) {
const {
rawDims: {
pageWidth,
pageHeight,
pageX,
pageY
}
} = parent.viewport;
const drawOutlines = this.deserializeDraw(pageX, pageY, pageWidth, pageHeight, this._INNER_MARGIN, data);
const editor = await super.deserialize(data, parent, uiManager);
editor.createDrawingOptions(data);
editor.#createDrawOutlines({
drawOutlines
});
editor.#addToDrawLayer();
editor.onScaleChanging();
editor.rotate();
return editor;
}
serializeDraw(isForCopying) {
const [pageX, pageY] = this.pageTranslation;
const [pageWidth, pageHeight] = this.pageDimensions;
return this.#drawOutlines.serialize([pageX, pageY, pageWidth, pageHeight], isForCopying);
}
renderAnnotationElement(annotation) {
annotation.updateEdited({
rect: this.getRect(0, 0)
});
return null;
}
static canCreateNewEmptyEditor() {
return false;
}
}
;// ./src/display/editor/drawers/inkdraw.js
class InkDrawOutliner {
#last = new Float64Array(6);
#line;
#lines;
#rotation;
#thickness;
#points;
#lastSVGPath = "";
#lastIndex = 0;
#outlines = new InkDrawOutline();
#parentWidth;
#parentHeight;
constructor(x, y, parentWidth, parentHeight, rotation, thickness) {
this.#parentWidth = parentWidth;
this.#parentHeight = parentHeight;
this.#rotation = rotation;
this.#thickness = thickness;
[x, y] = this.#normalizePoint(x, y);
const line = this.#line = [NaN, NaN, NaN, NaN, x, y];
this.#points = [x, y];
this.#lines = [{
line,
points: this.#points
}];
this.#last.set(line, 0);
}
updateProperty(name, value) {
if (name === "stroke-width") {
this.#thickness = value;
}
}
#normalizePoint(x, y) {
return Outline._normalizePoint(x, y, this.#parentWidth, this.#parentHeight, this.#rotation);
}
isEmpty() {
return !this.#lines || this.#lines.length === 0;
}
isCancellable() {
return this.#points.length <= 10;
}
add(x, y) {
[x, y] = this.#normalizePoint(x, y);
const [x1, y1, x2, y2] = this.#last.subarray(2, 6);
const diffX = x - x2;
const diffY = y - y2;
const d = Math.hypot(this.#parentWidth * diffX, this.#parentHeight * diffY);
if (d <= 2) {
return null;
}
this.#points.push(x, y);
if (isNaN(x1)) {
this.#last.set([x2, y2, x, y], 2);
this.#line.push(NaN, NaN, NaN, NaN, x, y);
return {
path: {
d: this.toSVGPath()
}
};
}
if (isNaN(this.#last[0])) {
this.#line.splice(6, 6);
}
this.#last.set([x1, y1, x2, y2, x, y], 0);
this.#line.push(...Outline.createBezierPoints(x1, y1, x2, y2, x, y));
return {
path: {
d: this.toSVGPath()
}
};
}
end(x, y) {
const change = this.add(x, y);
if (change) {
return change;
}
if (this.#points.length === 2) {
return {
path: {
d: this.toSVGPath()
}
};
}
return null;
}
startNew(x, y, parentWidth, parentHeight, rotation) {
this.#parentWidth = parentWidth;
this.#parentHeight = parentHeight;
this.#rotation = rotation;
[x, y] = this.#normalizePoint(x, y);
const line = this.#line = [NaN, NaN, NaN, NaN, x, y];
this.#points = [x, y];
const last = this.#lines.at(-1);
if (last) {
last.line = new Float32Array(last.line);
last.points = new Float32Array(last.points);
}
this.#lines.push({
line,
points: this.#points
});
this.#last.set(line, 0);
this.#lastIndex = 0;
this.toSVGPath();
return null;
}
getLastElement() {
return this.#lines.at(-1);
}
setLastElement(element) {
if (!this.#lines) {
return this.#outlines.setLastElement(element);
}
this.#lines.push(element);
this.#line = element.line;
this.#points = element.points;
this.#lastIndex = 0;
return {
path: {
d: this.toSVGPath()
}
};
}
removeLastElement() {
if (!this.#lines) {
return this.#outlines.removeLastElement();
}
this.#lines.pop();
this.#lastSVGPath = "";
for (let i = 0, ii = this.#lines.length; i < ii; i++) {
const {
line,
points
} = this.#lines[i];
this.#line = line;
this.#points = points;
this.#lastIndex = 0;
this.toSVGPath();
}
return {
path: {
d: this.#lastSVGPath
}
};
}
toSVGPath() {
const firstX = Outline.svgRound(this.#line[4]);
const firstY = Outline.svgRound(this.#line[5]);
if (this.#points.length === 2) {
this.#lastSVGPath = `${this.#lastSVGPath} M ${firstX} ${firstY} Z`;
return this.#lastSVGPath;
}
if (this.#points.length <= 6) {
const i = this.#lastSVGPath.lastIndexOf("M");
this.#lastSVGPath = `${this.#lastSVGPath.slice(0, i)} M ${firstX} ${firstY}`;
this.#lastIndex = 6;
}
if (this.#points.length === 4) {
const secondX = Outline.svgRound(this.#line[10]);
const secondY = Outline.svgRound(this.#line[11]);
this.#lastSVGPath = `${this.#lastSVGPath} L ${secondX} ${secondY}`;
this.#lastIndex = 12;
return this.#lastSVGPath;
}
const buffer = [];
if (this.#lastIndex === 0) {
buffer.push(`M ${firstX} ${firstY}`);
this.#lastIndex = 6;
}
for (let i = this.#lastIndex, ii = this.#line.length; i < ii; i += 6) {
const [c1x, c1y, c2x, c2y, x, y] = this.#line.slice(i, i + 6).map(Outline.svgRound);
buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`);
}
this.#lastSVGPath += buffer.join(" ");
this.#lastIndex = this.#line.length;
return this.#lastSVGPath;
}
getOutlines(parentWidth, parentHeight, scale, innerMargin) {
const last = this.#lines.at(-1);
last.line = new Float32Array(last.line);
last.points = new Float32Array(last.points);
this.#outlines.build(this.#lines, parentWidth, parentHeight, scale, this.#rotation, this.#thickness, innerMargin);
this.#last = null;
this.#line = null;
this.#lines = null;
this.#lastSVGPath = null;
return this.#outlines;
}
get defaultSVGProperties() {
return {
root: {
viewBox: "0 0 10000 10000"
},
rootClass: {
draw: true
},
bbox: [0, 0, 1, 1]
};
}
}
class InkDrawOutline extends Outline {
#bbox;
#currentRotation = 0;
#innerMargin;
#lines;
#parentWidth;
#parentHeight;
#parentScale;
#rotation;
#thickness;
build(lines, parentWidth, parentHeight, parentScale, rotation, thickness, innerMargin) {
this.#parentWidth = parentWidth;
this.#parentHeight = parentHeight;
this.#parentScale = parentScale;
this.#rotation = rotation;
this.#thickness = thickness;
this.#innerMargin = innerMargin ?? 0;
this.#lines = lines;
this.#computeBbox();
}
setLastElement(element) {
this.#lines.push(element);
return {
path: {
d: this.toSVGPath()
}
};
}
removeLastElement() {
this.#lines.pop();
return {
path: {
d: this.toSVGPath()
}
};
}
toSVGPath() {
const buffer = [];
for (const {
line
} of this.#lines) {
buffer.push(`M${Outline.svgRound(line[4])} ${Outline.svgRound(line[5])}`);
if (line.length === 6) {
buffer.push("Z");
continue;
}
if (line.length === 12) {
buffer.push(`L${Outline.svgRound(line[10])} ${Outline.svgRound(line[11])}`);
continue;
}
for (let i = 6, ii = line.length; i < ii; i += 6) {
const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6).map(Outline.svgRound);
buffer.push(`C${c1x} ${c1y} ${c2x} ${c2y} ${x} ${y}`);
}
}
return buffer.join("");
}
serialize([pageX, pageY, pageWidth, pageHeight], isForCopying) {
const serializedLines = [];
const serializedPoints = [];
const [x, y, width, height] = this.#getBBoxWithNoMargin();
let tx, ty, sx, sy, x1, y1, x2, y2, rescaleFn;
switch (this.#rotation) {
case 0:
rescaleFn = Outline._rescale;
tx = pageX;
ty = pageY + pageHeight;
sx = pageWidth;
sy = -pageHeight;
x1 = pageX + x * pageWidth;
y1 = pageY + (1 - y - height) * pageHeight;
x2 = pageX + (x + width) * pageWidth;
y2 = pageY + (1 - y) * pageHeight;
break;
case 90:
rescaleFn = Outline._rescaleAndSwap;
tx = pageX;
ty = pageY;
sx = pageWidth;
sy = pageHeight;
x1 = pageX + y * pageWidth;
y1 = pageY + x * pageHeight;
x2 = pageX + (y + height) * pageWidth;
y2 = pageY + (x + width) * pageHeight;
break;
case 180:
rescaleFn = Outline._rescale;
tx = pageX + pageWidth;
ty = pageY;
sx = -pageWidth;
sy = pageHeight;
x1 = pageX + (1 - x - width) * pageWidth;
y1 = pageY + y * pageHeight;
x2 = pageX + (1 - x) * pageWidth;
y2 = pageY + (y + height) * pageHeight;
break;
case 270:
rescaleFn = Outline._rescaleAndSwap;
tx = pageX + pageWidth;
ty = pageY + pageHeight;
sx = -pageWidth;
sy = -pageHeight;
x1 = pageX + (1 - y - height) * pageWidth;
y1 = pageY + (1 - x - width) * pageHeight;
x2 = pageX + (1 - y) * pageWidth;
y2 = pageY + (1 - x) * pageHeight;
break;
}
for (const {
line,
points
} of this.#lines) {
serializedLines.push(rescaleFn(line, tx, ty, sx, sy, isForCopying ? new Array(line.length) : null));
serializedPoints.push(rescaleFn(points, tx, ty, sx, sy, isForCopying ? new Array(points.length) : null));
}
return {
lines: serializedLines,
points: serializedPoints,
rect: [x1, y1, x2, y2]
};
}
static deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, {
paths: {
lines,
points
},
rotation,
thickness
}) {
const newLines = [];
let tx, ty, sx, sy, rescaleFn;
switch (rotation) {
case 0:
rescaleFn = Outline._rescale;
tx = -pageX / pageWidth;
ty = pageY / pageHeight + 1;
sx = 1 / pageWidth;
sy = -1 / pageHeight;
break;
case 90:
rescaleFn = Outline._rescaleAndSwap;
tx = -pageY / pageHeight;
ty = -pageX / pageWidth;
sx = 1 / pageHeight;
sy = 1 / pageWidth;
break;
case 180:
rescaleFn = Outline._rescale;
tx = pageX / pageWidth + 1;
ty = -pageY / pageHeight;
sx = -1 / pageWidth;
sy = 1 / pageHeight;
break;
case 270:
rescaleFn = Outline._rescaleAndSwap;
tx = pageY / pageHeight + 1;
ty = pageX / pageWidth + 1;
sx = -1 / pageHeight;
sy = -1 / pageWidth;
break;
}
if (!lines) {
lines = [];
for (const point of points) {
const len = point.length;
if (len === 2) {
lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1]]));
continue;
}
if (len === 4) {
lines.push(new Float32Array([NaN, NaN, NaN, NaN, point[0], point[1], NaN, NaN, NaN, NaN, point[2], point[3]]));
continue;
}
const line = new Float32Array(3 * (len - 2));
lines.push(line);
let [x1, y1, x2, y2] = point.subarray(0, 4);
line.set([NaN, NaN, NaN, NaN, x1, y1], 0);
for (let i = 4; i < len; i += 2) {
const x = point[i];
const y = point[i + 1];
line.set(Outline.createBezierPoints(x1, y1, x2, y2, x, y), (i - 2) * 3);
[x1, y1, x2, y2] = [x2, y2, x, y];
}
}
}
for (let i = 0, ii = lines.length; i < ii; i++) {
newLines.push({
line: rescaleFn(lines[i].map(x => x ?? NaN), tx, ty, sx, sy),
points: rescaleFn(points[i].map(x => x ?? NaN), tx, ty, sx, sy)
});
}
const outlines = new InkDrawOutline();
outlines.build(newLines, pageWidth, pageHeight, 1, rotation, thickness, innerMargin);
return outlines;
}
#getMarginComponents(thickness = this.#thickness) {
const margin = this.#innerMargin + thickness / 2 * this.#parentScale;
return this.#rotation % 180 === 0 ? [margin / this.#parentWidth, margin / this.#parentHeight] : [margin / this.#parentHeight, margin / this.#parentWidth];
}
#getBBoxWithNoMargin() {
const [x, y, width, height] = this.#bbox;
const [marginX, marginY] = this.#getMarginComponents(0);
return [x + marginX, y + marginY, width - 2 * marginX, height - 2 * marginY];
}
#computeBbox() {
const bbox = this.#bbox = new Float32Array([Infinity, Infinity, -Infinity, -Infinity]);
for (const {
line
} of this.#lines) {
if (line.length <= 12) {
for (let i = 4, ii = line.length; i < ii; i += 6) {
const [x, y] = line.subarray(i, i + 2);
bbox[0] = Math.min(bbox[0], x);
bbox[1] = Math.min(bbox[1], y);
bbox[2] = Math.max(bbox[2], x);
bbox[3] = Math.max(bbox[3], y);
}
continue;
}
let lastX = line[4],
lastY = line[5];
for (let i = 6, ii = line.length; i < ii; i += 6) {
const [c1x, c1y, c2x, c2y, x, y] = line.subarray(i, i + 6);
util.Util.bezierBoundingBox(lastX, lastY, c1x, c1y, c2x, c2y, x, y, bbox);
lastX = x;
lastY = y;
}
}
const [marginX, marginY] = this.#getMarginComponents();
bbox[0] = Math.min(1, Math.max(0, bbox[0] - marginX));
bbox[1] = Math.min(1, Math.max(0, bbox[1] - marginY));
bbox[2] = Math.min(1, Math.max(0, bbox[2] + marginX));
bbox[3] = Math.min(1, Math.max(0, bbox[3] + marginY));
bbox[2] -= bbox[0];
bbox[3] -= bbox[1];
}
get box() {
return this.#bbox;
}
updateProperty(name, value) {
if (name === "stroke-width") {
return this.#updateThickness(value);
}
return null;
}
#updateThickness(thickness) {
const [oldMarginX, oldMarginY] = this.#getMarginComponents();
this.#thickness = thickness;
const [newMarginX, newMarginY] = this.#getMarginComponents();
const [diffMarginX, diffMarginY] = [newMarginX - oldMarginX, newMarginY - oldMarginY];
const bbox = this.#bbox;
bbox[0] -= diffMarginX;
bbox[1] -= diffMarginY;
bbox[2] += 2 * diffMarginX;
bbox[3] += 2 * diffMarginY;
return bbox;
}
updateParentDimensions([width, height], scale) {
const [oldMarginX, oldMarginY] = this.#getMarginComponents();
this.#parentWidth = width;
this.#parentHeight = height;
this.#parentScale = scale;
const [newMarginX, newMarginY] = this.#getMarginComponents();
const diffMarginX = newMarginX - oldMarginX;
const diffMarginY = newMarginY - oldMarginY;
const bbox = this.#bbox;
bbox[0] -= diffMarginX;
bbox[1] -= diffMarginY;
bbox[2] += 2 * diffMarginX;
bbox[3] += 2 * diffMarginY;
return bbox;
}
updateRotation(rotation) {
this.#currentRotation = rotation;
return {
path: {
transform: this.rotationTransform
}
};
}
get viewBox() {
return this.#bbox.map(Outline.svgRound).join(" ");
}
get defaultProperties() {
const [x, y] = this.#bbox;
return {
root: {
viewBox: this.viewBox
},
path: {
"transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`
}
};
}
get rotationTransform() {
const [,, width, height] = this.#bbox;
let a = 0,
b = 0,
c = 0,
d = 0,
e = 0,
f = 0;
switch (this.#currentRotation) {
case 90:
b = height / width;
c = -width / height;
e = width;
break;
case 180:
a = -1;
d = -1;
e = width;
f = height;
break;
case 270:
b = -height / width;
c = width / height;
f = height;
break;
default:
return "";
}
return `matrix(${a} ${b} ${c} ${d} ${Outline.svgRound(e)} ${Outline.svgRound(f)})`;
}
getPathResizingSVGProperties([newX, newY, newWidth, newHeight]) {
const [marginX, marginY] = this.#getMarginComponents();
const [x, y, width, height] = this.#bbox;
if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) {
const tx = newX + newWidth / 2 - (x + width / 2);
const ty = newY + newHeight / 2 - (y + height / 2);
return {
path: {
"transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`,
transform: `${this.rotationTransform} translate(${tx} ${ty})`
}
};
}
const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX);
const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY);
const s2x = width / newWidth;
const s2y = height / newHeight;
return {
path: {
"transform-origin": `${Outline.svgRound(x)} ${Outline.svgRound(y)}`,
transform: `${this.rotationTransform} scale(${s2x} ${s2y}) ` + `translate(${Outline.svgRound(marginX)} ${Outline.svgRound(marginY)}) scale(${s1x} ${s1y}) ` + `translate(${Outline.svgRound(-marginX)} ${Outline.svgRound(-marginY)})`
}
};
}
getPathResizedSVGProperties([newX, newY, newWidth, newHeight]) {
const [marginX, marginY] = this.#getMarginComponents();
const bbox = this.#bbox;
const [x, y, width, height] = bbox;
bbox[0] = newX;
bbox[1] = newY;
bbox[2] = newWidth;
bbox[3] = newHeight;
if (Math.abs(width - marginX) <= Outline.PRECISION || Math.abs(height - marginY) <= Outline.PRECISION) {
const tx = newX + newWidth / 2 - (x + width / 2);
const ty = newY + newHeight / 2 - (y + height / 2);
for (const {
line,
points
} of this.#lines) {
Outline._translate(line, tx, ty, line);
Outline._translate(points, tx, ty, points);
}
return {
root: {
viewBox: this.viewBox
},
path: {
"transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`,
transform: this.rotationTransform || null,
d: this.toSVGPath()
}
};
}
const s1x = (newWidth - 2 * marginX) / (width - 2 * marginX);
const s1y = (newHeight - 2 * marginY) / (height - 2 * marginY);
const tx = -s1x * (x + marginX) + newX + marginX;
const ty = -s1y * (y + marginY) + newY + marginY;
if (s1x !== 1 || s1y !== 1 || tx !== 0 || ty !== 0) {
for (const {
line,
points
} of this.#lines) {
Outline._rescale(line, tx, ty, s1x, s1y, line);
Outline._rescale(points, tx, ty, s1x, s1y, points);
}
}
return {
root: {
viewBox: this.viewBox
},
path: {
"transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`,
transform: this.rotationTransform || null,
d: this.toSVGPath()
}
};
}
getPathTranslatedSVGProperties([newX, newY], parentDimensions) {
const [newParentWidth, newParentHeight] = parentDimensions;
const bbox = this.#bbox;
const tx = newX - bbox[0];
const ty = newY - bbox[1];
if (this.#parentWidth === newParentWidth && this.#parentHeight === newParentHeight) {
for (const {
line,
points
} of this.#lines) {
Outline._translate(line, tx, ty, line);
Outline._translate(points, tx, ty, points);
}
} else {
const sx = this.#parentWidth / newParentWidth;
const sy = this.#parentHeight / newParentHeight;
this.#parentWidth = newParentWidth;
this.#parentHeight = newParentHeight;
for (const {
line,
points
} of this.#lines) {
Outline._rescale(line, tx, ty, sx, sy, line);
Outline._rescale(points, tx, ty, sx, sy, points);
}
bbox[2] *= sx;
bbox[3] *= sy;
}
bbox[0] = newX;
bbox[1] = newY;
return {
root: {
viewBox: this.viewBox
},
path: {
d: this.toSVGPath(),
"transform-origin": `${Outline.svgRound(newX)} ${Outline.svgRound(newY)}`
}
};
}
get defaultSVGProperties() {
const bbox = this.#bbox;
return {
root: {
viewBox: this.viewBox
},
rootClass: {
draw: true
},
path: {
d: this.toSVGPath(),
"transform-origin": `${Outline.svgRound(bbox[0])} ${Outline.svgRound(bbox[1])}`,
transform: this.rotationTransform || null
},
bbox
};
}
}
;// ./src/display/editor/ink.js
class InkDrawingOptions extends DrawingOptions {
#viewParameters;
constructor(viewerParameters) {
super();
this.#viewParameters = viewerParameters;
super.updateProperties({
fill: "none",
stroke: editor_editor.AnnotationEditor._defaultLineColor,
"stroke-opacity": 1,
"stroke-width": 1,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"stroke-miterlimit": 10
});
}
updateSVGProperty(name, value) {
if (name === "stroke-width") {
value ??= this["stroke-width"];
value *= this.#viewParameters.realScale;
}
super.updateSVGProperty(name, value);
}
clone() {
const clone = new InkDrawingOptions(this.#viewParameters);
clone.updateAll(this);
return clone;
}
}
class InkEditor extends DrawingEditor {
static _type = "ink";
static _editorType = util.AnnotationEditorType.INK;
static _defaultDrawingOptions = null;
constructor(params) {
super({
...params,
name: "inkEditor"
});
this._willKeepAspectRatio = true;
}
static initialize(l10n, uiManager) {
editor_editor.AnnotationEditor.initialize(l10n, uiManager);
this._defaultDrawingOptions = new InkDrawingOptions(uiManager.viewParameters);
}
static getDefaultDrawingOptions(options) {
const clone = this._defaultDrawingOptions.clone();
clone.updateProperties(options);
return clone;
}
static get supportMultipleDrawings() {
return true;
}
static get typesMap() {
return (0,util.shadow)(this, "typesMap", new Map([[util.AnnotationEditorParamsType.INK_THICKNESS, "stroke-width"], [util.AnnotationEditorParamsType.INK_COLOR, "stroke"], [util.AnnotationEditorParamsType.INK_OPACITY, "stroke-opacity"]]));
}
static createDrawerInstance(x, y, parentWidth, parentHeight, rotation) {
return new InkDrawOutliner(x, y, parentWidth, parentHeight, rotation, this._defaultDrawingOptions["stroke-width"]);
}
static deserializeDraw(pageX, pageY, pageWidth, pageHeight, innerMargin, data) {
return InkDrawOutline.deserialize(pageX, pageY, pageWidth, pageHeight, innerMargin, data);
}
static async deserialize(data, parent, uiManager) {
let initialData = null;
if (data instanceof InkAnnotationElement) {
const {
data: {
inkLists,
rect,
rotation,
id,
color,
opacity,
borderStyle: {
rawWidth: thickness
},
popupRef
},
parent: {
page: {
pageNumber
}
}
} = data;
initialData = data = {
annotationType: util.AnnotationEditorType.INK,
color: Array.from(color),
thickness,
opacity,
paths: {
points: inkLists
},
boxes: null,
pageIndex: pageNumber - 1,
rect: rect.slice(0),
rotation,
id,
deleted: false,
popupRef
};
}
const editor = await super.deserialize(data, parent, uiManager);
editor.annotationElementId = data.id || null;
editor._initialData = initialData;
return editor;
}
onScaleChanging() {
if (!this.parent) {
return;
}
super.onScaleChanging();
const {
_drawId,
_drawingOptions,
parent
} = this;
_drawingOptions.updateSVGProperty("stroke-width");
parent.drawLayer.updateProperties(_drawId, _drawingOptions.toSVGProperties());
}
static onScaleChangingWhenDrawing() {
const parent = this._currentParent;
if (!parent) {
return;
}
super.onScaleChangingWhenDrawing();
this._defaultDrawingOptions.updateSVGProperty("stroke-width");
parent.drawLayer.updateProperties(this._currentDrawId, this._defaultDrawingOptions.toSVGProperties());
}
createDrawingOptions({
color,
thickness,
opacity
}) {
this._drawingOptions = InkEditor.getDefaultDrawingOptions({
stroke: util.Util.makeHexColor(...color),
"stroke-width": thickness,
"stroke-opacity": opacity
});
}
serialize(isForCopying = false) {
if (this.isEmpty()) {
return null;
}
if (this.deleted) {
return this.serializeDeleted();
}
const {
lines,
points,
rect
} = this.serializeDraw(isForCopying);
const {
_drawingOptions: {
stroke,
"stroke-opacity": opacity,
"stroke-width": thickness
}
} = this;
const serialized = {
annotationType: util.AnnotationEditorType.INK,
color: editor_editor.AnnotationEditor._colorManager.convert(stroke),
opacity,
thickness,
paths: {
lines,
points
},
pageIndex: this.pageIndex,
rect,
rotation: this.rotation,
structTreeParentId: this._structTreeParentId
};
if (isForCopying) {
return serialized;
}
if (this.annotationElementId && !this.#hasElementChanged(serialized)) {
return null;
}
serialized.id = this.annotationElementId;
return serialized;
}
#hasElementChanged(serialized) {
const {
color,
thickness,
opacity,
pageIndex
} = this._initialData;
return this._hasBeenMoved || this._hasBeenResized || serialized.color.some((c, i) => c !== color[i]) || serialized.thickness !== thickness || serialized.opacity !== opacity || serialized.pageIndex !== pageIndex;
}
renderAnnotationElement(annotation) {
const {
points,
rect
} = this.serializeDraw(false);
annotation.updateEdited({
rect,
thickness: this._drawingOptions["stroke-width"],
points
});
return null;
}
}
;// ./src/display/editor/stamp.js
class StampEditor extends editor_editor.AnnotationEditor {
#bitmap = null;
#bitmapId = null;
#bitmapPromise = null;
#bitmapUrl = null;
#bitmapFile = null;
#bitmapFileName = "";
#canvas = null;
#resizeTimeoutId = null;
#isSvg = false;
#hasBeenAddedInUndoStack = false;
static _type = "stamp";
static _editorType = util.AnnotationEditorType.STAMP;
constructor(params) {
super({
...params,
name: "stampEditor"
});
this.#bitmapUrl = params.bitmapUrl;
this.#bitmapFile = params.bitmapFile;
}
static initialize(l10n, uiManager) {
editor_editor.AnnotationEditor.initialize(l10n, uiManager);
}
static get supportedTypes() {
const types = ["apng", "avif", "bmp", "gif", "jpeg", "png", "svg+xml", "webp", "x-icon"];
return (0,util.shadow)(this, "supportedTypes", types.map(type => `image/${type}`));
}
static get supportedTypesStr() {
return (0,util.shadow)(this, "supportedTypesStr", this.supportedTypes.join(","));
}
static isHandlingMimeForPasting(mime) {
return this.supportedTypes.includes(mime);
}
static paste(item, parent) {
parent.pasteEditor(util.AnnotationEditorType.STAMP, {
bitmapFile: item.getAsFile()
});
}
altTextFinish() {
if (this._uiManager.useNewAltTextFlow) {
this.div.hidden = false;
}
super.altTextFinish();
}
get telemetryFinalData() {
return {
type: "stamp",
hasAltText: !!this.altTextData?.altText
};
}
static computeTelemetryFinalData(data) {
const hasAltTextStats = data.get("hasAltText");
return {
hasAltText: hasAltTextStats.get(true) ?? 0,
hasNoAltText: hasAltTextStats.get(false) ?? 0
};
}
#getBitmapFetched(data, fromId = false) {
if (!data) {
this.remove();
return;
}
this.#bitmap = data.bitmap;
if (!fromId) {
this.#bitmapId = data.id;
this.#isSvg = data.isSvg;
}
if (data.file) {
this.#bitmapFileName = data.file.name;
}
this.#createCanvas();
}
#getBitmapDone() {
this.#bitmapPromise = null;
this._uiManager.enableWaiting(false);
if (!this.#canvas) {
return;
}
if (this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) {
this._editToolbar.hide();
this._uiManager.editAltText(this, true);
return;
}
if (!this._uiManager.useNewAltTextWhenAddingImage && this._uiManager.useNewAltTextFlow && this.#bitmap) {
this._reportTelemetry({
action: "pdfjs.image.image_added",
data: {
alt_text_modal: false,
alt_text_type: "empty"
}
});
try {
this.mlGuessAltText();
} catch {}
}
this.div.focus();
}
async mlGuessAltText(imageData = null, updateAltTextData = true) {
if (this.hasAltTextData()) {
return null;
}
const {
mlManager
} = this._uiManager;
if (!mlManager) {
throw new Error("No ML.");
}
if (!(await mlManager.isEnabledFor("altText"))) {
throw new Error("ML isn't enabled for alt text.");
}
const {
data,
width,
height
} = imageData || this.copyCanvas(null, null, true).imageData;
const response = await mlManager.guess({
name: "altText",
request: {
data,
width,
height,
channels: data.length / (width * height)
}
});
if (!response) {
throw new Error("No response from the AI service.");
}
if (response.error) {
throw new Error("Error from the AI service.");
}
if (response.cancel) {
return null;
}
if (!response.output) {
throw new Error("No valid response from the AI service.");
}
const altText = response.output;
await this.setGuessedAltText(altText);
if (updateAltTextData && !this.hasAltTextData()) {
this.altTextData = {
alt: altText,
decorative: false
};
}
return altText;
}
#getBitmap() {
if (this.#bitmapId) {
this._uiManager.enableWaiting(true);
this._uiManager.imageManager.getFromId(this.#bitmapId).then(data => this.#getBitmapFetched(data, true)).finally(() => this.#getBitmapDone());
return;
}
if (this.#bitmapUrl) {
const url = this.#bitmapUrl;
this.#bitmapUrl = null;
this._uiManager.enableWaiting(true);
this.#bitmapPromise = this._uiManager.imageManager.getFromUrl(url).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());
return;
}
if (this.#bitmapFile) {
const file = this.#bitmapFile;
this.#bitmapFile = null;
this._uiManager.enableWaiting(true);
this.#bitmapPromise = this._uiManager.imageManager.getFromFile(file).then(data => this.#getBitmapFetched(data)).finally(() => this.#getBitmapDone());
return;
}
const input = document.createElement("input");
input.type = "file";
input.accept = StampEditor.supportedTypesStr;
const signal = this._uiManager._signal;
this.#bitmapPromise = new Promise(resolve => {
input.addEventListener("change", async () => {
if (!input.files || input.files.length === 0) {
this.remove();
} else {
this._uiManager.enableWaiting(true);
const data = await this._uiManager.imageManager.getFromFile(input.files[0]);
this._reportTelemetry({
action: "pdfjs.image.image_selected",
data: {
alt_text_modal: this._uiManager.useNewAltTextFlow
}
});
this.#getBitmapFetched(data);
}
resolve();
}, {
signal
});
input.addEventListener("cancel", () => {
this.remove();
resolve();
}, {
signal
});
}).finally(() => this.#getBitmapDone());
input.click();
}
remove() {
if (this.#bitmapId) {
this.#bitmap = null;
this._uiManager.imageManager.deleteId(this.#bitmapId);
this.#canvas?.remove();
this.#canvas = null;
if (this.#resizeTimeoutId) {
clearTimeout(this.#resizeTimeoutId);
this.#resizeTimeoutId = null;
}
}
super.remove();
}
rebuild() {
if (!this.parent) {
if (this.#bitmapId) {
this.#getBitmap();
}
return;
}
super.rebuild();
if (this.div === null) {
return;
}
if (this.#bitmapId && this.#canvas === null) {
this.#getBitmap();
}
if (!this.isAttachedToDOM) {
this.parent.add(this);
}
}
onceAdded(focus) {
this._isDraggable = true;
if (focus) {
this.div.focus();
}
}
isEmpty() {
return !(this.#bitmapPromise || this.#bitmap || this.#bitmapUrl || this.#bitmapFile || this.#bitmapId);
}
get isResizable() {
return true;
}
render() {
if (this.div) {
return this.div;
}
let baseX, baseY;
if (this.width) {
baseX = this.x;
baseY = this.y;
}
super.render();
this.div.hidden = true;
this.div.setAttribute("role", "figure");
this.addAltTextButton();
if (this.#bitmap) {
this.#createCanvas();
} else {
this.#getBitmap();
}
if (this.width && !this.annotationElementId) {
const [parentWidth, parentHeight] = this.parentDimensions;
this.setAt(baseX * parentWidth, baseY * parentHeight, this.width * parentWidth, this.height * parentHeight);
}
this._uiManager.addShouldRescale(this);
return this.div;
}
_onResized() {
this.onScaleChanging();
}
onScaleChanging() {
if (!this.parent) {
return;
}
if (this.#resizeTimeoutId !== null) {
clearTimeout(this.#resizeTimeoutId);
}
const TIME_TO_WAIT = 200;
this.#resizeTimeoutId = setTimeout(() => {
this.#resizeTimeoutId = null;
this.#drawBitmap();
}, TIME_TO_WAIT);
}
#createCanvas() {
const {
div
} = this;
let {
width,
height
} = this.#bitmap;
const [pageWidth, pageHeight] = this.pageDimensions;
const MAX_RATIO = 0.75;
if (this.width) {
width = this.width * pageWidth;
height = this.height * pageHeight;
} else if (width > MAX_RATIO * pageWidth || height > MAX_RATIO * pageHeight) {
const factor = Math.min(MAX_RATIO * pageWidth / width, MAX_RATIO * pageHeight / height);
width *= factor;
height *= factor;
}
const [parentWidth, parentHeight] = this.parentDimensions;
this.setDims(width * parentWidth / pageWidth, height * parentHeight / pageHeight);
this._uiManager.enableWaiting(false);
const canvas = this.#canvas = document.createElement("canvas");
canvas.setAttribute("role", "img");
this.addContainer(canvas);
this.width = width / pageWidth;
this.height = height / pageHeight;
if (this._initialOptions?.isCentered) {
this.center();
} else {
this.fixAndSetPosition();
}
this._initialOptions = null;
if (!this._uiManager.useNewAltTextWhenAddingImage || !this._uiManager.useNewAltTextFlow || this.annotationElementId) {
div.hidden = false;
}
this.#drawBitmap();
if (!this.#hasBeenAddedInUndoStack) {
this.parent.addUndoableEditor(this);
this.#hasBeenAddedInUndoStack = true;
}
this._reportTelemetry({
action: "inserted_image"
});
if (this.#bitmapFileName) {
canvas.setAttribute("aria-label", this.#bitmapFileName);
}
}
copyCanvas(maxDataDimension, maxPreviewDimension, createImageData = false) {
if (!maxDataDimension) {
maxDataDimension = 224;
}
const {
width: bitmapWidth,
height: bitmapHeight
} = this.#bitmap;
const outputScale = new display_utils.OutputScale();
let bitmap = this.#bitmap;
let width = bitmapWidth,
height = bitmapHeight;
let canvas = null;
if (maxPreviewDimension) {
if (bitmapWidth > maxPreviewDimension || bitmapHeight > maxPreviewDimension) {
const ratio = Math.min(maxPreviewDimension / bitmapWidth, maxPreviewDimension / bitmapHeight);
width = Math.floor(bitmapWidth * ratio);
height = Math.floor(bitmapHeight * ratio);
}
canvas = document.createElement("canvas");
const scaledWidth = canvas.width = Math.ceil(width * outputScale.sx);
const scaledHeight = canvas.height = Math.ceil(height * outputScale.sy);
if (!this.#isSvg) {
bitmap = this.#scaleBitmap(scaledWidth, scaledHeight);
}
const ctx = canvas.getContext("2d");
ctx.filter = this._uiManager.hcmFilter;
let white = "white",
black = "#cfcfd8";
if (this._uiManager.hcmFilter !== "none") {
black = "black";
} else if (window.matchMedia?.("(prefers-color-scheme: dark)").matches) {
white = "#8f8f9d";
black = "#42414d";
}
const boxDim = 15;
const boxDimWidth = boxDim * outputScale.sx;
const boxDimHeight = boxDim * outputScale.sy;
const pattern = new OffscreenCanvas(boxDimWidth * 2, boxDimHeight * 2);
const patternCtx = pattern.getContext("2d");
patternCtx.fillStyle = white;
patternCtx.fillRect(0, 0, boxDimWidth * 2, boxDimHeight * 2);
patternCtx.fillStyle = black;
patternCtx.fillRect(0, 0, boxDimWidth, boxDimHeight);
patternCtx.fillRect(boxDimWidth, boxDimHeight, boxDimWidth, boxDimHeight);
ctx.fillStyle = ctx.createPattern(pattern, "repeat");
ctx.fillRect(0, 0, scaledWidth, scaledHeight);
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight);
}
let imageData = null;
if (createImageData) {
let dataWidth, dataHeight;
if (outputScale.symmetric && bitmap.width < maxDataDimension && bitmap.height < maxDataDimension) {
dataWidth = bitmap.width;
dataHeight = bitmap.height;
} else {
bitmap = this.#bitmap;
if (bitmapWidth > maxDataDimension || bitmapHeight > maxDataDimension) {
const ratio = Math.min(maxDataDimension / bitmapWidth, maxDataDimension / bitmapHeight);
dataWidth = Math.floor(bitmapWidth * ratio);
dataHeight = Math.floor(bitmapHeight * ratio);
if (!this.#isSvg) {
bitmap = this.#scaleBitmap(dataWidth, dataHeight);
}
}
}
const offscreen = new OffscreenCanvas(dataWidth, dataHeight);
const offscreenCtx = offscreen.getContext("2d", {
willReadFrequently: true
});
offscreenCtx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, dataWidth, dataHeight);
imageData = {
width: dataWidth,
height: dataHeight,
data: offscreenCtx.getImageData(0, 0, dataWidth, dataHeight).data
};
}
return {
canvas,
width,
height,
imageData
};
}
#scaleBitmap(width, height) {
const {
width: bitmapWidth,
height: bitmapHeight
} = this.#bitmap;
let newWidth = bitmapWidth;
let newHeight = bitmapHeight;
let bitmap = this.#bitmap;
while (newWidth > 2 * width || newHeight > 2 * height) {
const prevWidth = newWidth;
const prevHeight = newHeight;
if (newWidth > 2 * width) {
newWidth = newWidth >= 16384 ? Math.floor(newWidth / 2) - 1 : Math.ceil(newWidth / 2);
}
if (newHeight > 2 * height) {
newHeight = newHeight >= 16384 ? Math.floor(newHeight / 2) - 1 : Math.ceil(newHeight / 2);
}
const offscreen = new OffscreenCanvas(newWidth, newHeight);
const ctx = offscreen.getContext("2d");
ctx.drawImage(bitmap, 0, 0, prevWidth, prevHeight, 0, 0, newWidth, newHeight);
bitmap = offscreen.transferToImageBitmap();
}
return bitmap;
}
#drawBitmap() {
const [parentWidth, parentHeight] = this.parentDimensions;
const {
width,
height
} = this;
const outputScale = new display_utils.OutputScale();
const scaledWidth = Math.ceil(width * parentWidth * outputScale.sx);
const scaledHeight = Math.ceil(height * parentHeight * outputScale.sy);
const canvas = this.#canvas;
if (!canvas || canvas.width === scaledWidth && canvas.height === scaledHeight) {
return;
}
canvas.width = scaledWidth;
canvas.height = scaledHeight;
const bitmap = this.#isSvg ? this.#bitmap : this.#scaleBitmap(scaledWidth, scaledHeight);
const ctx = canvas.getContext("2d");
ctx.filter = this._uiManager.hcmFilter;
ctx.drawImage(bitmap, 0, 0, bitmap.width, bitmap.height, 0, 0, scaledWidth, scaledHeight);
}
getImageForAltText() {
return this.#canvas;
}
#serializeBitmap(toUrl) {
if (toUrl) {
if (this.#isSvg) {
const url = this._uiManager.imageManager.getSvgUrl(this.#bitmapId);
if (url) {
return url;
}
}
const canvas = document.createElement("canvas");
({
width: canvas.width,
height: canvas.height
} = this.#bitmap);
const ctx = canvas.getContext("2d");
ctx.drawImage(this.#bitmap, 0, 0);
return canvas.toDataURL();
}
if (this.#isSvg) {
const [pageWidth, pageHeight] = this.pageDimensions;
const width = Math.round(this.width * pageWidth * display_utils.PixelsPerInch.PDF_TO_CSS_UNITS);
const height = Math.round(this.height * pageHeight * display_utils.PixelsPerInch.PDF_TO_CSS_UNITS);
const offscreen = new OffscreenCanvas(width, height);
const ctx = offscreen.getContext("2d");
ctx.drawImage(this.#bitmap, 0, 0, this.#bitmap.width, this.#bitmap.height, 0, 0, width, height);
return offscreen.transferToImageBitmap();
}
return structuredClone(this.#bitmap);
}
static async deserialize(data, parent, uiManager) {
let initialData = null;
if (data instanceof StampAnnotationElement) {
const {
data: {
rect,
rotation,
id,
structParent,
popupRef
},
container,
parent: {
page: {
pageNumber
}
}
} = data;
const canvas = container.querySelector("canvas");
const imageData = uiManager.imageManager.getFromCanvas(container.id, canvas);
canvas.remove();
const altText = (await parent._structTree.getAriaAttributes(`${util.AnnotationPrefix}${id}`))?.get("aria-label") || "";
initialData = data = {
annotationType: util.AnnotationEditorType.STAMP,
bitmapId: imageData.id,
bitmap: imageData.bitmap,
pageIndex: pageNumber - 1,
rect: rect.slice(0),
rotation,
id,
deleted: false,
accessibilityData: {
decorative: false,
altText
},
isSvg: false,
structParent,
popupRef
};
}
const editor = await super.deserialize(data, parent, uiManager);
const {
rect,
bitmap,
bitmapUrl,
bitmapId,
isSvg,
accessibilityData
} = data;
if (bitmapId && uiManager.imageManager.isValidId(bitmapId)) {
editor.#bitmapId = bitmapId;
if (bitmap) {
editor.#bitmap = bitmap;
}
} else {
editor.#bitmapUrl = bitmapUrl;
}
editor.#isSvg = isSvg;
const [parentWidth, parentHeight] = editor.pageDimensions;
editor.width = (rect[2] - rect[0]) / parentWidth;
editor.height = (rect[3] - rect[1]) / parentHeight;
editor.annotationElementId = data.id || null;
if (accessibilityData) {
editor.altTextData = accessibilityData;
}
editor._initialData = initialData;
editor.#hasBeenAddedInUndoStack = !!initialData;
return editor;
}
serialize(isForCopying = false, context = null) {
if (this.isEmpty()) {
return null;
}
if (this.deleted) {
return this.serializeDeleted();
}
const serialized = {
annotationType: util.AnnotationEditorType.STAMP,
bitmapId: this.#bitmapId,
pageIndex: this.pageIndex,
rect: this.getRect(0, 0),
rotation: this.rotation,
isSvg: this.#isSvg,
structTreeParentId: this._structTreeParentId
};
if (isForCopying) {
serialized.bitmapUrl = this.#serializeBitmap(true);
serialized.accessibilityData = this.serializeAltText(true);
return serialized;
}
const {
decorative,
altText
} = this.serializeAltText(false);
if (!decorative && altText) {
serialized.accessibilityData = {
type: "Figure",
alt: altText
};
}
if (this.annotationElementId) {
const changes = this.#hasElementChanged(serialized);
if (changes.isSame) {
return null;
}
if (changes.isSameAltText) {
delete serialized.accessibilityData;
} else {
serialized.accessibilityData.structParent = this._initialData.structParent ?? -1;
}
}
serialized.id = this.annotationElementId;
if (context === null) {
return serialized;
}
context.stamps ||= new Map();
const area = this.#isSvg ? (serialized.rect[2] - serialized.rect[0]) * (serialized.rect[3] - serialized.rect[1]) : null;
if (!context.stamps.has(this.#bitmapId)) {
context.stamps.set(this.#bitmapId, {
area,
serialized
});
serialized.bitmap = this.#serializeBitmap(false);
} else if (this.#isSvg) {
const prevData = context.stamps.get(this.#bitmapId);
if (area > prevData.area) {
prevData.area = area;
prevData.serialized.bitmap.close();
prevData.serialized.bitmap = this.#serializeBitmap(false);
}
}
return serialized;
}
#hasElementChanged(serialized) {
const {
pageIndex,
accessibilityData: {
altText
}
} = this._initialData;
const isSamePageIndex = serialized.pageIndex === pageIndex;
const isSameAltText = (serialized.accessibilityData?.alt || "") === altText;
return {
isSame: !this._hasBeenMoved && !this._hasBeenResized && isSamePageIndex && isSameAltText,
isSameAltText
};
}
renderAnnotationElement(annotation) {
annotation.updateEdited({
rect: this.getRect(0, 0)
});
return null;
}
}
;// ./src/display/editor/annotation_editor_layer.js
class PointerType {
static current = null;
static editor = null;
constructor(t) {
if (PointerType.current === null) {
PointerType.current = "";
window.addEventListener("pointerdown", this.windowPointerDown, !0);
}
}
destroy() {
if (PointerType.current !== null) {
window.removeEventListener("pointerdown", this.windowPointerDown, !0);
PointerType.current = null;
}
}
windowPointerDown(t) {
PointerType.current = t.pointerType;
return !0;
}
static initializeEditor() {
setTimeout(() => PointerType.editor = PointerType.current);
}
static sameAsEditor(pointerType = undefined) {
if (pointerType) {
return PointerType.editor == pointerType;
}
return PointerType.editor == PointerType.current;
}
}
new PointerType();
class AnnotationEditorLayer {
#accessibilityManager;
#allowClick = false;
#annotationLayer = null;
#clickAC = null;
#editorFocusTimeoutId = null;
#editors = new Map();
#hadPointerDown = false;
#isDisabling = false;
#isEnabling = false;
#drawingAC = null;
#focusedElement = null;
#textLayer = null;
#textSelectionAC = null;
#uiManager;
static _initialized = false;
static #editorTypes = new Map([FreeTextEditor, InkEditor, StampEditor, HighlightEditor].map(type => [type._editorType, type]));
constructor({
uiManager,
pageIndex,
div,
structTreeLayer,
accessibilityManager,
annotationLayer,
drawLayer,
textLayer,
viewport,
l10n,
eventBus
}) {
const editorTypes = [...AnnotationEditorLayer.#editorTypes.values()];
if (!AnnotationEditorLayer._initialized) {
AnnotationEditorLayer._initialized = true;
for (const editorType of editorTypes) {
editorType.initialize(l10n, uiManager);
}
}
uiManager.registerEditorTypes(editorTypes);
this.#uiManager = uiManager;
this.pageIndex = pageIndex;
this.div = div;
this.#accessibilityManager = accessibilityManager;
this.#annotationLayer = annotationLayer;
this.viewport = viewport;
this.#textLayer = textLayer;
this.drawLayer = drawLayer;
this._structTree = structTreeLayer;
this.#uiManager.addLayer(this);
this.eventBus = eventBus;
}
get isEmpty() {
return this.#editors.size === 0;
}
get isInvisible() {
return this.isEmpty && this.#uiManager.getMode() === util.AnnotationEditorType.NONE;
}
updateToolbar(mode) {
this.#uiManager.updateToolbar(mode);
}
updateMode(mode = this.#uiManager.getMode()) {
this.#cleanup();
switch (mode) {
case util.AnnotationEditorType.NONE:
this.disableTextSelection();
this.togglePointerEvents(false);
this.toggleAnnotationLayerPointerEvents(true);
this.disableClick();
return;
case util.AnnotationEditorType.INK:
this.disableTextSelection();
this.togglePointerEvents(true);
this.enableClick();
break;
case util.AnnotationEditorType.HIGHLIGHT:
this.enableTextSelection();
this.togglePointerEvents(false);
this.disableClick();
break;
default:
this.disableTextSelection();
this.togglePointerEvents(true);
this.enableClick();
}
PointerType.initializeEditor();
this.toggleAnnotationLayerPointerEvents(false);
const {
classList
} = this.div;
for (const editorType of AnnotationEditorLayer.#editorTypes.values()) {
classList.toggle(`${editorType._type}Editing`, mode === editorType._editorType);
}
this.div.hidden = false;
}
hasTextLayer(textLayer) {
return textLayer === this.#textLayer?.div;
}
setEditingState(isEditing) {
this.#uiManager.setEditingState(isEditing);
}
addCommands(params) {
this.#uiManager.addCommands(params);
}
cleanUndoStack(type) {
this.#uiManager.cleanUndoStack(type);
}
toggleDrawing(enabled = false) {
this.div.classList.toggle("drawing", !enabled);
}
togglePointerEvents(enabled = false) {
this.div.classList.toggle("disabled", !enabled);
}
toggleAnnotationLayerPointerEvents(enabled = false) {
this.#annotationLayer?.div.classList.toggle("disabled", !enabled);
}
async enable() {
this.#isEnabling = true;
this.div.tabIndex = 0;
this.togglePointerEvents(true);
const annotationElementIds = new Set();
for (const editor of this.#editors.values()) {
editor.enableEditing();
editor.show(true);
if (editor.annotationElementId) {
this.#uiManager.removeChangedExistingAnnotation(editor);
annotationElementIds.add(editor.annotationElementId);
}
}
if (!this.#annotationLayer) {
this.#isEnabling = false;
return;
}
const editables = this.#annotationLayer.getEditableAnnotations();
for (const editable of editables) {
editable.hide();
if (this.#uiManager.isDeletedAnnotationElement(editable.data.id)) {
continue;
}
if (annotationElementIds.has(editable.data.id)) {
continue;
}
const editor = await this.deserialize(editable);
if (!editor) {
continue;
}
this.addOrRebuild(editor);
editor.enableEditing();
}
this.#isEnabling = false;
}
disable() {
this.#isDisabling = true;
this.div.tabIndex = -1;
this.togglePointerEvents(false);
const changedAnnotations = new Map();
const resetAnnotations = new Map();
for (const editor of this.#editors.values()) {
editor.disableEditing();
if (!editor.annotationElementId) {
continue;
}
if (editor.serialize() !== null) {
changedAnnotations.set(editor.annotationElementId, editor);
continue;
} else {
resetAnnotations.set(editor.annotationElementId, editor);
}
this.getEditableAnnotation(editor.annotationElementId)?.show();
editor.remove();
}
if (this.#annotationLayer) {
const editables = this.#annotationLayer.getEditableAnnotations();
for (const editable of editables) {
const {
id
} = editable.data;
if (this.#uiManager.isDeletedAnnotationElement(id)) {
continue;
}
let editor = resetAnnotations.get(id);
if (editor) {
editor.resetAnnotationElement(editable);
editor.show(false);
editable.show();
continue;
}
editor = changedAnnotations.get(id);
if (editor) {
this.#uiManager.addChangedExistingAnnotation(editor);
if (editor.renderAnnotationElement(editable)) {
editor.show(false);
}
}
editable.show();
}
}
this.#cleanup();
if (this.isEmpty) {
this.div.hidden = true;
}
const {
classList
} = this.div;
for (const editorType of AnnotationEditorLayer.#editorTypes.values()) {
classList.remove(`${editorType._type}Editing`);
}
this.disableTextSelection();
this.toggleAnnotationLayerPointerEvents(true);
this.#isDisabling = false;
}
getEditableAnnotation(id) {
return this.#annotationLayer?.getEditableAnnotation(id) || null;
}
setActiveEditor(editor) {
const currentActive = this.#uiManager.getActive();
if (currentActive === editor) {
return;
}
this.#uiManager.setActiveEditor(editor);
}
enableTextSelection() {
this.div.tabIndex = -1;
if (this.#textLayer?.div && !this.#textSelectionAC) {
this.#textSelectionAC = new AbortController();
const signal = this.#uiManager.combinedSignal(this.#textSelectionAC);
this.#textLayer.div.addEventListener("pointerdown", this.#textLayerPointerDown.bind(this), {
signal
});
this.#textLayer.div.classList.add("highlighting");
}
}
disableTextSelection() {
this.div.tabIndex = 0;
if (this.#textLayer?.div && this.#textSelectionAC) {
this.#textSelectionAC.abort();
this.#textSelectionAC = null;
this.#textLayer.div.classList.remove("highlighting");
}
}
#textLayerPointerDown(event) {
this.#uiManager.unselectAll();
const {
target
} = event;
if (target === this.#textLayer.div || (target.getAttribute("role") === "img" || target.classList.contains("endOfContent")) && this.#textLayer.div.contains(target)) {
const {
isMac
} = util.FeatureTest.platform;
if (event.button !== 0 || event.ctrlKey && isMac) {
return;
}
this.#uiManager.showAllEditors("highlight", true, true);
this.#textLayer.div.classList.add("free");
this.toggleDrawing();
HighlightEditor.startHighlighting(this, this.#uiManager.direction === "ltr", {
target: this.#textLayer.div,
x: event.x,
y: event.y
});
this.#textLayer.div.addEventListener("pointerup", () => {
this.#textLayer.div.classList.remove("free");
this.toggleDrawing(true);
}, {
once: true,
signal: this.#uiManager._signal
});
event.preventDefault();
}
}
enableClick() {
if (this.#clickAC) {
return;
}
this.#clickAC = new AbortController();
const signal = this.#uiManager.combinedSignal(this.#clickAC);
this.div.addEventListener("pointerdown", this.pointerdown.bind(this), {
signal
});
const pointerup = this.pointerup.bind(this);
this.div.addEventListener("pointerup", pointerup, {
signal
});
this.div.addEventListener("pointercancel", pointerup, {
signal
});
}
disableClick() {
this.#clickAC?.abort();
this.#clickAC = null;
}
attach(editor) {
this.#editors.set(editor.id, editor);
const {
annotationElementId
} = editor;
if (annotationElementId && this.#uiManager.isDeletedAnnotationElement(annotationElementId)) {
this.#uiManager.removeDeletedAnnotationElement(editor);
}
}
detach(editor) {
this.#editors.delete(editor.id);
this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv);
if (!this.#isDisabling && editor.annotationElementId) {
this.#uiManager.addDeletedAnnotationElement(editor);
}
}
remove(editor) {
this.detach(editor);
this.#uiManager.removeEditor(editor);
editor.div.remove();
editor.isAttachedToDOM = false;
}
changeParent(editor) {
if (editor.parent === this) {
return;
}
if (editor.parent && editor.annotationElementId) {
this.#uiManager.addDeletedAnnotationElement(editor.annotationElementId);
editor_editor.AnnotationEditor.deleteAnnotationElement(editor);
editor.annotationElementId = null;
}
this.attach(editor);
editor.parent?.detach(editor);
editor.setParent(this);
if (editor.div && editor.isAttachedToDOM) {
editor.div.remove();
this.div.append(editor.div);
}
}
add(editor) {
if (editor.parent === this && editor.isAttachedToDOM) {
return;
}
this.changeParent(editor);
this.#uiManager.addEditor(editor);
this.attach(editor);
if (!editor.isAttachedToDOM) {
const div = editor.render();
this.div.append(div);
editor.isAttachedToDOM = true;
}
editor.fixAndSetPosition();
editor.onceAdded(!this.#isEnabling);
this.#uiManager.addToAnnotationStorage(editor);
editor._reportTelemetry(editor.telemetryInitialData);
}
moveEditorInDOM(editor) {
if (!editor.isAttachedToDOM) {
return;
}
const {
activeElement
} = document;
if (editor.div.contains(activeElement) && !this.#editorFocusTimeoutId) {
editor._focusEventsAllowed = false;
this.#editorFocusTimeoutId = setTimeout(() => {
this.#editorFocusTimeoutId = null;
if (!editor.div.contains(document.activeElement)) {
editor.div.addEventListener("focusin", () => {
editor._focusEventsAllowed = true;
}, {
once: true,
signal: this.#uiManager._signal
});
activeElement.focus();
} else {
editor._focusEventsAllowed = true;
}
}, 0);
}
editor._structTreeParentId = this.#accessibilityManager?.moveElementInDOM(this.div, editor.div, editor.contentDiv, true);
}
addOrRebuild(editor) {
if (editor.needsToBeRebuilt()) {
editor.parent ||= this;
editor.rebuild();
editor.show();
} else {
this.add(editor);
}
}
addUndoableEditor(editor) {
const cmd = () => editor._uiManager.rebuild(editor);
const undo = () => {
editor.remove();
};
this.addCommands({
cmd,
undo,
mustExec: false
});
}
getNextId() {
return this.#uiManager.getId();
}
get #currentEditorType() {
return AnnotationEditorLayer.#editorTypes.get(this.#uiManager.getMode());
}
combinedSignal(ac) {
return this.#uiManager.combinedSignal(ac);
}
#createNewEditor(params) {
const editorType = this.#currentEditorType;
return editorType ? new editorType.prototype.constructor(params) : null;
}
canCreateNewEmptyEditor() {
return this.#currentEditorType?.canCreateNewEmptyEditor();
}
pasteEditor(mode, params) {
this.#uiManager.updateToolbar(mode);
this.#uiManager.updateMode(mode);
const {
offsetX,
offsetY
} = this.#getCenterPoint();
const id = this.getNextId();
const editor = this.#createNewEditor({
parent: this,
id,
x: offsetX,
y: offsetY,
uiManager: this.#uiManager,
isCentered: true,
...params
});
if (editor) {
this.add(editor);
}
}
async deserialize(data) {
return (await AnnotationEditorLayer.#editorTypes.get(data.annotationType ?? data.annotationEditorType)?.deserialize(data, this, this.#uiManager)) || null;
}
createAndAddNewEditor(event, isCentered, data = {}) {
const id = this.getNextId();
const editor = this.#createNewEditor({
parent: this,
id,
x: event.offsetX,
y: event.offsetY,
uiManager: this.#uiManager,
isCentered,
eventBus: this.eventBus,
...data
});
if (editor) {
this.add(editor);
}
return editor;
}
#getCenterPoint() {
const {
x,
y,
width,
height
} = this.div.getBoundingClientRect();
const tlX = Math.max(0, x);
const tlY = Math.max(0, y);
const brX = Math.min(window.innerWidth, x + width);
const brY = Math.min(window.innerHeight, y + height);
const centerX = (tlX + brX) / 2 - x;
const centerY = (tlY + brY) / 2 - y;
const [offsetX, offsetY] = this.viewport.rotation % 180 === 0 ? [centerX, centerY] : [centerY, centerX];
return {
offsetX,
offsetY
};
}
addNewEditor() {
this.createAndAddNewEditor(this.#getCenterPoint(), true);
}
setSelected(editor) {
this.#uiManager.setSelected(editor);
}
toggleSelected(editor) {
this.#uiManager.toggleSelected(editor);
}
unselect(editor) {
this.#uiManager.unselect(editor);
}
pointerup(event) {
const {
isMac
} = util.FeatureTest.platform;
if (event.button !== 0 || event.ctrlKey && isMac) {
return;
}
if (event.target !== this.div) {
return;
}
if (!this.#hadPointerDown) {
return;
}
this.#hadPointerDown = false;
if (this.#currentEditorType?.isDrawer && this.#currentEditorType.supportMultipleDrawings) {
return;
}
if (!this.#allowClick) {
this.#allowClick = true;
return;
}
if (this.#uiManager.getMode() === util.AnnotationEditorType.STAMP) {
this.#uiManager.unselectAll();
return;
}
this.createAndAddNewEditor(event, false);
}
pointerdown(event) {
if (this.#uiManager.getMode() === util.AnnotationEditorType.HIGHLIGHT) {
if (!PointerType.sameAsEditor(event.pointerType)) {
return;
}
this.enableTextSelection();
} else if (this.#uiManager.getMode() === util.AnnotationEditorType.INK && !PointerType.sameAsEditor(event.pointerType)) {
return;
}
if (this.#hadPointerDown) {
this.#hadPointerDown = false;
return;
}
const {
isMac
} = util.FeatureTest.platform;
if (event.button !== 0 || event.ctrlKey && isMac) {
return;
}
if (event.target !== this.div) {
return;
}
this.#hadPointerDown = true;
if (this.#currentEditorType?.isDrawer) {
this.startDrawingSession(event);
return;
}
const editor = this.#uiManager.getActive();
this.#allowClick = !editor || editor.isEmpty();
}
startDrawingSession(event) {
this.div.focus();
if (this.#drawingAC) {
this.#currentEditorType.startDrawing(this, this.#uiManager, false, event);
return;
}
this.#uiManager.setCurrentDrawingSession(this);
this.#drawingAC = new AbortController();
const signal = this.#uiManager.combinedSignal(this.#drawingAC);
this.div.addEventListener("blur", ({
relatedTarget
}) => {
if (relatedTarget && !this.div.contains(relatedTarget)) {
this.#focusedElement = null;
this.commitOrRemove();
}
}, {
signal
});
this.#currentEditorType.startDrawing(this, this.#uiManager, false, event);
}
pause(on) {
if (on) {
const {
activeElement
} = document;
if (this.div.contains(activeElement)) {
this.#focusedElement = activeElement;
}
return;
}
if (this.#focusedElement) {
setTimeout(() => {
this.#focusedElement?.focus();
this.#focusedElement = null;
}, 0);
}
}
endDrawingSession(isAborted = false) {
if (!this.#drawingAC) {
return null;
}
this.#uiManager.setCurrentDrawingSession(null);
this.#drawingAC.abort();
this.#drawingAC = null;
this.#focusedElement = null;
return this.#currentEditorType.endDrawing(isAborted);
}
findNewParent(editor, x, y) {
const layer = this.#uiManager.findParent(x, y);
if (layer === null || layer === this) {
return false;
}
layer.changeParent(editor);
return true;
}
commitOrRemove() {
if (this.#drawingAC) {
this.endDrawingSession();
return true;
}
return false;
}
onScaleChanging() {
if (!this.#drawingAC) {
return;
}
this.#currentEditorType.onScaleChangingWhenDrawing(this);
}
destroy() {
this.commitOrRemove();
if (this.#uiManager.getActive()?.parent === this) {
this.#uiManager.commitOrRemove();
this.#uiManager.setActiveEditor(null);
}
if (this.#editorFocusTimeoutId) {
clearTimeout(this.#editorFocusTimeoutId);
this.#editorFocusTimeoutId = null;
}
for (const editor of this.#editors.values()) {
this.#accessibilityManager?.removePointerInTextLayer(editor.contentDiv);
editor.setParent(null);
editor.isAttachedToDOM = false;
editor.div.remove();
}
this.div = null;
this.#editors.clear();
this.#uiManager.removeLayer(this);
}
#cleanup() {
for (const editor of this.#editors.values()) {
if (editor.isEmpty()) {
editor.remove();
}
}
}
render({
viewport
}) {
this.viewport = viewport;
(0,display_utils.setLayerDimensions)(this.div, viewport);
for (const editor of this.#uiManager.getEditors(this.pageIndex)) {
this.add(editor);
editor.rebuild();
}
this.updateMode();
}
update({
viewport
}) {
this.#uiManager.commitOrRemove();
this.#cleanup();
const oldRotation = this.viewport.rotation;
const rotation = viewport.rotation;
this.viewport = viewport;
(0,display_utils.setLayerDimensions)(this.div, {
rotation
});
if (oldRotation !== rotation) {
for (const editor of this.#editors.values()) {
editor.rotate(rotation);
}
}
}
get pageDimensions() {
const {
pageWidth,
pageHeight
} = this.viewport.rawDims;
return [pageWidth, pageHeight];
}
get scale() {
return this.#uiManager.viewParameters.realScale;
}
}
;// ./src/display/draw_layer.js
class DrawLayer {
#parent = null;
#id = 0;
#mapping = new Map();
#toUpdate = new Map();
constructor({
pageIndex
}) {
this.pageIndex = pageIndex;
}
setParent(parent) {
if (!this.#parent) {
this.#parent = parent;
return;
}
if (this.#parent !== parent) {
if (this.#mapping.size > 0) {
for (const root of this.#mapping.values()) {
root.remove();
parent.append(root);
}
}
this.#parent = parent;
}
}
static get _svgFactory() {
return (0,util.shadow)(this, "_svgFactory", new DOMSVGFactory());
}
static #setBox(element, [x, y, width, height]) {
const {
style
} = element;
style.top = `${100 * y}%`;
style.left = `${100 * x}%`;
style.width = `${100 * width}%`;
style.height = `${100 * height}%`;
}
#createSVG() {
const svg = DrawLayer._svgFactory.create(1, 1, true);
this.#parent.append(svg);
svg.setAttribute("aria-hidden", true);
return svg;
}
#createClipPath(defs, pathId) {
const clipPath = DrawLayer._svgFactory.createElement("clipPath");
defs.append(clipPath);
const clipPathId = `clip_${pathId}`;
clipPath.setAttribute("id", clipPathId);
clipPath.setAttribute("clipPathUnits", "objectBoundingBox");
const clipPathUse = DrawLayer._svgFactory.createElement("use");
clipPath.append(clipPathUse);
clipPathUse.setAttribute("href", `#${pathId}`);
clipPathUse.classList.add("clip");
return clipPathId;
}
#updateProperties(element, properties) {
for (const [key, value] of Object.entries(properties)) {
if (value === null) {
element.removeAttribute(key);
} else {
element.setAttribute(key, value);
}
}
}
draw(properties, isPathUpdatable = false, hasClip = false) {
const id = this.#id++;
const root = this.#createSVG();
const defs = DrawLayer._svgFactory.createElement("defs");
root.append(defs);
const path = DrawLayer._svgFactory.createElement("path");
defs.append(path);
const pathId = `path_p${this.pageIndex}_${id}`;
path.setAttribute("id", pathId);
path.setAttribute("vector-effect", "non-scaling-stroke");
if (isPathUpdatable) {
this.#toUpdate.set(id, path);
}
const clipPathId = hasClip ? this.#createClipPath(defs, pathId) : null;
const use = DrawLayer._svgFactory.createElement("use");
root.append(use);
use.setAttribute("href", `#${pathId}`);
this.updateProperties(root, properties);
this.#mapping.set(id, root);
return {
id,
clipPathId: `url(#${clipPathId})`
};
}
drawOutline(properties, mustRemoveSelfIntersections) {
const id = this.#id++;
const root = this.#createSVG();
const defs = DrawLayer._svgFactory.createElement("defs");
root.append(defs);
const path = DrawLayer._svgFactory.createElement("path");
defs.append(path);
const pathId = `path_p${this.pageIndex}_${id}`;
path.setAttribute("id", pathId);
path.setAttribute("vector-effect", "non-scaling-stroke");
let maskId;
if (mustRemoveSelfIntersections) {
const mask = DrawLayer._svgFactory.createElement("mask");
defs.append(mask);
maskId = `mask_p${this.pageIndex}_${id}`;
mask.setAttribute("id", maskId);
mask.setAttribute("maskUnits", "objectBoundingBox");
const rect = DrawLayer._svgFactory.createElement("rect");
mask.append(rect);
rect.setAttribute("width", "1");
rect.setAttribute("height", "1");
rect.setAttribute("fill", "white");
const use = DrawLayer._svgFactory.createElement("use");
mask.append(use);
use.setAttribute("href", `#${pathId}`);
use.setAttribute("stroke", "none");
use.setAttribute("fill", "black");
use.setAttribute("fill-rule", "nonzero");
use.classList.add("mask");
}
const use1 = DrawLayer._svgFactory.createElement("use");
root.append(use1);
use1.setAttribute("href", `#${pathId}`);
if (maskId) {
use1.setAttribute("mask", `url(#${maskId})`);
}
const use2 = use1.cloneNode();
root.append(use2);
use1.classList.add("mainOutline");
use2.classList.add("secondaryOutline");
this.updateProperties(root, properties);
this.#mapping.set(id, root);
return id;
}
finalizeDraw(id, properties) {
this.#toUpdate.delete(id);
this.updateProperties(id, properties);
}
updateProperties(elementOrId, properties) {
if (!properties) {
return;
}
const {
root,
bbox,
rootClass,
path
} = properties;
const element = typeof elementOrId === "number" ? this.#mapping.get(elementOrId) : elementOrId;
if (!element) {
return;
}
if (root) {
this.#updateProperties(element, root);
}
if (bbox) {
DrawLayer.#setBox(element, bbox);
}
if (rootClass) {
const {
classList
} = element;
for (const [className, value] of Object.entries(rootClass)) {
classList.toggle(className, value);
}
}
if (path) {
const defs = element.firstChild;
const pathElement = defs.firstChild;
this.#updateProperties(pathElement, path);
}
}
updateParent(id, layer) {
if (layer === this) {
return;
}
const root = this.#mapping.get(id);
if (!root) {
return;
}
layer.#parent.append(root);
this.#mapping.delete(id);
layer.#mapping.set(id, root);
}
remove(id) {
this.#toUpdate.delete(id);
if (this.#parent === null) {
return;
}
this.#mapping.get(id).remove();
this.#mapping.delete(id);
}
destroy() {
this.#parent = null;
for (const root of this.#mapping.values()) {
root.remove();
}
this.#mapping.clear();
this.#toUpdate.clear();
}
}
// EXTERNAL MODULE: ./src/display/worker_options.js
var worker_options = __webpack_require__(164);
// EXTERNAL MODULE: ./src/display/text_layer.js
var text_layer = __webpack_require__(814);
// EXTERNAL MODULE: ./src/display/touch_manager.js
var touch_manager = __webpack_require__(900);
;// ./src/pdf.js
const pdfjsVersion = "4.10.708";
const pdfjsBuild = "f47106c3e";
{
globalThis.pdfjsTestingUtils = {
HighlightOutliner: HighlightOutliner
};
}
/***/ }),
/***/ 651:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ MurmurHash3_64: () => (/* binding */ MurmurHash3_64)
/* harmony export */ });
const SEED = 0xc3d2e1f0;
const MASK_HIGH = 0xffff0000;
const MASK_LOW = 0xffff;
class MurmurHash3_64 {
constructor(seed) {
this.h1 = seed ? seed & 0xffffffff : SEED;
this.h2 = seed ? seed & 0xffffffff : SEED;
}
update(input) {
let data, length;
if (typeof input === "string") {
data = new Uint8Array(input.length * 2);
length = 0;
for (let i = 0, ii = input.length; i < ii; i++) {
const code = input.charCodeAt(i);
if (code <= 0xff) {
data[length++] = code;
} else {
data[length++] = code >>> 8;
data[length++] = code & 0xff;
}
}
} else if (ArrayBuffer.isView(input)) {
data = input.slice();
length = data.byteLength;
} else {
throw new Error("Invalid data format, must be a string or TypedArray.");
}
const blockCounts = length >> 2;
const tailLength = length - blockCounts * 4;
const dataUint32 = new Uint32Array(data.buffer, 0, blockCounts);
let k1 = 0,
k2 = 0;
let h1 = this.h1,
h2 = this.h2;
const C1 = 0xcc9e2d51,
C2 = 0x1b873593;
const C1_LOW = C1 & MASK_LOW,
C2_LOW = C2 & MASK_LOW;
for (let i = 0; i < blockCounts; i++) {
if (i & 1) {
k1 = dataUint32[i];
k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;
k1 = k1 << 15 | k1 >>> 17;
k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;
h1 ^= k1;
h1 = h1 << 13 | h1 >>> 19;
h1 = h1 * 5 + 0xe6546b64;
} else {
k2 = dataUint32[i];
k2 = k2 * C1 & MASK_HIGH | k2 * C1_LOW & MASK_LOW;
k2 = k2 << 15 | k2 >>> 17;
k2 = k2 * C2 & MASK_HIGH | k2 * C2_LOW & MASK_LOW;
h2 ^= k2;
h2 = h2 << 13 | h2 >>> 19;
h2 = h2 * 5 + 0xe6546b64;
}
}
k1 = 0;
switch (tailLength) {
case 3:
k1 ^= data[blockCounts * 4 + 2] << 16;
case 2:
k1 ^= data[blockCounts * 4 + 1] << 8;
case 1:
k1 ^= data[blockCounts * 4];
k1 = k1 * C1 & MASK_HIGH | k1 * C1_LOW & MASK_LOW;
k1 = k1 << 15 | k1 >>> 17;
k1 = k1 * C2 & MASK_HIGH | k1 * C2_LOW & MASK_LOW;
if (blockCounts & 1) {
h1 ^= k1;
} else {
h2 ^= k1;
}
}
this.h1 = h1;
this.h2 = h2;
}
hexdigest() {
let h1 = this.h1,
h2 = this.h2;
h1 ^= h2 >>> 1;
h1 = h1 * 0xed558ccd & MASK_HIGH | h1 * 0x8ccd & MASK_LOW;
h2 = h2 * 0xff51afd7 & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16;
h1 ^= h2 >>> 1;
h1 = h1 * 0x1a85ec53 & MASK_HIGH | h1 * 0xec53 & MASK_LOW;
h2 = h2 * 0xc4ceb9fe & MASK_HIGH | ((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16;
h1 ^= h2 >>> 1;
return (h1 >>> 0).toString(16).padStart(8, "0") + (h2 >>> 0).toString(16).padStart(8, "0");
}
}
/***/ }),
/***/ 292:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ AbortException: () => (/* binding */ AbortException),
/* harmony export */ AnnotationBorderStyleType: () => (/* binding */ AnnotationBorderStyleType),
/* harmony export */ AnnotationEditorParamsType: () => (/* binding */ AnnotationEditorParamsType),
/* harmony export */ AnnotationEditorPrefix: () => (/* binding */ AnnotationEditorPrefix),
/* harmony export */ AnnotationEditorType: () => (/* binding */ AnnotationEditorType),
/* harmony export */ AnnotationMode: () => (/* binding */ AnnotationMode),
/* harmony export */ AnnotationPrefix: () => (/* binding */ AnnotationPrefix),
/* harmony export */ AnnotationType: () => (/* binding */ AnnotationType),
/* harmony export */ BaseException: () => (/* binding */ BaseException),
/* harmony export */ FONT_IDENTITY_MATRIX: () => (/* binding */ FONT_IDENTITY_MATRIX),
/* harmony export */ FeatureTest: () => (/* binding */ FeatureTest),
/* harmony export */ FormatError: () => (/* binding */ FormatError),
/* harmony export */ IDENTITY_MATRIX: () => (/* binding */ IDENTITY_MATRIX),
/* harmony export */ ImageKind: () => (/* binding */ ImageKind),
/* harmony export */ InvalidPDFException: () => (/* binding */ InvalidPDFException),
/* harmony export */ LINE_FACTOR: () => (/* binding */ LINE_FACTOR),
/* harmony export */ MAX_IMAGE_SIZE_TO_CACHE: () => (/* binding */ MAX_IMAGE_SIZE_TO_CACHE),
/* harmony export */ MissingPDFException: () => (/* binding */ MissingPDFException),
/* harmony export */ OPS: () => (/* binding */ OPS),
/* harmony export */ PasswordException: () => (/* binding */ PasswordException),
/* harmony export */ PasswordResponses: () => (/* binding */ PasswordResponses),
/* harmony export */ PermissionFlag: () => (/* binding */ PermissionFlag),
/* harmony export */ RenderingIntentFlag: () => (/* binding */ RenderingIntentFlag),
/* harmony export */ TextRenderingMode: () => (/* binding */ TextRenderingMode),
/* harmony export */ UnexpectedResponseException: () => (/* binding */ UnexpectedResponseException),
/* harmony export */ UnknownErrorException: () => (/* binding */ UnknownErrorException),
/* harmony export */ Util: () => (/* binding */ Util),
/* harmony export */ VerbosityLevel: () => (/* binding */ VerbosityLevel),
/* harmony export */ assert: () => (/* binding */ assert),
/* harmony export */ createValidAbsoluteUrl: () => (/* binding */ createValidAbsoluteUrl),
/* harmony export */ getUuid: () => (/* binding */ getUuid),
/* harmony export */ getVerbosityLevel: () => (/* binding */ getVerbosityLevel),
/* harmony export */ info: () => (/* binding */ info),
/* harmony export */ isNodeJS: () => (/* binding */ isNodeJS),
/* harmony export */ normalizeUnicode: () => (/* binding */ normalizeUnicode),
/* harmony export */ objectFromMap: () => (/* binding */ objectFromMap),
/* harmony export */ setVerbosityLevel: () => (/* binding */ setVerbosityLevel),
/* harmony export */ shadow: () => (/* binding */ shadow),
/* harmony export */ string32: () => (/* binding */ string32),
/* harmony export */ stringToBytes: () => (/* binding */ stringToBytes),
/* harmony export */ toBase64Util: () => (/* binding */ toBase64Util),
/* harmony export */ unreachable: () => (/* binding */ unreachable),
/* harmony export */ warn: () => (/* binding */ warn)
/* harmony export */ });
/* unused harmony exports AnnotationActionEventType, AnnotationFieldFlag, AnnotationFlag, AnnotationReplyType, BASELINE_FACTOR, bytesToString, DocumentActionEventType, fromBase64Util, getModificationDate, hexNumbers, isArrayEqual, LINE_DESCENT_FACTOR, objectSize, PageActionEventType, stringToPDFString, stringToUTF8String, toHexUtil, utf8StringToString */
/* harmony import */ var _external_ngx_logger_ngx_console_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(641);
const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser");
const IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
const FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
const MAX_IMAGE_SIZE_TO_CACHE = 10e6;
const LINE_FACTOR = 1.35;
const LINE_DESCENT_FACTOR = 0.35;
const BASELINE_FACTOR = LINE_DESCENT_FACTOR / LINE_FACTOR;
const RenderingIntentFlag = {
ANY: 0x01,
DISPLAY: 0x02,
PRINT: 0x04,
SAVE: 0x08,
ANNOTATIONS_FORMS: 0x10,
ANNOTATIONS_STORAGE: 0x20,
ANNOTATIONS_DISABLE: 0x40,
IS_EDITING: 0x80,
OPLIST: 0x100
};
const AnnotationMode = {
DISABLE: 0,
ENABLE: 1,
ENABLE_FORMS: 2,
ENABLE_STORAGE: 3
};
const AnnotationEditorPrefix = "pdfjs_internal_editor_";
const AnnotationEditorType = {
DISABLE: -1,
NONE: 0,
FREETEXT: 3,
HIGHLIGHT: 9,
STAMP: 13,
INK: 15
};
const AnnotationEditorParamsType = {
RESIZE: 1,
CREATE: 2,
FREETEXT_SIZE: 11,
FREETEXT_COLOR: 12,
FREETEXT_OPACITY: 13,
INK_COLOR: 21,
INK_THICKNESS: 22,
INK_OPACITY: 23,
HIGHLIGHT_COLOR: 31,
HIGHLIGHT_DEFAULT_COLOR: 32,
HIGHLIGHT_THICKNESS: 33,
HIGHLIGHT_FREE: 34,
HIGHLIGHT_SHOW_ALL: 35,
DRAW_STEP: 41
};
const PermissionFlag = {
PRINT: 0x04,
MODIFY_CONTENTS: 0x08,
COPY: 0x10,
MODIFY_ANNOTATIONS: 0x20,
FILL_INTERACTIVE_FORMS: 0x100,
COPY_FOR_ACCESSIBILITY: 0x200,
ASSEMBLE: 0x400,
PRINT_HIGH_QUALITY: 0x800
};
const TextRenderingMode = {
FILL: 0,
STROKE: 1,
FILL_STROKE: 2,
INVISIBLE: 3,
FILL_ADD_TO_PATH: 4,
STROKE_ADD_TO_PATH: 5,
FILL_STROKE_ADD_TO_PATH: 6,
ADD_TO_PATH: 7,
FILL_STROKE_MASK: 3,
ADD_TO_PATH_FLAG: 4
};
const ImageKind = {
GRAYSCALE_1BPP: 1,
RGB_24BPP: 2,
RGBA_32BPP: 3
};
const AnnotationType = {
TEXT: 1,
LINK: 2,
FREETEXT: 3,
LINE: 4,
SQUARE: 5,
CIRCLE: 6,
POLYGON: 7,
POLYLINE: 8,
HIGHLIGHT: 9,
UNDERLINE: 10,
SQUIGGLY: 11,
STRIKEOUT: 12,
STAMP: 13,
CARET: 14,
INK: 15,
POPUP: 16,
FILEATTACHMENT: 17,
SOUND: 18,
MOVIE: 19,
WIDGET: 20,
SCREEN: 21,
PRINTERMARK: 22,
TRAPNET: 23,
WATERMARK: 24,
THREED: 25,
REDACT: 26
};
const AnnotationReplyType = {
GROUP: "Group",
REPLY: "R"
};
const AnnotationFlag = {
INVISIBLE: 0x01,
HIDDEN: 0x02,
PRINT: 0x04,
NOZOOM: 0x08,
NOROTATE: 0x10,
NOVIEW: 0x20,
READONLY: 0x40,
LOCKED: 0x80,
TOGGLENOVIEW: 0x100,
LOCKEDCONTENTS: 0x200
};
const AnnotationFieldFlag = {
READONLY: 0x0000001,
REQUIRED: 0x0000002,
NOEXPORT: 0x0000004,
MULTILINE: 0x0001000,
PASSWORD: 0x0002000,
NOTOGGLETOOFF: 0x0004000,
RADIO: 0x0008000,
PUSHBUTTON: 0x0010000,
COMBO: 0x0020000,
EDIT: 0x0040000,
SORT: 0x0080000,
FILESELECT: 0x0100000,
MULTISELECT: 0x0200000,
DONOTSPELLCHECK: 0x0400000,
DONOTSCROLL: 0x0800000,
COMB: 0x1000000,
RICHTEXT: 0x2000000,
RADIOSINUNISON: 0x2000000,
COMMITONSELCHANGE: 0x4000000
};
const AnnotationBorderStyleType = {
SOLID: 1,
DASHED: 2,
BEVELED: 3,
INSET: 4,
UNDERLINE: 5
};
const AnnotationActionEventType = {
E: "Mouse Enter",
X: "Mouse Exit",
D: "Mouse Down",
U: "Mouse Up",
Fo: "Focus",
Bl: "Blur",
PO: "PageOpen",
PC: "PageClose",
PV: "PageVisible",
PI: "PageInvisible",
K: "Keystroke",
F: "Format",
V: "Validate",
C: "Calculate"
};
const DocumentActionEventType = {
WC: "WillClose",
WS: "WillSave",
DS: "DidSave",
WP: "WillPrint",
DP: "DidPrint"
};
const PageActionEventType = {
O: "PageOpen",
C: "PageClose"
};
const VerbosityLevel = {
ERRORS: 0,
WARNINGS: 1,
INFOS: 5
};
const OPS = {
dependency: 1,
setLineWidth: 2,
setLineCap: 3,
setLineJoin: 4,
setMiterLimit: 5,
setDash: 6,
setRenderingIntent: 7,
setFlatness: 8,
setGState: 9,
save: 10,
restore: 11,
transform: 12,
moveTo: 13,
lineTo: 14,
curveTo: 15,
curveTo2: 16,
curveTo3: 17,
closePath: 18,
rectangle: 19,
stroke: 20,
closeStroke: 21,
fill: 22,
eoFill: 23,
fillStroke: 24,
eoFillStroke: 25,
closeFillStroke: 26,
closeEOFillStroke: 27,
endPath: 28,
clip: 29,
eoClip: 30,
beginText: 31,
endText: 32,
setCharSpacing: 33,
setWordSpacing: 34,
setHScale: 35,
setLeading: 36,
setFont: 37,
setTextRenderingMode: 38,
setTextRise: 39,
moveText: 40,
setLeadingMoveText: 41,
setTextMatrix: 42,
nextLine: 43,
showText: 44,
showSpacedText: 45,
nextLineShowText: 46,
nextLineSetSpacingShowText: 47,
setCharWidth: 48,
setCharWidthAndBounds: 49,
setStrokeColorSpace: 50,
setFillColorSpace: 51,
setStrokeColor: 52,
setStrokeColorN: 53,
setFillColor: 54,
setFillColorN: 55,
setStrokeGray: 56,
setFillGray: 57,
setStrokeRGBColor: 58,
setFillRGBColor: 59,
setStrokeCMYKColor: 60,
setFillCMYKColor: 61,
shadingFill: 62,
beginInlineImage: 63,
beginImageData: 64,
endInlineImage: 65,
paintXObject: 66,
markPoint: 67,
markPointProps: 68,
beginMarkedContent: 69,
beginMarkedContentProps: 70,
endMarkedContent: 71,
beginCompat: 72,
endCompat: 73,
paintFormXObjectBegin: 74,
paintFormXObjectEnd: 75,
beginGroup: 76,
endGroup: 77,
beginAnnotation: 80,
endAnnotation: 81,
paintImageMaskXObject: 83,
paintImageMaskXObjectGroup: 84,
paintImageXObject: 85,
paintInlineImageXObject: 86,
paintInlineImageXObjectGroup: 87,
paintImageXObjectRepeat: 88,
paintImageMaskXObjectRepeat: 89,
paintSolidColorImageMask: 90,
constructPath: 91,
setStrokeTransparent: 92,
setFillTransparent: 93
};
const PasswordResponses = {
NEED_PASSWORD: 1,
INCORRECT_PASSWORD: 2
};
let verbosity = VerbosityLevel.WARNINGS;
function setVerbosityLevel(level) {
if (Number.isInteger(level)) {
verbosity = level;
}
}
function getVerbosityLevel() {
return verbosity;
}
function info(msg) {
if (verbosity >= VerbosityLevel.INFOS) {
if (typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope) {
console.log(`Info: ${msg}`);
} else if (Window && _external_ngx_logger_ngx_console_js__WEBPACK_IMPORTED_MODULE_0__.NgxConsole) {
_external_ngx_logger_ngx_console_js__WEBPACK_IMPORTED_MODULE_0__.NgxConsole.log(`Info: ${msg}`);
} else {
console.log(`Info: ${msg}`);
}
}
}
function warn(msg) {
if (verbosity >= VerbosityLevel.WARNINGS) {
if (typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope) {
_external_ngx_logger_ngx_console_js__WEBPACK_IMPORTED_MODULE_0__.NgxConsole.log(`Warning: ${msg}`);
} else if (Window && _external_ngx_logger_ngx_console_js__WEBPACK_IMPORTED_MODULE_0__.NgxConsole) {
_external_ngx_logger_ngx_console_js__WEBPACK_IMPORTED_MODULE_0__.NgxConsole.log(`Warning: ${msg}`);
} else {
console.log(`Warning: ${msg}`);
}
}
}
function unreachable(msg) {
throw new Error(msg);
}
function assert(cond, msg) {
if (!cond) {
unreachable(msg);
}
}
function _isValidProtocol(url) {
switch (url?.protocol) {
case "http:":
case "https:":
case "ftp:":
case "mailto:":
case "tel:":
case "capacitor":
return true;
default:
return false;
}
}
function createValidAbsoluteUrl(url, baseUrl = null, options = null) {
if (!url) {
return null;
}
try {
if (options && typeof url === "string") {
if (options.addDefaultProtocol && url.startsWith("www.")) {
const dots = url.match(/\./g);
if (dots?.length >= 2) {
url = `http://${url}`;
}
}
if (options.tryConvertEncoding) {
try {
url = stringToUTF8String(url);
} catch {}
}
}
const absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url);
if (_isValidProtocol(absoluteUrl)) {
return absoluteUrl;
}
} catch {}
return null;
}
function shadow(obj, prop, value, nonSerializable = false) {
Object.defineProperty(obj, prop, {
value,
enumerable: !nonSerializable,
configurable: true,
writable: false
});
return value;
}
const BaseException = function BaseExceptionClosure() {
function BaseException(message, name) {
this.message = message;
this.name = name;
}
BaseException.prototype = new Error();
BaseException.constructor = BaseException;
return BaseException;
}();
class PasswordException extends BaseException {
constructor(msg, code) {
super(msg, "PasswordException");
this.code = code;
}
}
class UnknownErrorException extends BaseException {
constructor(msg, details) {
super(msg, "UnknownErrorException");
this.details = details;
}
}
class InvalidPDFException extends BaseException {
constructor(msg) {
super(msg, "InvalidPDFException");
}
}
class MissingPDFException extends BaseException {
constructor(msg) {
super(msg, "MissingPDFException");
}
}
class UnexpectedResponseException extends BaseException {
constructor(msg, status) {
super(msg, "UnexpectedResponseException");
this.status = status;
}
}
class FormatError extends BaseException {
constructor(msg) {
super(msg, "FormatError");
}
}
class AbortException extends BaseException {
constructor(msg) {
super(msg, "AbortException");
}
}
function bytesToString(bytes) {
if (typeof bytes !== "object" || bytes?.length === undefined) {
unreachable("Invalid argument for bytesToString");
}
const length = bytes.length;
const MAX_ARGUMENT_COUNT = 8192;
if (length < MAX_ARGUMENT_COUNT) {
return String.fromCharCode.apply(null, bytes);
}
const strBuf = [];
for (let i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
const chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
const chunk = bytes.subarray(i, chunkEnd);
strBuf.push(String.fromCharCode.apply(null, chunk));
}
return strBuf.join("");
}
function stringToBytes(str) {
if (typeof str !== "string") {
unreachable("Invalid argument for stringToBytes");
}
const length = str.length;
const bytes = new Uint8Array(length);
for (let i = 0; i < length; ++i) {
bytes[i] = str.charCodeAt(i) & 0xff;
}
return bytes;
}
function string32(value) {
return String.fromCharCode(value >> 24 & 0xff, value >> 16 & 0xff, value >> 8 & 0xff, value & 0xff);
}
function objectSize(obj) {
return Object.keys(obj).length;
}
function objectFromMap(map) {
const obj = Object.create(null);
for (const [key, value] of map) {
obj[key] = value;
}
return obj;
}
function isLittleEndian() {
const buffer8 = new Uint8Array(4);
buffer8[0] = 1;
const view32 = new Uint32Array(buffer8.buffer, 0, 1);
return view32[0] === 1;
}
function isEvalSupported() {
try {
new Function("");
return true;
} catch {
return false;
}
}
class FeatureTest {
static get isLittleEndian() {
return shadow(this, "isLittleEndian", isLittleEndian());
}
static get isEvalSupported() {
return shadow(this, "isEvalSupported", isEvalSupported());
}
static get isOffscreenCanvasSupported() {
return shadow(this, "isOffscreenCanvasSupported", typeof OffscreenCanvas !== "undefined");
}
static get isImageDecoderSupported() {
return shadow(this, "isImageDecoderSupported", typeof ImageDecoder !== "undefined");
}
static get platform() {
if (typeof navigator !== "undefined" && typeof navigator?.platform === "string") {
return shadow(this, "platform", {
isMac: navigator.platform.includes("Mac"),
isWindows: navigator.platform.includes("Win"),
isFirefox: typeof navigator?.userAgent === "string" && navigator.userAgent.includes("Firefox")
});
}
return shadow(this, "platform", {
isMac: false,
isWindows: false,
isFirefox: false
});
}
static get isCSSRoundSupported() {
return shadow(this, "isCSSRoundSupported", globalThis.CSS?.supports?.("width: round(1.5px, 1px)"));
}
}
const hexNumbers = Array.from(Array(256).keys(), n => n.toString(16).padStart(2, "0"));
class Util {
static makeHexColor(r, g, b) {
return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;
}
static scaleMinMax(transform, minMax) {
let temp;
if (transform[0]) {
if (transform[0] < 0) {
temp = minMax[0];
minMax[0] = minMax[2];
minMax[2] = temp;
}
minMax[0] *= transform[0];
minMax[2] *= transform[0];
if (transform[3] < 0) {
temp = minMax[1];
minMax[1] = minMax[3];
minMax[3] = temp;
}
minMax[1] *= transform[3];
minMax[3] *= transform[3];
} else {
temp = minMax[0];
minMax[0] = minMax[1];
minMax[1] = temp;
temp = minMax[2];
minMax[2] = minMax[3];
minMax[3] = temp;
if (transform[1] < 0) {
temp = minMax[1];
minMax[1] = minMax[3];
minMax[3] = temp;
}
minMax[1] *= transform[1];
minMax[3] *= transform[1];
if (transform[2] < 0) {
temp = minMax[0];
minMax[0] = minMax[2];
minMax[2] = temp;
}
minMax[0] *= transform[2];
minMax[2] *= transform[2];
}
minMax[0] += transform[4];
minMax[1] += transform[5];
minMax[2] += transform[4];
minMax[3] += transform[5];
}
static transform(m1, m2) {
return [m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5]];
}
static applyTransform(p, m) {
const xt = p[0] * m[0] + p[1] * m[2] + m[4];
const yt = p[0] * m[1] + p[1] * m[3] + m[5];
return [xt, yt];
}
static applyInverseTransform(p, m) {
const d = m[0] * m[3] - m[1] * m[2];
const xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
const yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
return [xt, yt];
}
static getAxialAlignedBoundingBox(r, m) {
const p1 = this.applyTransform(r, m);
const p2 = this.applyTransform(r.slice(2, 4), m);
const p3 = this.applyTransform([r[0], r[3]], m);
const p4 = this.applyTransform([r[2], r[1]], m);
return [Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1])];
}
static inverseTransform(m) {
const d = m[0] * m[3] - m[1] * m[2];
return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
}
static singularValueDecompose2dScale(m) {
const transpose = [m[0], m[2], m[1], m[3]];
const a = m[0] * transpose[0] + m[1] * transpose[2];
const b = m[0] * transpose[1] + m[1] * transpose[3];
const c = m[2] * transpose[0] + m[3] * transpose[2];
const d = m[2] * transpose[1] + m[3] * transpose[3];
const first = (a + d) / 2;
const second = Math.sqrt((a + d) ** 2 - 4 * (a * d - c * b)) / 2;
const sx = first + second || 1;
const sy = first - second || 1;
return [Math.sqrt(sx), Math.sqrt(sy)];
}
static normalizeRect(rect) {
const r = rect.slice(0);
if (rect[0] > rect[2]) {
r[0] = rect[2];
r[2] = rect[0];
}
if (rect[1] > rect[3]) {
r[1] = rect[3];
r[3] = rect[1];
}
return r;
}
static intersect(rect1, rect2) {
const xLow = Math.max(Math.min(rect1[0], rect1[2]), Math.min(rect2[0], rect2[2]));
const xHigh = Math.min(Math.max(rect1[0], rect1[2]), Math.max(rect2[0], rect2[2]));
if (xLow > xHigh) {
return null;
}
const yLow = Math.max(Math.min(rect1[1], rect1[3]), Math.min(rect2[1], rect2[3]));
const yHigh = Math.min(Math.max(rect1[1], rect1[3]), Math.max(rect2[1], rect2[3]));
if (yLow > yHigh) {
return null;
}
return [xLow, yLow, xHigh, yHigh];
}
static #getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, t, minMax) {
if (t <= 0 || t >= 1) {
return;
}
const mt = 1 - t;
const tt = t * t;
const ttt = tt * t;
const x = mt * (mt * (mt * x0 + 3 * t * x1) + 3 * tt * x2) + ttt * x3;
const y = mt * (mt * (mt * y0 + 3 * t * y1) + 3 * tt * y2) + ttt * y3;
minMax[0] = Math.min(minMax[0], x);
minMax[1] = Math.min(minMax[1], y);
minMax[2] = Math.max(minMax[2], x);
minMax[3] = Math.max(minMax[3], y);
}
static #getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, a, b, c, minMax) {
if (Math.abs(a) < 1e-12) {
if (Math.abs(b) >= 1e-12) {
this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, -c / b, minMax);
}
return;
}
const delta = b ** 2 - 4 * c * a;
if (delta < 0) {
return;
}
const sqrtDelta = Math.sqrt(delta);
const a2 = 2 * a;
this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b + sqrtDelta) / a2, minMax);
this.#getExtremumOnCurve(x0, x1, x2, x3, y0, y1, y2, y3, (-b - sqrtDelta) / a2, minMax);
}
static bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
if (minMax) {
minMax[0] = Math.min(minMax[0], x0, x3);
minMax[1] = Math.min(minMax[1], y0, y3);
minMax[2] = Math.max(minMax[2], x0, x3);
minMax[3] = Math.max(minMax[3], y0, y3);
} else {
minMax = [Math.min(x0, x3), Math.min(y0, y3), Math.max(x0, x3), Math.max(y0, y3)];
}
this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-x0 + 3 * (x1 - x2) + x3), 6 * (x0 - 2 * x1 + x2), 3 * (x1 - x0), minMax);
this.#getExtremum(x0, x1, x2, x3, y0, y1, y2, y3, 3 * (-y0 + 3 * (y1 - y2) + y3), 6 * (y0 - 2 * y1 + y2), 3 * (y1 - y0), minMax);
return minMax;
}
}
const PDFStringTranslateTable = (/* unused pure expression or super */ null && ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2d8, 0x2c7, 0x2c6, 0x2d9, 0x2dd, 0x2db, 0x2da, 0x2dc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018, 0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x141, 0x152, 0x160, 0x178, 0x17d, 0x131, 0x142, 0x153, 0x161, 0x17e, 0, 0x20ac]));
function stringToPDFString(str) {
if (str[0] >= "\xEF") {
let encoding;
if (str[0] === "\xFE" && str[1] === "\xFF") {
encoding = "utf-16be";
if (str.length % 2 === 1) {
str = str.slice(0, -1);
}
} else if (str[0] === "\xFF" && str[1] === "\xFE") {
encoding = "utf-16le";
if (str.length % 2 === 1) {
str = str.slice(0, -1);
}
} else if (str[0] === "\xEF" && str[1] === "\xBB" && str[2] === "\xBF") {
encoding = "utf-8";
}
if (encoding) {
try {
const decoder = new TextDecoder(encoding, {
fatal: true
});
const buffer = stringToBytes(str);
const decoded = decoder.decode(buffer);
if (!decoded.includes("\x1b")) {
return decoded;
}
return decoded.replaceAll(/\x1b[^\x1b]*(?:\x1b|$)/g, "");
} catch (ex) {
warn(`stringToPDFString: "${ex}".`);
}
}
}
const strBuf = [];
for (let i = 0, ii = str.length; i < ii; i++) {
const charCode = str.charCodeAt(i);
if (charCode === 0x1b) {
while (++i < ii && str.charCodeAt(i) !== 0x1b) {}
continue;
}
const code = PDFStringTranslateTable[charCode];
strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
}
return strBuf.join("");
}
function stringToUTF8String(str) {
return decodeURIComponent(escape(str));
}
function utf8StringToString(str) {
return unescape(encodeURIComponent(str));
}
function isArrayEqual(arr1, arr2) {
if (arr1.length !== arr2.length) {
return false;
}
for (let i = 0, ii = arr1.length; i < ii; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
}
function getModificationDate(date = new Date()) {
const buffer = [date.getUTCFullYear().toString(), (date.getUTCMonth() + 1).toString().padStart(2, "0"), date.getUTCDate().toString().padStart(2, "0"), date.getUTCHours().toString().padStart(2, "0"), date.getUTCMinutes().toString().padStart(2, "0"), date.getUTCSeconds().toString().padStart(2, "0")];
return buffer.join("");
}
let NormalizeRegex = null;
let NormalizationMap = null;
function normalizeUnicode(str) {
if (!NormalizeRegex) {
NormalizeRegex = /([\u00a0\u00b5\u037e\u0eb3\u2000-\u200a\u202f\u2126\ufb00-\ufb04\ufb06\ufb20-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufba1\ufba4-\ufba9\ufbae-\ufbb1\ufbd3-\ufbdc\ufbde-\ufbe7\ufbea-\ufbf8\ufbfc-\ufbfd\ufc00-\ufc5d\ufc64-\ufcf1\ufcf5-\ufd3d\ufd88\ufdf4\ufdfa-\ufdfb\ufe71\ufe77\ufe79\ufe7b\ufe7d]+)|(\ufb05+)/gu;
NormalizationMap = new Map([["ſt", "ſt"]]);
}
return str.replaceAll(NormalizeRegex, (_, p1, p2) => p1 ? p1.normalize("NFKC") : NormalizationMap.get(p2));
}
function getUuid() {
if (typeof crypto.randomUUID === "function") {
return crypto.randomUUID();
}
const buf = new Uint8Array(32);
crypto.getRandomValues(buf);
return bytesToString(buf);
}
const AnnotationPrefix = "pdfjs_internal_id_";
function toHexUtil(arr) {
if (Uint8Array.prototype.toHex) {
return arr.toHex();
}
return Array.from(arr, num => hexNumbers[num]).join("");
}
function toBase64Util(arr) {
if (Uint8Array.prototype.toBase64) {
return arr.toBase64();
}
return btoa(bytesToString(arr));
}
function fromBase64Util(str) {
if (Uint8Array.fromBase64) {
return Uint8Array.fromBase64(str);
}
return stringToBytes(atob(str));
}
if (typeof Promise.try !== "function") {
Promise.try = function (fn, ...args) {
return new Promise(resolve => {
resolve(fn(...args));
});
};
}
/***/ }),
/***/ 276:
/***/ ((__unused_webpack___webpack_module__, __unused_webpack___webpack_exports__, __webpack_require__) => {
// UNUSED EXPORTS: PDFViewerApplication, PDFViewerApplicationConstants, PDFViewerApplicationOptions, webViewerLoad
// EXTERNAL MODULE: ./external/ngx-logger/ngx-console.js
var ngx_console = __webpack_require__(641);
;// ./web/ui_utils.js
const DEFAULT_SCALE_VALUE = "auto";
const DEFAULT_SCALE = 1.0;
const DEFAULT_SCALE_DELTA = 1.1;
const MIN_SCALE = 0.1;
const MAX_SCALE = 10.0;
const UNKNOWN_SCALE = 0;
const MAX_AUTO_SCALE = 1.25;
const SCROLLBAR_PADDING = 40;
const VERTICAL_PADDING = 5;
const RenderingStates = {
INITIAL: 0,
RUNNING: 1,
PAUSED: 2,
FINISHED: 3
};
const PresentationModeState = {
UNKNOWN: 0,
NORMAL: 1,
CHANGING: 2,
FULLSCREEN: 3
};
const SidebarView = {
UNKNOWN: -1,
NONE: 0,
THUMBS: 1,
OUTLINE: 2,
ATTACHMENTS: 3,
LAYERS: 4
};
const TextLayerMode = {
DISABLE: 0,
ENABLE: 1,
ENABLE_PERMISSIONS: 2
};
const ScrollMode = {
UNKNOWN: -1,
VERTICAL: 0,
HORIZONTAL: 1,
WRAPPED: 2,
PAGE: 3
};
const SpreadMode = {
UNKNOWN: -1,
NONE: 0,
ODD: 1,
EVEN: 2
};
const CursorTool = {
SELECT: 0,
HAND: 1,
ZOOM: 2
};
const AutoPrintRegExp = /\bprint\s*\(/;
function scrollIntoView(element, spot, scrollMatches = false, infiniteScroll = false) {
if (element.classList.contains("stf__item") || element.parentElement?.classList.contains("stf__item") || element.parentElement?.parentElement?.classList.contains("stf__item")) {
return;
}
let parent = element.offsetParent;
if (!parent) {
ngx_console.NgxConsole.error("offsetParent is not set -- cannot scroll");
return;
}
let offsetY = element.offsetTop + element.clientTop;
let offsetX = element.offsetLeft + element.clientLeft;
while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) {
offsetY += parent.offsetTop;
offsetX += parent.offsetLeft;
parent = parent.offsetParent;
if (!parent) {
if (infiniteScroll) {
if (document.body.clientHeight > offsetY) {
offsetY -= 32;
window.scrollTo(window.scrollX, offsetY);
}
}
return;
}
}
if (spot) {
if (spot.top !== undefined) {
offsetY += spot.top;
}
if (spot.left !== undefined) {
offsetX += spot.left;
parent.scrollLeft = offsetX;
}
} else if (isDivInViewport(element)) {
return;
}
parent.scrollTop = offsetY;
}
function isDivInViewport(element) {
const rect = element.getBoundingClientRect();
return rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
}
function watchScroll(viewAreaElement, callback, abortSignal = undefined) {
const debounceScroll = function (evt) {
if (rAF) {
return;
}
rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
rAF = null;
const currentX = viewAreaElement.scrollLeft;
const lastX = state.lastX;
if (currentX !== lastX) {
state.right = currentX > lastX;
}
state.lastX = currentX;
const currentY = viewAreaElement.scrollTop;
const lastY = state.lastY;
if (currentY !== lastY) {
state.down = currentY > lastY;
}
state.lastY = currentY;
callback(state);
});
};
const state = {
right: true,
down: true,
lastX: viewAreaElement.scrollLeft,
lastY: viewAreaElement.scrollTop,
_eventHandler: debounceScroll
};
let rAF = null;
viewAreaElement.addEventListener("scroll", debounceScroll, {
useCapture: true,
signal: abortSignal
});
abortSignal?.addEventListener("abort", () => window.cancelAnimationFrame(rAF), {
once: true
});
return state;
}
function parseQueryString(query) {
const params = new Map();
for (const [key, value] of new URLSearchParams(query)) {
params.set(key.toLowerCase(), value);
}
return params;
}
const InvisibleCharsRegExp = /[\x00-\x1F]/g;
function removeNullCharacters(str, replaceInvisible = false) {
if (!InvisibleCharsRegExp.test(str)) {
return str;
}
if (replaceInvisible) {
return str.replaceAll(InvisibleCharsRegExp, m => m === "\x00" ? "" : " ");
}
return str.replaceAll("\x00", "");
}
function binarySearchFirstItem(items, condition, start = 0) {
let minIndex = start;
let maxIndex = items.length - 1;
if (maxIndex < 0 || !condition(items[maxIndex])) {
return items.length;
}
if (condition(items[minIndex])) {
return minIndex;
}
while (minIndex < maxIndex) {
const currentIndex = minIndex + maxIndex >> 1;
const currentItem = items[currentIndex];
if (condition(currentItem)) {
maxIndex = currentIndex;
} else {
minIndex = currentIndex + 1;
}
}
return minIndex;
}
function approximateFraction(x) {
if (Math.floor(x) === x) {
return [x, 1];
}
const xinv = 1 / x;
const limit = 8;
if (xinv > limit) {
return [1, limit];
} else if (Math.floor(xinv) === xinv) {
return [1, xinv];
}
const x_ = x > 1 ? xinv : x;
let a = 0,
b = 1,
c = 1,
d = 1;
while (true) {
const p = a + c,
q = b + d;
if (q > limit) {
break;
}
if (x_ <= p / q) {
c = p;
d = q;
} else {
a = p;
b = q;
}
}
let result;
if (x_ - a / b < c / d - x_) {
result = x_ === x ? [a, b] : [b, a];
} else {
result = x_ === x ? [c, d] : [d, c];
}
return result;
}
function floorToDivide(x, div) {
return x - x % div;
}
function getPageSizeInches({
view,
userUnit,
rotate
}) {
const [x1, y1, x2, y2] = view;
const changeOrientation = rotate % 180 !== 0;
const width = (x2 - x1) / 72 * userUnit;
const height = (y2 - y1) / 72 * userUnit;
return {
width: changeOrientation ? height : width,
height: changeOrientation ? width : height
};
}
function backtrackBeforeAllVisibleElements(index, views, top) {
if (index < 2) {
return index;
}
let elt = views[index].div;
let pageTop = elt.offsetTop + elt.clientTop;
if (pageTop >= top) {
elt = views[index - 1].div;
pageTop = elt.offsetTop + elt.clientTop;
}
for (let i = index - 2; i >= 0; --i) {
elt = views[i].div;
if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) {
break;
}
index = i;
}
return index;
}
function getVisibleElements({
scrollEl,
views,
sortByVisibility = false,
horizontal = false,
rtl = false
}) {
const top = scrollEl.scrollTop,
bottom = top + scrollEl.clientHeight;
const left = scrollEl.scrollLeft,
right = left + scrollEl.clientWidth;
function isElementBottomAfterViewTop(view) {
const element = view.div;
const elementBottom = element.offsetTop + element.clientTop + element.clientHeight;
return elementBottom > top;
}
function isElementNextAfterViewHorizontally(view) {
const element = view.div;
const elementLeft = element.offsetLeft + element.clientLeft;
const elementRight = elementLeft + element.clientWidth;
return rtl ? elementLeft < right : elementRight > left;
}
const visible = [],
ids = new Set(),
numViews = views.length;
let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop);
if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) {
firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top);
}
let lastEdge = horizontal ? right : -1;
for (let i = firstVisibleElementInd; i < numViews; i++) {
const view = views[i],
element = view.div;
const currentWidth = element.offsetLeft + element.clientLeft;
const currentHeight = element.offsetTop + element.clientTop;
const viewWidth = element.clientWidth,
viewHeight = element.clientHeight;
const viewRight = currentWidth + viewWidth;
const viewBottom = currentHeight + viewHeight;
if (lastEdge === -1) {
if (viewBottom >= bottom) {
lastEdge = viewBottom;
}
} else if ((horizontal ? currentWidth : currentHeight) > lastEdge) {
break;
}
if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) {
continue;
}
const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
const fractionHeight = (viewHeight - hiddenHeight) / viewHeight,
fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
const percent = fractionHeight * fractionWidth * 100 | 0;
visible.push({
id: view.id,
x: currentWidth,
y: currentHeight,
view,
percent,
widthPercent: fractionWidth * 100 | 0
});
ids.add(view.id);
}
const first = visible[0],
last = visible.at(-1);
if (sortByVisibility) {
visible.sort(function (a, b) {
const pc = a.percent - b.percent;
if (Math.abs(pc) > 0.001) {
return -pc;
}
return a.id - b.id;
});
}
return {
first,
last,
views: visible,
ids
};
}
function normalizeWheelEventDirection(evt) {
let delta = Math.hypot(evt.deltaX, evt.deltaY);
const angle = Math.atan2(evt.deltaY, evt.deltaX);
if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
delta = -delta;
}
return delta;
}
function normalizeWheelEventDelta(evt) {
const deltaMode = evt.deltaMode;
let delta = normalizeWheelEventDirection(evt);
const MOUSE_PIXELS_PER_LINE = 30;
const MOUSE_LINES_PER_PAGE = 30;
if (deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
} else if (deltaMode === WheelEvent.DOM_DELTA_LINE) {
delta /= MOUSE_LINES_PER_PAGE;
}
return delta;
}
function isValidRotation(angle) {
return Number.isInteger(angle) && angle % 90 === 0;
}
function isValidScrollMode(mode) {
return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN;
}
function isValidSpreadMode(mode) {
return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN;
}
function isPortraitOrientation(size) {
return size.width <= size.height;
}
const animationStarted = new Promise(function (resolve) {
window.requestAnimationFrame(resolve);
});
const docStyle = document.documentElement.style;
function clamp(v, min, max) {
return Math.min(Math.max(v, min), max);
}
class ProgressBar {
#classList = null;
#disableAutoFetchTimeout = null;
#percent = 0;
#style = null;
#visible = true;
constructor(bar) {
this.#classList = bar.classList;
this.#style = bar.style;
}
get percent() {
return this.#percent;
}
set percent(val) {
this.#percent = clamp(val, 0, 100);
if (isNaN(val)) {
this.#classList.add("indeterminate");
return;
}
this.#classList.remove("indeterminate");
this.#style.setProperty("--progressBar-percent", `${this.#percent}%`);
}
setWidth(viewer) {
if (!viewer) {
return;
}
const container = viewer.parentNode;
const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
if (scrollbarWidth > 0) {
this.#style.setProperty("--progressBar-end-offset", `${scrollbarWidth}px`);
}
}
setDisableAutoFetch(delay = 5000) {
if (this.#percent === 100 || isNaN(this.#percent)) {
return;
}
if (this.#disableAutoFetchTimeout) {
clearTimeout(this.#disableAutoFetchTimeout);
}
this.show();
this.#disableAutoFetchTimeout = setTimeout(() => {
this.#disableAutoFetchTimeout = null;
this.hide();
}, delay);
}
hide() {
if (!this.#visible) {
return;
}
this.#visible = false;
this.#classList.add("hidden");
}
show() {
if (this.#visible) {
return;
}
this.#visible = true;
this.#classList.remove("hidden");
}
}
function getActiveOrFocusedElement() {
let curRoot = document;
let curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
while (curActiveOrFocused?.shadowRoot) {
curRoot = curActiveOrFocused.shadowRoot;
curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
}
return curActiveOrFocused;
}
function apiPageLayoutToViewerModes(layout) {
let scrollMode = ScrollMode.VERTICAL,
spreadMode = SpreadMode.NONE;
switch (layout) {
case "SinglePage":
scrollMode = ScrollMode.PAGE;
break;
case "OneColumn":
break;
case "TwoPageLeft":
scrollMode = ScrollMode.PAGE;
case "TwoColumnLeft":
spreadMode = SpreadMode.ODD;
break;
case "TwoPageRight":
scrollMode = ScrollMode.PAGE;
case "TwoColumnRight":
spreadMode = SpreadMode.EVEN;
break;
}
return {
scrollMode,
spreadMode
};
}
function apiPageModeToSidebarView(mode) {
switch (mode) {
case "UseNone":
return SidebarView.NONE;
case "UseThumbs":
return SidebarView.THUMBS;
case "UseOutlines":
return SidebarView.OUTLINE;
case "UseAttachments":
return SidebarView.ATTACHMENTS;
case "UseOC":
return SidebarView.LAYERS;
}
return SidebarView.NONE;
}
function toggleCheckedBtn(button, toggle, view = null) {
button.classList.toggle("toggled", toggle);
button.setAttribute("aria-checked", toggle);
view?.classList.toggle("hidden", !toggle);
}
function toggleExpandedBtn(button, toggle, view = null) {
button.classList.toggle("toggled", toggle);
button.setAttribute("aria-expanded", toggle);
view?.classList.toggle("hidden", !toggle);
if (view && !view.classList.contains("hidden")) {
const rect = button.getBoundingClientRect();
const container = button.closest("#toolbarViewer");
if (container) {
const containerRect = container.getBoundingClientRect();
const distanceFromRight = containerRect.right - rect.right;
view.style.removeProperty("inset-inline-end");
view.style.setProperty("inset-inline-end", distanceFromRight + "px");
}
}
}
const calcRound = function () {
const e = document.createElement("div");
e.style.width = "round(down, calc(1.6666666666666665 * 792px), 1px)";
return e.style.width === "calc(1320px)" ? Math.fround : x => x;
}();
function isInsideNgxExtendedPdfViewer(element) {
let currentElement = element;
while (currentElement) {
if (currentElement.tagName.toLowerCase() === 'ngx-extended-pdf-viewer') {
return true;
}
currentElement = currentElement.parentElement;
}
return false;
}
;// ./web/app_options.js
{
var compatParams = new Map();
const userAgent = navigator.userAgent || "";
const platform = navigator.platform || "";
const maxTouchPoints = navigator.maxTouchPoints || 1;
const isAndroid = /Android/.test(userAgent);
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1;
(function () {
if (isIOS || isAndroid) {
compatParams.set("maxCanvasPixels", 5242880);
}
})();
(function () {
if (isAndroid) {
compatParams.set("useSystemFonts", false);
}
})();
}
const OptionKind = {
BROWSER: 0x01,
VIEWER: 0x02,
API: 0x04,
WORKER: 0x08,
EVENT_DISPATCH: 0x10,
PREFERENCE: 0x80
};
const Type = {
BOOLEAN: 0x01,
NUMBER: 0x02,
OBJECT: 0x04,
STRING: 0x08,
UNDEFINED: 0x10
};
const defaultOptions = {
defaultCacheSize: {
value: 50,
kind: OptionKind.VIEWER
},
passwordPrompt: {
value: null,
kind: OptionKind.VIEWER
},
enableDragAndDrop: {
value: true,
kind: OptionKind.VIEWER
},
pageViewMode: {
value: "multiple",
kind: OptionKind.VIEWER
},
findController: {
value: null,
kind: OptionKind.VIEWER
},
secondaryFindController: {
value: null,
kind: OptionKind.VIEWER
},
allowedGlobalEvents: {
value: null,
kind: OptionKind.BROWSER
},
canvasMaxAreaInBytes: {
value: -1,
kind: OptionKind.BROWSER + OptionKind.API
},
isInAutomation: {
value: false,
kind: OptionKind.BROWSER
},
localeProperties: {
value: {
lang: navigator.language || "en-US"
},
kind: OptionKind.BROWSER
},
nimbusDataStr: {
value: "",
kind: OptionKind.BROWSER
},
supportsCaretBrowsingMode: {
value: false,
kind: OptionKind.BROWSER
},
supportsDocumentFonts: {
value: true,
kind: OptionKind.BROWSER
},
supportsIntegratedFind: {
value: false,
kind: OptionKind.BROWSER
},
supportsMouseWheelZoomCtrlKey: {
value: true,
kind: OptionKind.BROWSER
},
supportsMouseWheelZoomMetaKey: {
value: true,
kind: OptionKind.BROWSER
},
supportsPinchToZoom: {
value: true,
kind: OptionKind.BROWSER
},
toolbarDensity: {
value: 0,
kind: OptionKind.BROWSER + OptionKind.EVENT_DISPATCH
},
altTextLearnMoreUrl: {
value: "",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
annotationEditorMode: {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
annotationMode: {
value: 2,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
cursorToolOnLoad: {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
debuggerSrc: {
value: "./debugger.mjs",
kind: OptionKind.VIEWER
},
defaultZoomDelay: {
value: 400,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
defaultZoomValue: {
value: "auto",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
disableHistory: {
value: false,
kind: OptionKind.VIEWER
},
disablePageLabels: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enableAltText: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enableAltTextModelDownload: {
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH
},
enableGuessAltText: {
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE + OptionKind.EVENT_DISPATCH
},
enableHighlightFloatingButton: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enableNewAltTextWhenAddingImage: {
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enablePermissions: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enablePrintAutoRotate: {
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enableScripting: {
value: true,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
enableUpdatedAddImage: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
externalLinkRel: {
value: "noopener noreferrer nofollow",
kind: OptionKind.VIEWER
},
externalLinkTarget: {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
highlightEditorColors: {
value: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
historyUpdateUrl: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
ignoreDestinationZoom: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
imageResourcesPath: {
value: "./images/",
kind: OptionKind.VIEWER
},
maxCanvasPixels: {
value: 2 ** 25,
kind: OptionKind.VIEWER
},
forcePageColors: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
pageColorsBackground: {
value: "Canvas",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
pageColorsForeground: {
value: "CanvasText",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
pdfBugEnabled: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
printResolution: {
value: 150,
kind: OptionKind.VIEWER
},
removePageBorders: {
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
sidebarViewOnLoad: {
value: -1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
scrollModeOnLoad: {
value: -1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
spreadModeOnLoad: {
value: -1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
textLayerMode: {
value: 1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
viewOnLoad: {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
},
cMapPacked: {
value: true,
kind: OptionKind.API
},
cMapUrl: {
value: "../web/cmaps/",
kind: OptionKind.API
},
disableAutoFetch: {
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE
},
disableFontFace: {
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE
},
disableRange: {
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE
},
disableStream: {
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE
},
docBaseUrl: {
value: "",
kind: OptionKind.API
},
enableHWA: {
value: true,
kind: OptionKind.API + OptionKind.VIEWER + OptionKind.PREFERENCE
},
enableXfa: {
value: true,
kind: OptionKind.API + OptionKind.PREFERENCE
},
fontExtraProperties: {
value: false,
kind: OptionKind.API
},
isEvalSupported: {
value: true,
kind: OptionKind.API
},
isOffscreenCanvasSupported: {
value: true,
kind: OptionKind.API
},
maxZoom: {
value: 10,
kind: OptionKind.VIEWER
},
minZoom: {
value: 0.1,
kind: OptionKind.VIEWER
},
maxImageSize: {
value: -1,
kind: OptionKind.API
},
pdfBug: {
value: false,
kind: OptionKind.API
},
standardFontDataUrl: {
value: "../web/standard_fonts/",
kind: OptionKind.API
},
useSystemFonts: {
value: undefined,
kind: OptionKind.API,
type: Type.BOOLEAN + Type.UNDEFINED
},
verbosity: {
value: 1,
kind: OptionKind.API
},
workerPort: {
value: null,
kind: OptionKind.WORKER
},
workerSrc: {
value: "./assets/pdf.worker.mjs",
kind: OptionKind.WORKER
}
};
{
defaultOptions.defaultUrl = {
value: "compressed.tracemonkey-pldi-09.pdf",
kind: OptionKind.VIEWER
};
defaultOptions.sandboxBundleSrc = {
value: "../build/pdf.sandbox.mjs",
kind: OptionKind.VIEWER
};
defaultOptions.viewerCssTheme = {
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE
};
defaultOptions.enableFakeMLManager = {
value: true,
kind: OptionKind.VIEWER
};
}
{
defaultOptions.disablePreferences = {
value: false,
kind: OptionKind.VIEWER
};
}
class AppOptions {
static eventBus;
static #opts = new Map();
static {
for (const name in defaultOptions) {
this.#opts.set(name, defaultOptions[name].value);
}
for (const [name, value] of compatParams) {
this.#opts.set(name, value);
}
this._hasInvokedSet = false;
this._checkDisablePreferences = () => {
if (this.get("disablePreferences")) {
return true;
}
if (this._hasInvokedSet) {
console.warn("The Preferences may override manually set AppOptions; " + 'please use the "disablePreferences"-option to prevent that.');
}
return false;
};
}
static get(name) {
return this.#opts.get(name);
}
static getAll(kind = null, defaultOnly = false) {
const options = Object.create(null);
for (const name in defaultOptions) {
const defaultOpt = defaultOptions[name];
if (kind && !(kind & defaultOpt.kind)) {
continue;
}
options[name] = !defaultOnly ? this.#opts.get(name) : defaultOpt.value;
}
return options;
}
static set(name, value) {
this.setAll({
[name]: value
});
}
static setAll(options, prefs = false) {
this._hasInvokedSet ||= true;
let events;
for (const name in options) {
const defaultOpt = defaultOptions[name];
let userOpt = options[name];
if (!defaultOpt || !(typeof userOpt === typeof defaultOpt.value || Type[(typeof userOpt).toUpperCase()] & defaultOpt.type)) {
if (typeof defaultOpt.value === "number" && typeof userOpt === "string") {
if (!Number.isNaN(Number(userOpt))) {
userOpt = Number(userOpt);
}
}
if (!(typeof userOpt === typeof defaultOpt.value || Type[(typeof userOpt).toUpperCase()] & defaultOpt.type)) {
if (!["maxZoom", "minZoom", "passwordPrompt", "defaultZoomValue", "findController"].includes(name)) {
console.error("Invalid AppOptions value: " + name + " = " + userOpt);
continue;
}
}
}
const {
kind
} = defaultOpt;
if (prefs && !(kind & OptionKind.BROWSER || kind & OptionKind.PREFERENCE)) {
console.error("Invalid AppOptions parameter: " + name + " = " + userOpt);
continue;
}
if (this.eventBus && kind & OptionKind.EVENT_DISPATCH) {
(events ||= new Map()).set(name, userOpt);
}
this.#opts.set(name, userOpt);
}
if (events) {
for (const [name, value] of events) {
this.eventBus.dispatch(name.toLowerCase(), {
source: this,
value
});
}
}
}
}
;// ./web/pdf_link_service.js
const DEFAULT_LINK_REL = "noopener noreferrer nofollow";
const LinkTarget = {
NONE: 0,
SELF: 1,
BLANK: 2,
PARENT: 3,
TOP: 4
};
class PDFLinkService {
externalLinkEnabled = true;
constructor({
eventBus,
externalLinkTarget = null,
externalLinkRel = null,
ignoreDestinationZoom = false
} = {}) {
this.eventBus = eventBus;
this.externalLinkTarget = externalLinkTarget;
this.externalLinkRel = externalLinkRel;
this._ignoreDestinationZoom = ignoreDestinationZoom;
this.baseUrl = null;
this.pdfDocument = null;
this.pdfViewer = null;
this.pdfHistory = null;
}
setDocument(pdfDocument, baseUrl = null) {
this.baseUrl = baseUrl;
this.pdfDocument = pdfDocument;
}
setViewer(pdfViewer) {
this.pdfViewer = pdfViewer;
}
setHistory(pdfHistory) {
this.pdfHistory = pdfHistory;
}
get pagesCount() {
return this.pdfDocument ? this.pdfDocument.numPages : 0;
}
get page() {
return this.pdfDocument ? this.pdfViewer.currentPageNumber : 1;
}
set page(value) {
if (this.pdfDocument) {
this.pdfViewer.currentPageNumber = value;
}
}
get rotation() {
return this.pdfDocument ? this.pdfViewer.pagesRotation : 0;
}
set rotation(value) {
if (this.pdfDocument) {
this.pdfViewer.pagesRotation = value;
}
}
get isInPresentationMode() {
return this.pdfDocument ? this.pdfViewer.isInPresentationMode : false;
}
async goToDestination(dest) {
if (!this.pdfDocument) {
return;
}
let namedDest, explicitDest, pageNumber;
if (typeof dest === "string") {
namedDest = dest;
explicitDest = await this.pdfDocument.getDestination(dest);
} else {
namedDest = null;
explicitDest = await dest;
}
if (!Array.isArray(explicitDest)) {
console.error(`goToDestination: "${explicitDest}" is not a valid destination array, for dest="${dest}".`);
return;
}
const [destRef] = explicitDest;
if (destRef && typeof destRef === "object") {
pageNumber = this.pdfDocument.cachedPageNumber(destRef);
if (!pageNumber) {
try {
pageNumber = (await this.pdfDocument.getPageIndex(destRef)) + 1;
} catch {
ngx_console.NgxConsole.error(`goToDestination: "${destRef}" is not a valid page reference, for dest="${dest}".`);
return;
}
}
} else if (Number.isInteger(destRef)) {
pageNumber = destRef + 1;
}
if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
ngx_console.NgxConsole.error(`goToDestination: "${pageNumber}" is not a valid page number, for dest="${dest}".`);
return;
}
if (this.pdfHistory) {
this.pdfHistory.pushCurrentPosition();
this.pdfHistory.push({
namedDest,
explicitDest,
pageNumber
});
}
this.pdfViewer.scrollPageIntoView({
pageNumber,
destArray: explicitDest,
ignoreDestinationZoom: this._ignoreDestinationZoom
});
}
goToPage(val) {
if (!this.pdfDocument) {
return;
}
const pageNumber = typeof val === "string" && this.pdfViewer.pageLabelToPageNumber(val) || val | 0;
if (!(Number.isInteger(pageNumber) && pageNumber > 0 && pageNumber <= this.pagesCount)) {
ngx_console.NgxConsole.error(`PDFLinkService.goToPage: "${val}" is not a valid page.`);
return;
}
if (this.pdfHistory) {
this.pdfHistory.pushCurrentPosition();
this.pdfHistory.pushPage(pageNumber);
}
if (this.pdfViewer.pageViewMode === "book") {
if (this.pdfViewer.pageFlip) {
this.pdfViewer.ensureAdjacentPagesAreLoaded();
const evenPage = this.pdfViewer.currentPageNumber - this.pdfViewer.currentPageNumber % 2;
const evenTargetPage = pageNumber - pageNumber % 2;
if (evenPage === evenTargetPage - 2) {
this.pdfViewer.pageFlip.flipNext();
} else if (evenPage === evenTargetPage + 2) {
this.pdfViewer.pageFlip.flipPrev();
} else {
this.pdfViewer.pageFlip.turnToPage(pageNumber - 1);
}
}
} else {
this.pdfViewer.scrollPageIntoView({
pageNumber
});
}
}
addLinkAttributes(link, url, newWindow = false) {
if (!url || typeof url !== "string") {
throw new Error('A valid "url" parameter must provided.');
}
const target = newWindow ? LinkTarget.BLANK : this.externalLinkTarget,
rel = this.externalLinkRel;
if (this.externalLinkEnabled) {
link.href = link.title = url;
} else {
link.href = "";
link.title = `Disabled: ${url}`;
link.onclick = () => false;
}
let targetStr = "";
switch (target) {
case LinkTarget.NONE:
break;
case LinkTarget.SELF:
targetStr = "_self";
break;
case LinkTarget.BLANK:
targetStr = "_blank";
break;
case LinkTarget.PARENT:
targetStr = "_parent";
break;
case LinkTarget.TOP:
targetStr = "_top";
break;
}
link.target = targetStr;
link.rel = typeof rel === "string" ? rel : DEFAULT_LINK_REL;
}
getDestinationHash(dest) {
if (typeof dest === "string") {
if (dest.length > 0) {
return this.getAnchorUrl("#" + escape(dest));
}
} else if (Array.isArray(dest)) {
const str = JSON.stringify(dest);
if (str.length > 0) {
return this.getAnchorUrl("#" + escape(str));
}
}
return this.getAnchorUrl("");
}
getAnchorUrl(anchor) {
return this.baseUrl ? this.baseUrl + anchor : anchor;
}
setHash(hash) {
if (!this.pdfDocument) {
return;
}
let pageNumber, dest;
if (hash.includes("=")) {
const params = parseQueryString(hash);
if (params.has("search")) {
const query = params.get("search").replaceAll('"', ""),
phrase = params.get("phrase") === "true";
this.eventBus.dispatch("findfromurlhash", {
source: this,
query: phrase ? query : query.match(/\S+/g)
});
}
if (params.has("page")) {
pageNumber = params.get("page") | 0 || 1;
}
if (params.has("zoom")) {
const zoomArgs = params.get("zoom").split(",");
const zoomArg = zoomArgs[0];
const zoomArgNumber = parseFloat(zoomArg);
if (!zoomArg.includes("Fit")) {
dest = [null, {
name: "XYZ"
}, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null, zoomArgs.length > 2 ? zoomArgs[2] | 0 : null, zoomArgNumber ? zoomArgNumber / 100 : zoomArg];
} else if (zoomArg === "Fit" || zoomArg === "FitB") {
dest = [null, {
name: zoomArg
}];
} else if (zoomArg === "FitH" || zoomArg === "FitBH" || zoomArg === "FitV" || zoomArg === "FitBV") {
dest = [null, {
name: zoomArg
}, zoomArgs.length > 1 ? zoomArgs[1] | 0 : null];
} else if (zoomArg === "FitR") {
if (zoomArgs.length !== 5) {
ngx_console.NgxConsole.error('PDFLinkService.setHash: Not enough parameters for "FitR".');
} else {
dest = [null, {
name: zoomArg
}, zoomArgs[1] | 0, zoomArgs[2] | 0, zoomArgs[3] | 0, zoomArgs[4] | 0];
}
} else {
ngx_console.NgxConsole.error(`PDFLinkService.setHash: "${zoomArg}" is not a valid zoom value.`);
}
}
if (dest) {
this.pdfViewer.scrollPageIntoView({
pageNumber: pageNumber || this.page,
destArray: dest,
allowNegativeOffset: true
});
} else if (pageNumber) {
this.page = pageNumber;
}
if (params.has("pagemode")) {
this.eventBus.dispatch("pagemode", {
source: this,
mode: params.get("pagemode")
});
}
if (params.has("nameddest")) {
this.goToDestination(params.get("nameddest"));
}
return;
}
dest = unescape(hash);
try {
dest = JSON.parse(dest);
if (!Array.isArray(dest)) {
dest = dest.toString();
}
} catch {}
if (typeof dest === "string" || PDFLinkService.#isValidExplicitDest(dest)) {
this.goToDestination(dest);
return;
}
console.error(`PDFLinkService.setHash: "${unescape(hash)}" is not a valid destination.`);
}
executeNamedAction(action) {
if (!this.pdfDocument) {
return;
}
switch (action) {
case "GoBack":
this.pdfHistory?.back();
break;
case "GoForward":
this.pdfHistory?.forward();
break;
case "NextPage":
this.pdfViewer.nextPage();
break;
case "PrevPage":
this.pdfViewer.previousPage();
break;
case "LastPage":
this.page = this.pagesCount;
break;
case "FirstPage":
this.page = 1;
break;
default:
break;
}
this.eventBus.dispatch("namedaction", {
source: this,
action
});
}
async executeSetOCGState(action) {
if (!this.pdfDocument) {
return;
}
const pdfDocument = this.pdfDocument,
optionalContentConfig = await this.pdfViewer.optionalContentConfigPromise;
if (pdfDocument !== this.pdfDocument) {
return;
}
optionalContentConfig.setOCGState(action);
this.pdfViewer.optionalContentConfigPromise = Promise.resolve(optionalContentConfig);
}
static #isValidExplicitDest(dest) {
if (!Array.isArray(dest) || dest.length < 2) {
return false;
}
const [page, zoom, ...args] = dest;
if (!(typeof page === "object" && Number.isInteger(page?.num) && Number.isInteger(page?.gen)) && !Number.isInteger(page)) {
return false;
}
if (!(typeof zoom === "object" && typeof zoom?.name === "string")) {
return false;
}
const argsLen = args.length;
let allowNull = true;
switch (zoom.name) {
case "XYZ":
if (argsLen < 2 || argsLen > 3) {
return false;
}
break;
case "Fit":
case "FitB":
return argsLen === 0;
case "FitH":
case "FitBH":
case "FitV":
case "FitBV":
if (argsLen > 1) {
return false;
}
break;
case "FitR":
if (argsLen !== 4) {
return false;
}
allowNull = false;
break;
default:
return false;
}
for (const arg of args) {
if (!(typeof arg === "number" || allowNull && arg === null)) {
return false;
}
}
return true;
}
}
class SimpleLinkService extends PDFLinkService {
setDocument(pdfDocument, baseUrl = null) {}
}
// EXTERNAL MODULE: ./src/shared/util.js
var util = __webpack_require__(292);
// EXTERNAL MODULE: ./src/display/touch_manager.js
var touch_manager = __webpack_require__(900);
// EXTERNAL MODULE: ./src/display/api.js + 18 modules
var api = __webpack_require__(925);
// EXTERNAL MODULE: ./src/display/display_utils.js
var display_utils = __webpack_require__(419);
// EXTERNAL MODULE: ./src/pdf.js + 16 modules
var pdf = __webpack_require__(909);
;// ./web/pdfjs.js
;// ./web/ngx-extended-pdf-viewer-version.js
const ngxExtendedPdfViewerVersion = '23.0.0-alpha.7';
;// ./web/event_utils.js
const WaitOnType = {
EVENT: "event",
TIMEOUT: "timeout"
};
async function waitOnEventOrTimeout({
target,
name,
delay = 0
}) {
if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
throw new Error("waitOnEventOrTimeout - invalid parameters.");
}
const {
promise,
resolve
} = Promise.withResolvers();
const ac = new AbortController();
function handler(type) {
ac.abort();
clearTimeout(timeout);
resolve(type);
}
const evtMethod = target instanceof EventBus ? "_on" : "addEventListener";
target[evtMethod](name, handler.bind(null, WaitOnType.EVENT), {
signal: ac.signal
});
const timeout = setTimeout(handler.bind(null, WaitOnType.TIMEOUT), delay);
return promise;
}
class EventBus {
#listeners = Object.create(null);
on(eventName, listener, options = null) {
this._on(eventName, listener, {
external: true,
once: options?.once,
signal: options?.signal
});
}
off(eventName, listener, options = null) {
this._off(eventName, listener);
}
dispatch(eventName, data) {
const eventListeners = this.#listeners[eventName];
if (!eventListeners || eventListeners.length === 0) {
return;
}
let externalListeners;
for (const {
listener,
external,
once
} of eventListeners.slice(0)) {
if (once) {
this._off(eventName, listener);
}
if (external) {
(externalListeners ||= []).push(listener);
continue;
}
listener(data);
}
if (externalListeners) {
for (const listener of externalListeners) {
listener(data);
}
externalListeners = null;
}
}
_on(eventName, listener, options = null) {
let rmAbort = null;
if (options?.signal instanceof AbortSignal) {
const {
signal
} = options;
if (signal.aborted) {
console.error("Cannot use an `aborted` signal.");
return;
}
const onAbort = () => this._off(eventName, listener);
rmAbort = () => signal.removeEventListener("abort", onAbort);
signal.addEventListener("abort", onAbort);
}
const eventListeners = this.#listeners[eventName] ||= [];
eventListeners.push({
listener,
external: options?.external === true,
once: options?.once === true,
rmAbort
});
}
_off(eventName, listener, options = null) {
const eventListeners = this.#listeners[eventName];
if (!eventListeners) {
return;
}
for (let i = 0, ii = eventListeners.length; i < ii; i++) {
const evt = eventListeners[i];
if (evt.listener === listener) {
evt.rmAbort?.();
eventListeners.splice(i, 1);
return;
}
}
}
destroy() {
let eventName;
for (eventName in this.#listeners) {
this.#listeners[eventName].length = 0;
}
}
}
class FirefoxEventBus extends EventBus {
#externalServices;
#globalEventNames;
#isInAutomation;
constructor(globalEventNames, externalServices, isInAutomation) {
super();
this.#globalEventNames = globalEventNames;
this.#externalServices = externalServices;
this.#isInAutomation = isInAutomation;
}
dispatch(eventName, data) {
throw new Error("Not implemented: FirefoxEventBus.dispatch");
}
}
;// ./web/external_services.js
class BaseExternalServices {
updateFindControlState(data) {}
updateFindMatchesCount(data) {}
initPassiveLoading() {}
reportTelemetry(data) {}
async createL10n() {
throw new Error("Not implemented: createL10n");
}
createScripting() {
throw new Error("Not implemented: createScripting");
}
updateEditorStates(data) {
throw new Error("Not implemented: updateEditorStates");
}
dispatchGlobalEvent(_event) {}
}
;// ./web/preferences.js
class BasePreferences {
#defaults = Object.freeze({
altTextLearnMoreUrl: "",
annotationEditorMode: 0,
annotationMode: 2,
cursorToolOnLoad: 0,
defaultZoomDelay: 400,
defaultZoomValue: "auto",
disablePageLabels: false,
enableAltText: false,
enableAltTextModelDownload: true,
enableGuessAltText: true,
enableHighlightFloatingButton: false,
enableNewAltTextWhenAddingImage: true,
enablePermissions: false,
enablePrintAutoRotate: true,
enableScripting: true,
enableUpdatedAddImage: false,
externalLinkTarget: 0,
highlightEditorColors: "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F",
historyUpdateUrl: false,
ignoreDestinationZoom: false,
forcePageColors: false,
pageColorsBackground: "Canvas",
pageColorsForeground: "CanvasText",
pdfBugEnabled: false,
removePageBorders: false,
sidebarViewOnLoad: -1,
scrollModeOnLoad: -1,
spreadModeOnLoad: -1,
textLayerMode: 1,
viewOnLoad: 0,
disableAutoFetch: false,
disableFontFace: false,
disableRange: false,
disableStream: false,
enableHWA: true,
enableXfa: true,
viewerCssTheme: 0
});
#initializedPromise = null;
constructor() {
this.#initializedPromise = this._readFromStorage(this.#defaults).then(({
browserPrefs,
prefs
}) => {
if (AppOptions._checkDisablePreferences()) {
return;
}
AppOptions.setAll({
...browserPrefs,
...prefs
}, true);
});
}
async _writeToStorage(prefObj) {
throw new Error("Not implemented: _writeToStorage");
}
async _readFromStorage(prefObj) {
throw new Error("Not implemented: _readFromStorage");
}
async reset() {
await this.#initializedPromise;
AppOptions.setAll(this.#defaults, true);
await this._writeToStorage(this.#defaults);
}
async set(name, value) {
await this.#initializedPromise;
AppOptions.setAll({
[name]: value
}, true);
await this._writeToStorage(AppOptions.getAll(OptionKind.PREFERENCE));
}
async get(name) {
await this.#initializedPromise;
return AppOptions.get(name);
}
get initializedPromise() {
return this.#initializedPromise;
}
}
;// ./node_modules/@fluent/bundle/esm/types.js
class FluentType {
constructor(value) {
this.value = value;
}
valueOf() {
return this.value;
}
}
class FluentNone extends FluentType {
constructor(value = "???") {
super(value);
}
toString(scope) {
return `{${this.value}}`;
}
}
class FluentNumber extends FluentType {
constructor(value, opts = {}) {
super(value);
this.opts = opts;
}
toString(scope) {
try {
const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts);
return nf.format(this.value);
} catch (err) {
scope.reportError(err);
return this.value.toString(10);
}
}
}
class FluentDateTime extends FluentType {
constructor(value, opts = {}) {
super(value);
this.opts = opts;
}
toString(scope) {
try {
const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts);
return dtf.format(this.value);
} catch (err) {
scope.reportError(err);
return new Date(this.value).toISOString();
}
}
}
;// ./node_modules/@fluent/bundle/esm/resolver.js
const MAX_PLACEABLES = 100;
const FSI = "\u2068";
const PDI = "\u2069";
function match(scope, selector, key) {
if (key === selector) {
return true;
}
if (key instanceof FluentNumber && selector instanceof FluentNumber && key.value === selector.value) {
return true;
}
if (selector instanceof FluentNumber && typeof key === "string") {
let category = scope.memoizeIntlObject(Intl.PluralRules, selector.opts).select(selector.value);
if (key === category) {
return true;
}
}
return false;
}
function getDefault(scope, variants, star) {
if (variants[star]) {
return resolvePattern(scope, variants[star].value);
}
scope.reportError(new RangeError("No default"));
return new FluentNone();
}
function getArguments(scope, args) {
const positional = [];
const named = Object.create(null);
for (const arg of args) {
if (arg.type === "narg") {
named[arg.name] = resolveExpression(scope, arg.value);
} else {
positional.push(resolveExpression(scope, arg));
}
}
return {
positional,
named
};
}
function resolveExpression(scope, expr) {
switch (expr.type) {
case "str":
return expr.value;
case "num":
return new FluentNumber(expr.value, {
minimumFractionDigits: expr.precision
});
case "var":
return resolveVariableReference(scope, expr);
case "mesg":
return resolveMessageReference(scope, expr);
case "term":
return resolveTermReference(scope, expr);
case "func":
return resolveFunctionReference(scope, expr);
case "select":
return resolveSelectExpression(scope, expr);
default:
return new FluentNone();
}
}
function resolveVariableReference(scope, {
name
}) {
let arg;
if (scope.params) {
if (Object.prototype.hasOwnProperty.call(scope.params, name)) {
arg = scope.params[name];
} else {
return new FluentNone(`$${name}`);
}
} else if (scope.args && Object.prototype.hasOwnProperty.call(scope.args, name)) {
arg = scope.args[name];
} else {
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
return new FluentNone(`$${name}`);
}
if (arg instanceof FluentType) {
return arg;
}
switch (typeof arg) {
case "string":
return arg;
case "number":
return new FluentNumber(arg);
case "object":
if (arg instanceof Date) {
return new FluentDateTime(arg.getTime());
}
default:
scope.reportError(new TypeError(`Variable type not supported: $${name}, ${typeof arg}`));
return new FluentNone(`$${name}`);
}
}
function resolveMessageReference(scope, {
name,
attr
}) {
const message = scope.bundle._messages.get(name);
if (!message) {
scope.reportError(new ReferenceError(`Unknown message: ${name}`));
return new FluentNone(name);
}
if (attr) {
const attribute = message.attributes[attr];
if (attribute) {
return resolvePattern(scope, attribute);
}
scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));
return new FluentNone(`${name}.${attr}`);
}
if (message.value) {
return resolvePattern(scope, message.value);
}
scope.reportError(new ReferenceError(`No value: ${name}`));
return new FluentNone(name);
}
function resolveTermReference(scope, {
name,
attr,
args
}) {
const id = `-${name}`;
const term = scope.bundle._terms.get(id);
if (!term) {
scope.reportError(new ReferenceError(`Unknown term: ${id}`));
return new FluentNone(id);
}
if (attr) {
const attribute = term.attributes[attr];
if (attribute) {
scope.params = getArguments(scope, args).named;
const resolved = resolvePattern(scope, attribute);
scope.params = null;
return resolved;
}
scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));
return new FluentNone(`${id}.${attr}`);
}
scope.params = getArguments(scope, args).named;
const resolved = resolvePattern(scope, term.value);
scope.params = null;
return resolved;
}
function resolveFunctionReference(scope, {
name,
args
}) {
let func = scope.bundle._functions[name];
if (!func) {
scope.reportError(new ReferenceError(`Unknown function: ${name}()`));
return new FluentNone(`${name}()`);
}
if (typeof func !== "function") {
scope.reportError(new TypeError(`Function ${name}() is not callable`));
return new FluentNone(`${name}()`);
}
try {
let resolved = getArguments(scope, args);
return func(resolved.positional, resolved.named);
} catch (err) {
scope.reportError(err);
return new FluentNone(`${name}()`);
}
}
function resolveSelectExpression(scope, {
selector,
variants,
star
}) {
let sel = resolveExpression(scope, selector);
if (sel instanceof FluentNone) {
return getDefault(scope, variants, star);
}
for (const variant of variants) {
const key = resolveExpression(scope, variant.key);
if (match(scope, sel, key)) {
return resolvePattern(scope, variant.value);
}
}
return getDefault(scope, variants, star);
}
function resolveComplexPattern(scope, ptn) {
if (scope.dirty.has(ptn)) {
scope.reportError(new RangeError("Cyclic reference"));
return new FluentNone();
}
scope.dirty.add(ptn);
const result = [];
const useIsolating = scope.bundle._useIsolating && ptn.length > 1;
for (const elem of ptn) {
if (typeof elem === "string") {
result.push(scope.bundle._transform(elem));
continue;
}
scope.placeables++;
if (scope.placeables > MAX_PLACEABLES) {
scope.dirty.delete(ptn);
throw new RangeError(`Too many placeables expanded: ${scope.placeables}, ` + `max allowed is ${MAX_PLACEABLES}`);
}
if (useIsolating) {
result.push(FSI);
}
result.push(resolveExpression(scope, elem).toString(scope));
if (useIsolating) {
result.push(PDI);
}
}
scope.dirty.delete(ptn);
return result.join("");
}
function resolvePattern(scope, value) {
if (typeof value === "string") {
return scope.bundle._transform(value);
}
return resolveComplexPattern(scope, value);
}
;// ./node_modules/@fluent/bundle/esm/scope.js
class Scope {
constructor(bundle, errors, args) {
this.dirty = new WeakSet();
this.params = null;
this.placeables = 0;
this.bundle = bundle;
this.errors = errors;
this.args = args;
}
reportError(error) {
if (!this.errors || !(error instanceof Error)) {
throw error;
}
this.errors.push(error);
}
memoizeIntlObject(ctor, opts) {
let cache = this.bundle._intls.get(ctor);
if (!cache) {
cache = {};
this.bundle._intls.set(ctor, cache);
}
let id = JSON.stringify(opts);
if (!cache[id]) {
cache[id] = new ctor(this.bundle.locales, opts);
}
return cache[id];
}
}
;// ./node_modules/@fluent/bundle/esm/builtins.js
function values(opts, allowed) {
const unwrapped = Object.create(null);
for (const [name, opt] of Object.entries(opts)) {
if (allowed.includes(name)) {
unwrapped[name] = opt.valueOf();
}
}
return unwrapped;
}
const NUMBER_ALLOWED = ["unitDisplay", "currencyDisplay", "useGrouping", "minimumIntegerDigits", "minimumFractionDigits", "maximumFractionDigits", "minimumSignificantDigits", "maximumSignificantDigits"];
function NUMBER(args, opts) {
let arg = args[0];
if (arg instanceof FluentNone) {
return new FluentNone(`NUMBER(${arg.valueOf()})`);
}
if (arg instanceof FluentNumber) {
return new FluentNumber(arg.valueOf(), {
...arg.opts,
...values(opts, NUMBER_ALLOWED)
});
}
if (arg instanceof FluentDateTime) {
return new FluentNumber(arg.valueOf(), {
...values(opts, NUMBER_ALLOWED)
});
}
throw new TypeError("Invalid argument to NUMBER");
}
const DATETIME_ALLOWED = ["dateStyle", "timeStyle", "fractionalSecondDigits", "dayPeriod", "hour12", "weekday", "era", "year", "month", "day", "hour", "minute", "second", "timeZoneName"];
function DATETIME(args, opts) {
let arg = args[0];
if (arg instanceof FluentNone) {
return new FluentNone(`DATETIME(${arg.valueOf()})`);
}
if (arg instanceof FluentDateTime) {
return new FluentDateTime(arg.valueOf(), {
...arg.opts,
...values(opts, DATETIME_ALLOWED)
});
}
if (arg instanceof FluentNumber) {
return new FluentDateTime(arg.valueOf(), {
...values(opts, DATETIME_ALLOWED)
});
}
throw new TypeError("Invalid argument to DATETIME");
}
;// ./node_modules/@fluent/bundle/esm/memoizer.js
const cache = new Map();
function getMemoizerForLocale(locales) {
const stringLocale = Array.isArray(locales) ? locales.join(" ") : locales;
let memoizer = cache.get(stringLocale);
if (memoizer === undefined) {
memoizer = new Map();
cache.set(stringLocale, memoizer);
}
return memoizer;
}
;// ./node_modules/@fluent/bundle/esm/bundle.js
class FluentBundle {
constructor(locales, {
functions,
useIsolating = true,
transform = v => v
} = {}) {
this._terms = new Map();
this._messages = new Map();
this.locales = Array.isArray(locales) ? locales : [locales];
this._functions = {
NUMBER: NUMBER,
DATETIME: DATETIME,
...functions
};
this._useIsolating = useIsolating;
this._transform = transform;
this._intls = getMemoizerForLocale(locales);
}
hasMessage(id) {
return this._messages.has(id);
}
getMessage(id) {
return this._messages.get(id);
}
addResource(res, {
allowOverrides = false
} = {}) {
const errors = [];
for (let i = 0; i < res.body.length; i++) {
let entry = res.body[i];
if (entry.id.startsWith("-")) {
if (allowOverrides === false && this._terms.has(entry.id)) {
errors.push(new Error(`Attempt to override an existing term: "${entry.id}"`));
continue;
}
this._terms.set(entry.id, entry);
} else {
if (allowOverrides === false && this._messages.has(entry.id)) {
errors.push(new Error(`Attempt to override an existing message: "${entry.id}"`));
continue;
}
this._messages.set(entry.id, entry);
}
}
return errors;
}
formatPattern(pattern, args = null, errors = null) {
if (typeof pattern === "string") {
return this._transform(pattern);
}
let scope = new Scope(this, errors, args);
try {
let value = resolveComplexPattern(scope, pattern);
return value.toString(scope);
} catch (err) {
if (scope.errors && err instanceof Error) {
scope.errors.push(err);
return new FluentNone().toString(scope);
}
throw err;
}
}
}
;// ./node_modules/@fluent/bundle/esm/resource.js
const RE_MESSAGE_START = /^(-?[a-zA-Z][\w-]*) *= */gm;
const RE_ATTRIBUTE_START = /\.([a-zA-Z][\w-]*) *= */y;
const RE_VARIANT_START = /\*?\[/y;
const RE_NUMBER_LITERAL = /(-?[0-9]+(?:\.([0-9]+))?)/y;
const RE_IDENTIFIER = /([a-zA-Z][\w-]*)/y;
const RE_REFERENCE = /([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y;
const RE_FUNCTION_NAME = /^[A-Z][A-Z0-9_-]*$/;
const RE_TEXT_RUN = /([^{}\n\r]+)/y;
const RE_STRING_RUN = /([^\\"\n\r]*)/y;
const RE_STRING_ESCAPE = /\\([\\"])/y;
const RE_UNICODE_ESCAPE = /\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y;
const RE_LEADING_NEWLINES = /^\n+/;
const RE_TRAILING_SPACES = / +$/;
const RE_BLANK_LINES = / *\r?\n/g;
const RE_INDENT = /( *)$/;
const TOKEN_BRACE_OPEN = /{\s*/y;
const TOKEN_BRACE_CLOSE = /\s*}/y;
const TOKEN_BRACKET_OPEN = /\[\s*/y;
const TOKEN_BRACKET_CLOSE = /\s*] */y;
const TOKEN_PAREN_OPEN = /\s*\(\s*/y;
const TOKEN_ARROW = /\s*->\s*/y;
const TOKEN_COLON = /\s*:\s*/y;
const TOKEN_COMMA = /\s*,?\s*/y;
const TOKEN_BLANK = /\s+/y;
class FluentResource {
constructor(source) {
this.body = [];
RE_MESSAGE_START.lastIndex = 0;
let cursor = 0;
while (true) {
let next = RE_MESSAGE_START.exec(source);
if (next === null) {
break;
}
cursor = RE_MESSAGE_START.lastIndex;
try {
this.body.push(parseMessage(next[1]));
} catch (err) {
if (err instanceof SyntaxError) {
continue;
}
throw err;
}
}
function test(re) {
re.lastIndex = cursor;
return re.test(source);
}
function consumeChar(char, errorClass) {
if (source[cursor] === char) {
cursor++;
return true;
}
if (errorClass) {
throw new errorClass(`Expected ${char}`);
}
return false;
}
function consumeToken(re, errorClass) {
if (test(re)) {
cursor = re.lastIndex;
return true;
}
if (errorClass) {
throw new errorClass(`Expected ${re.toString()}`);
}
return false;
}
function match(re) {
re.lastIndex = cursor;
let result = re.exec(source);
if (result === null) {
throw new SyntaxError(`Expected ${re.toString()}`);
}
cursor = re.lastIndex;
return result;
}
function match1(re) {
return match(re)[1];
}
function parseMessage(id) {
let value = parsePattern();
let attributes = parseAttributes();
if (value === null && Object.keys(attributes).length === 0) {
throw new SyntaxError("Expected message value or attributes");
}
return {
id,
value,
attributes
};
}
function parseAttributes() {
let attrs = Object.create(null);
while (test(RE_ATTRIBUTE_START)) {
let name = match1(RE_ATTRIBUTE_START);
let value = parsePattern();
if (value === null) {
throw new SyntaxError("Expected attribute value");
}
attrs[name] = value;
}
return attrs;
}
function parsePattern() {
let first;
if (test(RE_TEXT_RUN)) {
first = match1(RE_TEXT_RUN);
}
if (source[cursor] === "{" || source[cursor] === "}") {
return parsePatternElements(first ? [first] : [], Infinity);
}
let indent = parseIndent();
if (indent) {
if (first) {
return parsePatternElements([first, indent], indent.length);
}
indent.value = trim(indent.value, RE_LEADING_NEWLINES);
return parsePatternElements([indent], indent.length);
}
if (first) {
return trim(first, RE_TRAILING_SPACES);
}
return null;
}
function parsePatternElements(elements = [], commonIndent) {
while (true) {
if (test(RE_TEXT_RUN)) {
elements.push(match1(RE_TEXT_RUN));
continue;
}
if (source[cursor] === "{") {
elements.push(parsePlaceable());
continue;
}
if (source[cursor] === "}") {
throw new SyntaxError("Unbalanced closing brace");
}
let indent = parseIndent();
if (indent) {
elements.push(indent);
commonIndent = Math.min(commonIndent, indent.length);
continue;
}
break;
}
let lastIndex = elements.length - 1;
let lastElement = elements[lastIndex];
if (typeof lastElement === "string") {
elements[lastIndex] = trim(lastElement, RE_TRAILING_SPACES);
}
let baked = [];
for (let element of elements) {
if (element instanceof Indent) {
element = element.value.slice(0, element.value.length - commonIndent);
}
if (element) {
baked.push(element);
}
}
return baked;
}
function parsePlaceable() {
consumeToken(TOKEN_BRACE_OPEN, SyntaxError);
let selector = parseInlineExpression();
if (consumeToken(TOKEN_BRACE_CLOSE)) {
return selector;
}
if (consumeToken(TOKEN_ARROW)) {
let variants = parseVariants();
consumeToken(TOKEN_BRACE_CLOSE, SyntaxError);
return {
type: "select",
selector,
...variants
};
}
throw new SyntaxError("Unclosed placeable");
}
function parseInlineExpression() {
if (source[cursor] === "{") {
return parsePlaceable();
}
if (test(RE_REFERENCE)) {
let [, sigil, name, attr = null] = match(RE_REFERENCE);
if (sigil === "$") {
return {
type: "var",
name
};
}
if (consumeToken(TOKEN_PAREN_OPEN)) {
let args = parseArguments();
if (sigil === "-") {
return {
type: "term",
name,
attr,
args
};
}
if (RE_FUNCTION_NAME.test(name)) {
return {
type: "func",
name,
args
};
}
throw new SyntaxError("Function names must be all upper-case");
}
if (sigil === "-") {
return {
type: "term",
name,
attr,
args: []
};
}
return {
type: "mesg",
name,
attr
};
}
return parseLiteral();
}
function parseArguments() {
let args = [];
while (true) {
switch (source[cursor]) {
case ")":
cursor++;
return args;
case undefined:
throw new SyntaxError("Unclosed argument list");
}
args.push(parseArgument());
consumeToken(TOKEN_COMMA);
}
}
function parseArgument() {
let expr = parseInlineExpression();
if (expr.type !== "mesg") {
return expr;
}
if (consumeToken(TOKEN_COLON)) {
return {
type: "narg",
name: expr.name,
value: parseLiteral()
};
}
return expr;
}
function parseVariants() {
let variants = [];
let count = 0;
let star;
while (test(RE_VARIANT_START)) {
if (consumeChar("*")) {
star = count;
}
let key = parseVariantKey();
let value = parsePattern();
if (value === null) {
throw new SyntaxError("Expected variant value");
}
variants[count++] = {
key,
value
};
}
if (count === 0) {
return null;
}
if (star === undefined) {
throw new SyntaxError("Expected default variant");
}
return {
variants,
star
};
}
function parseVariantKey() {
consumeToken(TOKEN_BRACKET_OPEN, SyntaxError);
let key;
if (test(RE_NUMBER_LITERAL)) {
key = parseNumberLiteral();
} else {
key = {
type: "str",
value: match1(RE_IDENTIFIER)
};
}
consumeToken(TOKEN_BRACKET_CLOSE, SyntaxError);
return key;
}
function parseLiteral() {
if (test(RE_NUMBER_LITERAL)) {
return parseNumberLiteral();
}
if (source[cursor] === '"') {
return parseStringLiteral();
}
throw new SyntaxError("Invalid expression");
}
function parseNumberLiteral() {
let [, value, fraction = ""] = match(RE_NUMBER_LITERAL);
let precision = fraction.length;
return {
type: "num",
value: parseFloat(value),
precision
};
}
function parseStringLiteral() {
consumeChar('"', SyntaxError);
let value = "";
while (true) {
value += match1(RE_STRING_RUN);
if (source[cursor] === "\\") {
value += parseEscapeSequence();
continue;
}
if (consumeChar('"')) {
return {
type: "str",
value
};
}
throw new SyntaxError("Unclosed string literal");
}
}
function parseEscapeSequence() {
if (test(RE_STRING_ESCAPE)) {
return match1(RE_STRING_ESCAPE);
}
if (test(RE_UNICODE_ESCAPE)) {
let [, codepoint4, codepoint6] = match(RE_UNICODE_ESCAPE);
let codepoint = parseInt(codepoint4 || codepoint6, 16);
return codepoint <= 0xd7ff || 0xe000 <= codepoint ? String.fromCodePoint(codepoint) : "�";
}
throw new SyntaxError("Unknown escape sequence");
}
function parseIndent() {
let start = cursor;
consumeToken(TOKEN_BLANK);
switch (source[cursor]) {
case ".":
case "[":
case "*":
case "}":
case undefined:
return false;
case "{":
return makeIndent(source.slice(start, cursor));
}
if (source[cursor - 1] === " ") {
return makeIndent(source.slice(start, cursor));
}
return false;
}
function trim(text, re) {
return text.replace(re, "");
}
function makeIndent(blank) {
let value = blank.replace(RE_BLANK_LINES, "\n");
let length = RE_INDENT.exec(blank)[1].length;
return new Indent(value, length);
}
}
}
class Indent {
constructor(value, length) {
this.value = value;
this.length = length;
}
}
;// ./node_modules/@fluent/bundle/esm/index.js
;// ./node_modules/@fluent/dom/esm/overlay.js
const reOverlay = /<|&#?\w+;/;
const TEXT_LEVEL_ELEMENTS = {
"http://www.w3.org/1999/xhtml": ["em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "bdi", "bdo", "span", "br", "wbr"]
};
const LOCALIZABLE_ATTRIBUTES = {
"http://www.w3.org/1999/xhtml": {
global: ["title", "aria-label", "aria-valuetext"],
a: ["download"],
area: ["download", "alt"],
input: ["alt", "placeholder"],
menuitem: ["label"],
menu: ["label"],
optgroup: ["label"],
option: ["label"],
track: ["label"],
img: ["alt"],
textarea: ["placeholder"],
th: ["abbr"]
},
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": {
global: ["accesskey", "aria-label", "aria-valuetext", "label", "title", "tooltiptext"],
description: ["value"],
key: ["key", "keycode"],
label: ["value"],
textbox: ["placeholder", "value"]
}
};
function translateElement(element, translation) {
const {
value
} = translation;
if (typeof value === "string") {
if (element.localName === "title" && element.namespaceURI === "http://www.w3.org/1999/xhtml") {
element.textContent = value;
} else if (!reOverlay.test(value)) {
element.textContent = value;
} else {
const templateElement = element.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml", "template");
templateElement.innerHTML = value;
overlayChildNodes(templateElement.content, element);
}
}
overlayAttributes(translation, element);
}
function overlayChildNodes(fromFragment, toElement) {
for (const childNode of fromFragment.childNodes) {
if (childNode.nodeType === childNode.TEXT_NODE) {
continue;
}
if (childNode.hasAttribute("data-l10n-name")) {
const sanitized = getNodeForNamedElement(toElement, childNode);
fromFragment.replaceChild(sanitized, childNode);
continue;
}
if (isElementAllowed(childNode)) {
const sanitized = createSanitizedElement(childNode);
fromFragment.replaceChild(sanitized, childNode);
continue;
}
console.warn(`An element of forbidden type "${childNode.localName}" was found in ` + "the translation. Only safe text-level elements and elements with " + "data-l10n-name are allowed.");
fromFragment.replaceChild(createTextNodeFromTextContent(childNode), childNode);
}
toElement.textContent = "";
toElement.appendChild(fromFragment);
}
function hasAttribute(attributes, name) {
if (!attributes) {
return false;
}
for (let attr of attributes) {
if (attr.name === name) {
return true;
}
}
return false;
}
function overlayAttributes(fromElement, toElement) {
const explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") ? toElement.getAttribute("data-l10n-attrs").split(",").map(i => i.trim()) : null;
for (const attr of Array.from(toElement.attributes)) {
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && !hasAttribute(fromElement.attributes, attr.name)) {
toElement.removeAttribute(attr.name);
}
}
if (!fromElement.attributes) {
return;
}
for (const attr of Array.from(fromElement.attributes)) {
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && toElement.getAttribute(attr.name) !== attr.value) {
toElement.setAttribute(attr.name, attr.value);
}
}
}
function getNodeForNamedElement(sourceElement, translatedChild) {
const childName = translatedChild.getAttribute("data-l10n-name");
const sourceChild = sourceElement.querySelector(`[data-l10n-name="${childName}"]`);
if (!sourceChild) {
console.warn(`An element named "${childName}" wasn't found in the source.`);
return createTextNodeFromTextContent(translatedChild);
}
if (sourceChild.localName !== translatedChild.localName) {
console.warn(`An element named "${childName}" was found in the translation ` + `but its type ${translatedChild.localName} didn't match the ` + `element found in the source (${sourceChild.localName}).`);
return createTextNodeFromTextContent(translatedChild);
}
sourceElement.removeChild(sourceChild);
const clone = sourceChild.cloneNode(false);
return shallowPopulateUsing(translatedChild, clone);
}
function createSanitizedElement(element) {
const clone = element.ownerDocument.createElement(element.localName);
return shallowPopulateUsing(element, clone);
}
function createTextNodeFromTextContent(element) {
return element.ownerDocument.createTextNode(element.textContent);
}
function isElementAllowed(element) {
const allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI];
return allowed && allowed.includes(element.localName);
}
function isAttrNameLocalizable(name, element, explicitlyAllowed = null) {
if (explicitlyAllowed && explicitlyAllowed.includes(name)) {
return true;
}
const allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI];
if (!allowed) {
return false;
}
const attrName = name.toLowerCase();
const elemName = element.localName;
if (allowed.global.includes(attrName)) {
return true;
}
if (!allowed[elemName]) {
return false;
}
if (allowed[elemName].includes(attrName)) {
return true;
}
if (element.namespaceURI === "http://www.w3.org/1999/xhtml" && elemName === "input" && attrName === "value") {
const type = element.type.toLowerCase();
if (type === "submit" || type === "button" || type === "reset") {
return true;
}
}
return false;
}
function shallowPopulateUsing(fromElement, toElement) {
toElement.textContent = fromElement.textContent;
overlayAttributes(fromElement, toElement);
return toElement;
}
;// ./node_modules/cached-iterable/src/cached_iterable.mjs
class CachedIterable extends Array {
static from(iterable) {
if (iterable instanceof this) {
return iterable;
}
return new this(iterable);
}
}
;// ./node_modules/cached-iterable/src/cached_sync_iterable.mjs
class CachedSyncIterable extends CachedIterable {
constructor(iterable) {
super();
if (Symbol.iterator in Object(iterable)) {
this.iterator = iterable[Symbol.iterator]();
} else {
throw new TypeError("Argument must implement the iteration protocol.");
}
}
[Symbol.iterator]() {
const cached = this;
let cur = 0;
return {
next() {
if (cached.length <= cur) {
cached.push(cached.iterator.next());
}
return cached[cur++];
}
};
}
touchNext(count = 1) {
let idx = 0;
while (idx++ < count) {
const last = this[this.length - 1];
if (last && last.done) {
break;
}
this.push(this.iterator.next());
}
return this[this.length - 1];
}
}
;// ./node_modules/cached-iterable/src/cached_async_iterable.mjs
class CachedAsyncIterable extends CachedIterable {
constructor(iterable) {
super();
if (Symbol.asyncIterator in Object(iterable)) {
this.iterator = iterable[Symbol.asyncIterator]();
} else if (Symbol.iterator in Object(iterable)) {
this.iterator = iterable[Symbol.iterator]();
} else {
throw new TypeError("Argument must implement the iteration protocol.");
}
}
[Symbol.asyncIterator]() {
const cached = this;
let cur = 0;
return {
async next() {
if (cached.length <= cur) {
cached.push(cached.iterator.next());
}
return cached[cur++];
}
};
}
async touchNext(count = 1) {
let idx = 0;
while (idx++ < count) {
const last = this[this.length - 1];
if (last && (await last).done) {
break;
}
this.push(this.iterator.next());
}
return this[this.length - 1];
}
}
;// ./node_modules/cached-iterable/src/index.mjs
;// ./node_modules/@fluent/dom/esm/localization.js
class Localization {
constructor(resourceIds = [], generateBundles) {
this.resourceIds = resourceIds;
this.generateBundles = generateBundles;
this.onChange(true);
}
addResourceIds(resourceIds, eager = false) {
this.resourceIds.push(...resourceIds);
this.onChange(eager);
return this.resourceIds.length;
}
removeResourceIds(resourceIds) {
this.resourceIds = this.resourceIds.filter(r => !resourceIds.includes(r));
this.onChange();
return this.resourceIds.length;
}
async formatWithFallback(keys, method) {
const translations = [];
let hasAtLeastOneBundle = false;
for await (const bundle of this.bundles) {
hasAtLeastOneBundle = true;
const missingIds = keysFromBundle(method, bundle, keys, translations);
if (missingIds.size === 0) {
break;
}
if (typeof console !== "undefined") {
const locale = bundle.locales[0];
const ids = Array.from(missingIds).join(", ");
console.warn(`[fluent] Missing translations in ${locale}: ${ids}`);
}
}
if (!hasAtLeastOneBundle && typeof console !== "undefined") {
console.warn(`[fluent] Request for keys failed because no resource bundles got generated.
keys: ${JSON.stringify(keys)}.
resourceIds: ${JSON.stringify(this.resourceIds)}.`);
}
return translations;
}
formatMessages(keys) {
return this.formatWithFallback(keys, messageFromBundle);
}
formatValues(keys) {
return this.formatWithFallback(keys, valueFromBundle);
}
async formatValue(id, args) {
const [val] = await this.formatValues([{
id,
args
}]);
return val;
}
handleEvent() {
this.onChange();
}
onChange(eager = false) {
this.bundles = CachedAsyncIterable.from(this.generateBundles(this.resourceIds));
if (eager) {
this.bundles.touchNext(2);
}
}
}
function valueFromBundle(bundle, errors, message, args) {
if (message.value) {
return bundle.formatPattern(message.value, args, errors);
}
return null;
}
function messageFromBundle(bundle, errors, message, args) {
const formatted = {
value: null,
attributes: null
};
if (message.value) {
formatted.value = bundle.formatPattern(message.value, args, errors);
}
let attrNames = Object.keys(message.attributes);
if (attrNames.length > 0) {
formatted.attributes = new Array(attrNames.length);
for (let [i, name] of attrNames.entries()) {
let value = bundle.formatPattern(message.attributes[name], args, errors);
formatted.attributes[i] = {
name,
value
};
}
}
return formatted;
}
function keysFromBundle(method, bundle, keys, translations) {
const messageErrors = [];
const missingIds = new Set();
keys.forEach(({
id,
args
}, i) => {
if (translations[i] !== undefined) {
return;
}
let message = bundle.getMessage(id);
if (message) {
messageErrors.length = 0;
translations[i] = method(bundle, messageErrors, message, args);
if (messageErrors.length > 0 && typeof console !== "undefined") {
const locale = bundle.locales[0];
const errors = messageErrors.join(", ");
console.warn(`[fluent][resolver] errors in ${locale}/${id}: ${errors}.`);
}
} else {
missingIds.add(id);
}
});
return missingIds;
}
;// ./node_modules/@fluent/dom/esm/dom_localization.js
const L10NID_ATTR_NAME = "data-l10n-id";
const L10NARGS_ATTR_NAME = "data-l10n-args";
const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`;
class DOMLocalization extends Localization {
constructor(resourceIds, generateBundles) {
super(resourceIds, generateBundles);
this.roots = new Set();
this.pendingrAF = null;
this.pendingElements = new Set();
this.windowElement = null;
this.mutationObserver = null;
this.observerConfig = {
attributes: true,
characterData: false,
childList: true,
subtree: true,
attributeFilter: [L10NID_ATTR_NAME, L10NARGS_ATTR_NAME]
};
}
onChange(eager = false) {
super.onChange(eager);
if (this.roots) {
this.translateRoots();
}
}
setAttributes(element, id, args) {
element.setAttribute(L10NID_ATTR_NAME, id);
if (args) {
element.setAttribute(L10NARGS_ATTR_NAME, JSON.stringify(args));
} else {
element.removeAttribute(L10NARGS_ATTR_NAME);
}
return element;
}
getAttributes(element) {
return {
id: element.getAttribute(L10NID_ATTR_NAME),
args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null)
};
}
connectRoot(newRoot) {
for (const root of this.roots) {
if (root === newRoot || root.contains(newRoot) || newRoot.contains(root)) {
throw new Error("Cannot add a root that overlaps with existing root.");
}
}
if (this.windowElement) {
if (this.windowElement !== newRoot.ownerDocument.defaultView) {
throw new Error(`Cannot connect a root:
DOMLocalization already has a root from a different window.`);
}
} else {
this.windowElement = newRoot.ownerDocument.defaultView;
this.mutationObserver = new this.windowElement.MutationObserver(mutations => this.translateMutations(mutations));
}
this.roots.add(newRoot);
this.mutationObserver.observe(newRoot, this.observerConfig);
}
disconnectRoot(root) {
this.roots.delete(root);
this.pauseObserving();
if (this.roots.size === 0) {
this.mutationObserver = null;
if (this.windowElement && this.pendingrAF) {
this.windowElement.cancelAnimationFrame(this.pendingrAF);
}
this.windowElement = null;
this.pendingrAF = null;
this.pendingElements.clear();
return true;
}
this.resumeObserving();
return false;
}
translateRoots() {
const roots = Array.from(this.roots);
return Promise.all(roots.map(root => this.translateFragment(root)));
}
pauseObserving() {
if (!this.mutationObserver) {
return;
}
this.translateMutations(this.mutationObserver.takeRecords());
this.mutationObserver.disconnect();
}
resumeObserving() {
if (!this.mutationObserver) {
return;
}
for (const root of this.roots) {
this.mutationObserver.observe(root, this.observerConfig);
}
}
translateMutations(mutations) {
for (const mutation of mutations) {
switch (mutation.type) {
case "attributes":
if (mutation.target.hasAttribute("data-l10n-id")) {
this.pendingElements.add(mutation.target);
}
break;
case "childList":
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType === addedNode.ELEMENT_NODE) {
if (addedNode.childElementCount) {
for (const element of this.getTranslatables(addedNode)) {
this.pendingElements.add(element);
}
} else if (addedNode.hasAttribute(L10NID_ATTR_NAME)) {
this.pendingElements.add(addedNode);
}
}
}
break;
}
}
if (this.pendingElements.size > 0) {
if (this.pendingrAF === null) {
this.pendingrAF = this.windowElement.requestAnimationFrame(() => {
this.translateElements(Array.from(this.pendingElements));
this.pendingElements.clear();
this.pendingrAF = null;
});
}
}
}
translateFragment(frag) {
return this.translateElements(this.getTranslatables(frag));
}
async translateElements(elements) {
if (!elements.length) {
return undefined;
}
const keys = elements.map(this.getKeysForElement);
const translations = await this.formatMessages(keys);
return this.applyTranslations(elements, translations);
}
applyTranslations(elements, translations) {
this.pauseObserving();
for (let i = 0; i < elements.length; i++) {
if (translations[i] !== undefined) {
translateElement(elements[i], translations[i]);
}
}
this.resumeObserving();
}
getTranslatables(element) {
const nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY));
if (typeof element.hasAttribute === "function" && element.hasAttribute(L10NID_ATTR_NAME)) {
nodes.push(element);
}
return nodes;
}
getKeysForElement(element) {
return {
id: element.getAttribute(L10NID_ATTR_NAME),
args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null)
};
}
}
;// ./node_modules/@fluent/dom/esm/index.js
;// ./web/l10n.js
class L10n {
#dir;
#elements;
#lang;
#l10n;
constructor({
lang,
isRTL
}, l10n = null) {
this.#lang = L10n.#fixupLangCode(lang);
this.#l10n = l10n;
this.#dir = isRTL ?? L10n.#isRTL(this.#lang) ? "rtl" : "ltr";
}
_setL10n(l10n) {
this.#l10n = l10n;
}
getLanguage() {
return this.#lang;
}
getDirection() {
return this.#dir;
}
async get(ids, args = null, fallback) {
if (Array.isArray(ids)) {
ids = ids.map(id => ({
id
}));
const messages = await this.#l10n.formatMessages(ids);
return messages.map(message => message.value);
}
const messages = await this.#l10n.formatMessages([{
id: ids,
args
}]);
return messages[0]?.value || fallback;
}
async translate(element) {
(this.#elements ||= new Set()).add(element);
try {
this.#l10n.connectRoot(element);
} catch {}
await this.#l10n.translateRoots();
}
async translateOnce(element) {
try {
await this.#l10n.translateElements([element]);
} catch (ex) {
console.error("translateOnce:", ex);
}
}
async destroy() {
if (this.#elements) {
for (const element of this.#elements) {
this.#l10n.disconnectRoot(element);
}
this.#elements.clear();
this.#elements = null;
}
this.#l10n.pauseObserving();
}
pause() {
this.#l10n.pauseObserving();
}
resume() {
this.#l10n.resumeObserving();
}
static #fixupLangCode(langCode) {
langCode = langCode?.toLowerCase() || "en-us";
const PARTIAL_LANG_CODES = {
en: "en-us",
es: "es-es",
fy: "fy-nl",
ga: "ga-ie",
gu: "gu-in",
hi: "hi-in",
hy: "hy-am",
nb: "nb-no",
ne: "ne-np",
nn: "nn-no",
pa: "pa-in",
pt: "pt-pt",
sv: "sv-se",
zh: "zh-cn"
};
return PARTIAL_LANG_CODES[langCode] || langCode;
}
static #isRTL(lang) {
const shortCode = lang.split("-", 1)[0];
return ["ar", "he", "fa", "ps", "ur"].includes(shortCode);
}
}
const GenericL10n = null;
;// ./web/genericl10n.js
function createBundle(lang, text) {
const resource = new FluentResource(text);
const bundle = new FluentBundle(lang);
const errors = bundle.addResource(resource);
if (errors.length) {
console.error("L10n errors", errors);
}
return bundle;
}
class genericl10n_GenericL10n extends L10n {
constructor(lang) {
super({
lang
});
const generateBundles = !lang ? genericl10n_GenericL10n.#generateBundlesFallback.bind(genericl10n_GenericL10n, this.getLanguage()) : genericl10n_GenericL10n.#generateBundles.bind(genericl10n_GenericL10n, "en-us", this.getLanguage());
this._setL10n(new DOMLocalization([], generateBundles));
}
static async *#generateBundles(defaultLang, baseLang) {
const {
baseURL,
paths
} = await this.#getPaths();
const langs = [baseLang];
if (defaultLang !== baseLang) {
const shortLang = baseLang.split("-", 1)[0];
if (shortLang !== baseLang) {
langs.push(shortLang);
}
langs.push(defaultLang);
}
for (const lang of langs) {
const bundle = await this.#createBundle(lang, baseURL, paths);
if (bundle) {
yield bundle;
} else if (lang === "en-us") {
yield this.#createBundleFallback(lang);
}
}
}
static async #createBundle(lang, baseURL, paths) {
const path = paths[lang];
if (!path) {
return null;
}
const url = new URL(path, baseURL);
const text = await (0,pdf.fetchData)(url, "text");
return createBundle(lang, text);
}
static async #getPaths() {
try {
const {
href
} = document.querySelector(`link[type="application/l10n"]`);
const paths = await (0,pdf.fetchData)(href, "json");
return {
baseURL: href.replace(/[^/]*$/, "") || "./",
paths
};
} catch {}
return {
baseURL: "./",
paths: Object.create(null)
};
}
static async *#generateBundlesFallback(lang) {
yield this.#createBundleFallback(lang);
}
static async #createBundleFallback(lang) {
const text = "pdfjs-previous-button =\n .title = Previous Page\npdfjs-previous-button-label = Previous\npdfjs-next-button =\n .title = Next Page\npdfjs-next-button-label = Next\npdfjs-page-input =\n .title = Page\npdfjs-of-pages = of { $pagesCount }\npdfjs-page-of-pages = ({ $pageNumber } of { $pagesCount })\npdfjs-zoom-out-button =\n .title = Zoom Out\npdfjs-zoom-out-button-label = Zoom Out\npdfjs-zoom-in-button =\n .title = Zoom In\npdfjs-zoom-in-button-label = Zoom In\npdfjs-zoom-select =\n .title = Zoom\npdfjs-presentation-mode-button =\n .title = Switch to Presentation Mode\npdfjs-presentation-mode-button-label = Presentation Mode\npdfjs-open-file-button =\n .title = Open File\npdfjs-open-file-button-label = Open\npdfjs-print-button =\n .title = Print\npdfjs-print-button-label = Print\npdfjs-save-button =\n .title = Save\npdfjs-save-button-label = Save\npdfjs-download-button =\n .title = Download\npdfjs-download-button-label = Download\npdfjs-bookmark-button =\n .title = Current Page (View URL from Current Page)\npdfjs-bookmark-button-label = Current Page\npdfjs-tools-button =\n .title = Tools\npdfjs-tools-button-label = Tools\npdfjs-first-page-button =\n .title = Go to First Page\npdfjs-first-page-button-label = Go to First Page\npdfjs-last-page-button =\n .title = Go to Last Page\npdfjs-last-page-button-label = Go to Last Page\npdfjs-page-rotate-cw-button =\n .title = Rotate Clockwise\npdfjs-page-rotate-cw-button-label = Rotate Clockwise\npdfjs-page-rotate-ccw-button =\n .title = Rotate Counterclockwise\npdfjs-page-rotate-ccw-button-label = Rotate Counterclockwise\npdfjs-cursor-text-select-tool-button =\n .title = Enable Text Selection Tool\npdfjs-cursor-text-select-tool-button-label = Text Selection Tool\npdfjs-cursor-hand-tool-button =\n .title = Enable Hand Tool\npdfjs-cursor-hand-tool-button-label = Hand Tool\npdfjs-scroll-page-button =\n .title = Use Page Scrolling\npdfjs-scroll-page-button-label = Page Scrolling\npdfjs-scroll-vertical-button =\n .title = Use Vertical Scrolling\npdfjs-scroll-vertical-button-label = Vertical Scrolling\npdfjs-scroll-horizontal-button =\n .title = Use Horizontal Scrolling\npdfjs-scroll-horizontal-button-label = Horizontal Scrolling\npdfjs-scroll-wrapped-button =\n .title = Use Wrapped Scrolling\npdfjs-scroll-wrapped-button-label = Wrapped Scrolling\npdfjs-spread-none-button =\n .title = Do not join page spreads\npdfjs-spread-none-button-label = No Spreads\npdfjs-spread-odd-button =\n .title = Join page spreads starting with odd-numbered pages\npdfjs-spread-odd-button-label = Odd Spreads\npdfjs-spread-even-button =\n .title = Join page spreads starting with even-numbered pages\npdfjs-spread-even-button-label = Even Spreads\npdfjs-document-properties-button =\n .title = Document Properties\u2026\npdfjs-document-properties-button-label = Document Properties\u2026\npdfjs-document-properties-file-name = File name:\npdfjs-document-properties-file-size = File size:\npdfjs-document-properties-size-kb = { NUMBER($kb, maximumSignificantDigits: 3) } KB ({ $b } bytes)\npdfjs-document-properties-size-mb = { NUMBER($mb, maximumSignificantDigits: 3) } MB ({ $b } bytes)\npdfjs-document-properties-title = Title:\npdfjs-document-properties-author = Author:\npdfjs-document-properties-subject = Subject:\npdfjs-document-properties-keywords = Keywords:\npdfjs-document-properties-creation-date = Creation Date:\npdfjs-document-properties-modification-date = Modification Date:\npdfjs-document-properties-date-time-string = { DATETIME($dateObj, dateStyle: \"short\", timeStyle: \"medium\") }\npdfjs-document-properties-creator = Creator:\npdfjs-document-properties-producer = PDF Producer:\npdfjs-document-properties-version = PDF Version:\npdfjs-document-properties-page-count = Page Count:\npdfjs-document-properties-page-size = Page Size:\npdfjs-document-properties-page-size-unit-inches = in\npdfjs-document-properties-page-size-unit-millimeters = mm\npdfjs-document-properties-page-size-orientation-portrait = portrait\npdfjs-document-properties-page-size-orientation-landscape = landscape\npdfjs-document-properties-page-size-name-a-three = A3\npdfjs-document-properties-page-size-name-a-four = A4\npdfjs-document-properties-page-size-name-letter = Letter\npdfjs-document-properties-page-size-name-legal = Legal\npdfjs-document-properties-page-size-dimension-string = { $width } \xD7 { $height } { $unit } ({ $orientation })\npdfjs-document-properties-page-size-dimension-name-string = { $width } \xD7 { $height } { $unit } ({ $name }, { $orientation })\npdfjs-document-properties-linearized = Fast Web View:\npdfjs-document-properties-linearized-yes = Yes\npdfjs-document-properties-linearized-no = No\npdfjs-document-properties-close-button = Close\npdfjs-print-progress-message = Preparing document for printing\u2026\npdfjs-print-progress-percent = { $progress }%\npdfjs-print-progress-close-button = Cancel\npdfjs-printing-not-supported = Warning: Printing is not fully supported by this browser.\npdfjs-printing-not-ready = Warning: The PDF is not fully loaded for printing.\npdfjs-toggle-sidebar-button =\n .title = Toggle Sidebar\npdfjs-toggle-sidebar-notification-button =\n .title = Toggle Sidebar (document contains outline/attachments/layers)\npdfjs-toggle-sidebar-button-label = Toggle Sidebar\npdfjs-document-outline-button =\n .title = Show Document Outline (double-click to expand/collapse all items)\npdfjs-document-outline-button-label = Document Outline\npdfjs-attachments-button =\n .title = Show Attachments\npdfjs-attachments-button-label = Attachments\npdfjs-layers-button =\n .title = Show Layers (double-click to reset all layers to the default state)\npdfjs-layers-button-label = Layers\npdfjs-thumbs-button =\n .title = Show Thumbnails\npdfjs-thumbs-button-label = Thumbnails\npdfjs-current-outline-item-button =\n .title = Find Current Outline Item\npdfjs-current-outline-item-button-label = Current Outline Item\npdfjs-findbar-button =\n .title = Find in Document\npdfjs-findbar-button-label = Find\npdfjs-additional-layers = Additional Layers\npdfjs-thumb-page-title =\n .title = Page { $page }\npdfjs-thumb-page-canvas =\n .aria-label = Thumbnail of Page { $page }\npdfjs-find-input =\n .title = Find\n .placeholder = Find in document\u2026\npdfjs-find-previous-button =\n .title = Find the previous occurrence of the phrase\npdfjs-find-previous-button-label = Previous\npdfjs-find-next-button =\n .title = Find the next occurrence of the phrase\npdfjs-find-next-button-label = Next\npdfjs-find-highlight-checkbox = Highlight All\npdfjs-find-match-case-checkbox-label = Match Case\npdfjs-find-match-diacritics-checkbox-label = Match Diacritics\npdfjs-find-entire-word-checkbox-label = Whole Words\npdfjs-find-reached-top = Reached top of document, continued from bottom\npdfjs-find-reached-bottom = Reached end of document, continued from top\npdfjs-find-match-count =\n { $total ->\n [one] { $current } of { $total } match\n *[other] { $current } of { $total } matches\n }\npdfjs-find-match-count-limit =\n { $limit ->\n [one] More than { $limit } match\n *[other] More than { $limit } matches\n }\npdfjs-find-not-found = Phrase not found\npdfjs-page-scale-width = Page Width\npdfjs-page-scale-fit = Page Fit\npdfjs-page-scale-auto = Automatic Zoom\npdfjs-page-scale-actual = Actual Size\npdfjs-page-scale-percent = { $scale }%\npdfjs-page-landmark =\n .aria-label = Page { $page }\npdfjs-loading-error = An error occurred while loading the PDF.\npdfjs-invalid-file-error = Invalid or corrupted PDF file.\npdfjs-missing-file-error = Missing PDF file.\npdfjs-unexpected-response-error = Unexpected server response.\npdfjs-rendering-error = An error occurred while rendering the page.\npdfjs-annotation-date-time-string = { DATETIME($dateObj, dateStyle: \"short\", timeStyle: \"medium\") }\npdfjs-text-annotation-type =\n .alt = [{ $type } Annotation]\npdfjs-password-label = Enter the password to open this PDF file.\npdfjs-password-invalid = Invalid password. Please try again.\npdfjs-password-ok-button = OK\npdfjs-password-cancel-button = Cancel\npdfjs-web-fonts-disabled = Web fonts are disabled: unable to use embedded PDF fonts.\npdfjs-editor-free-text-button =\n .title = Text\npdfjs-editor-free-text-button-label = Text\npdfjs-editor-ink-button =\n .title = Draw\npdfjs-editor-ink-button-label = Draw\npdfjs-editor-stamp-button =\n .title = Add or edit images\npdfjs-editor-stamp-button-label = Add or edit images\npdfjs-editor-highlight-button =\n .title = Highlight\npdfjs-editor-highlight-button-label = Highlight\npdfjs-highlight-floating-button1 =\n .title = Highlight\n .aria-label = Highlight\npdfjs-highlight-floating-button-label = Highlight\npdfjs-editor-remove-ink-button =\n .title = Remove drawing\npdfjs-editor-remove-freetext-button =\n .title = Remove text\npdfjs-editor-remove-stamp-button =\n .title = Remove image\npdfjs-editor-remove-highlight-button =\n .title = Remove highlight\npdfjs-editor-free-text-color-input = Color\npdfjs-editor-free-text-size-input = Size\npdfjs-editor-ink-color-input = Color\npdfjs-editor-ink-thickness-input = Thickness\npdfjs-editor-ink-opacity-input = Opacity\npdfjs-editor-stamp-add-image-button =\n .title = Add image\npdfjs-editor-stamp-add-image-button-label = Add image\npdfjs-editor-free-highlight-thickness-input = Thickness\npdfjs-editor-free-highlight-thickness-title =\n .title = Change thickness when highlighting items other than text\npdfjs-free-text2 =\n .aria-label = Text Editor\n .default-content = Start typing\u2026\npdfjs-ink =\n .aria-label = Draw Editor\npdfjs-ink-canvas =\n .aria-label = User-created image\npdfjs-editor-alt-text-button =\n .aria-label = Alt text\npdfjs-editor-alt-text-button-label = Alt text\npdfjs-editor-alt-text-edit-button =\n .aria-label = Edit alt text\npdfjs-editor-alt-text-dialog-label = Choose an option\npdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people can\u2019t see the image or when it doesn\u2019t load.\npdfjs-editor-alt-text-add-description-label = Add a description\npdfjs-editor-alt-text-add-description-description = Aim for 1-2 sentences that describe the subject, setting, or actions.\npdfjs-editor-alt-text-mark-decorative-label = Mark as decorative\npdfjs-editor-alt-text-mark-decorative-description = This is used for ornamental images, like borders or watermarks.\npdfjs-editor-alt-text-cancel-button = Cancel\npdfjs-editor-alt-text-save-button = Save\npdfjs-editor-alt-text-decorative-tooltip = Marked as decorative\npdfjs-editor-alt-text-textarea =\n .placeholder = For example, \u201CA young man sits down at a table to eat a meal\u201D\npdfjs-editor-resizer-top-left =\n .aria-label = Top left corner \u2014 resize\npdfjs-editor-resizer-top-middle =\n .aria-label = Top middle \u2014 resize\npdfjs-editor-resizer-top-right =\n .aria-label = Top right corner \u2014 resize\npdfjs-editor-resizer-middle-right =\n .aria-label = Middle right \u2014 resize\npdfjs-editor-resizer-bottom-right =\n .aria-label = Bottom right corner \u2014 resize\npdfjs-editor-resizer-bottom-middle =\n .aria-label = Bottom middle \u2014 resize\npdfjs-editor-resizer-bottom-left =\n .aria-label = Bottom left corner \u2014 resize\npdfjs-editor-resizer-middle-left =\n .aria-label = Middle left \u2014 resize\npdfjs-editor-highlight-colorpicker-label = Highlight color\npdfjs-editor-colorpicker-button =\n .title = Change color\npdfjs-editor-colorpicker-dropdown =\n .aria-label = Color choices\npdfjs-editor-colorpicker-yellow =\n .title = Yellow\npdfjs-editor-colorpicker-green =\n .title = Green\npdfjs-editor-colorpicker-blue =\n .title = Blue\npdfjs-editor-colorpicker-pink =\n .title = Pink\npdfjs-editor-colorpicker-red =\n .title = Red\npdfjs-editor-highlight-show-all-button-label = Show all\npdfjs-editor-highlight-show-all-button =\n .title = Show all\npdfjs-editor-new-alt-text-dialog-edit-label = Edit alt text (image description)\npdfjs-editor-new-alt-text-dialog-add-label = Add alt text (image description)\npdfjs-editor-new-alt-text-textarea =\n .placeholder = Write your description here\u2026\npdfjs-editor-new-alt-text-description = Short description for people who can\u2019t see the image or when the image doesn\u2019t load.\npdfjs-editor-new-alt-text-disclaimer1 = This alt text was created automatically and may be inaccurate.\npdfjs-editor-new-alt-text-disclaimer-learn-more-url = Learn more\npdfjs-editor-new-alt-text-create-automatically-button-label = Create alt text automatically\npdfjs-editor-new-alt-text-not-now-button = Not now\npdfjs-editor-new-alt-text-error-title = Couldn\u2019t create alt text automatically\npdfjs-editor-new-alt-text-error-description = Please write your own alt text or try again later.\npdfjs-editor-new-alt-text-error-close-button = Close\npdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB)\n .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB)\npdfjs-editor-new-alt-text-added-button =\n .aria-label = Alt text added\npdfjs-editor-new-alt-text-added-button-label = Alt text added\npdfjs-editor-new-alt-text-missing-button =\n .aria-label = Missing alt text\npdfjs-editor-new-alt-text-missing-button-label = Missing alt text\npdfjs-editor-new-alt-text-to-review-button =\n .aria-label = Review alt text\npdfjs-editor-new-alt-text-to-review-button-label = Review alt text\npdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer = Created automatically: { $generatedAltText }\npdfjs-image-alt-text-settings-button =\n .title = Image alt text settings\npdfjs-image-alt-text-settings-button-label = Image alt text settings\npdfjs-editor-alt-text-settings-dialog-label = Image alt text settings\npdfjs-editor-alt-text-settings-automatic-title = Automatic alt text\npdfjs-editor-alt-text-settings-create-model-button-label = Create alt text automatically\npdfjs-editor-alt-text-settings-create-model-description = Suggests descriptions to help people who can\u2019t see the image or when the image doesn\u2019t load.\npdfjs-editor-alt-text-settings-download-model-label = Alt text AI model ({ $totalSize } MB)\npdfjs-editor-alt-text-settings-ai-model-description = Runs locally on your device so your data stays private. Required for automatic alt text.\npdfjs-editor-alt-text-settings-delete-model-button = Delete\npdfjs-editor-alt-text-settings-download-model-button = Download\npdfjs-editor-alt-text-settings-downloading-model-button = Downloading\u2026\npdfjs-editor-alt-text-settings-editor-title = Alt text editor\npdfjs-editor-alt-text-settings-show-dialog-button-label = Show alt text editor right away when adding an image\npdfjs-editor-alt-text-settings-show-dialog-description = Helps you make sure all your images have alt text.\npdfjs-editor-alt-text-settings-close-button = Close\npdfjs-editor-undo-bar-message-highlight = Highlight removed\npdfjs-editor-undo-bar-message-freetext = Text removed\npdfjs-editor-undo-bar-message-ink = Drawing removed\npdfjs-editor-undo-bar-message-stamp = Image removed\npdfjs-editor-undo-bar-message-multiple =\n { $count ->\n [one] { $count } annotation removed\n *[other] { $count } annotations removed\n }\npdfjs-editor-undo-bar-undo-button =\n .title = Undo\npdfjs-editor-undo-bar-undo-button-label = Undo\npdfjs-editor-undo-bar-close-button =\n .title = Close\npdfjs-editor-undo-bar-close-button-label = Close";
return createBundle(lang, text);
}
}
;// ./web/generic_scripting.js
async function docProperties(pdfDocument) {
const url = "",
baseUrl = url.split("#", 1)[0];
let {
info,
metadata,
contentDispositionFilename,
contentLength
} = await pdfDocument.getMetadata();
if (!contentLength) {
const {
length
} = await pdfDocument.getDownloadInfo();
contentLength = length;
}
return {
...info,
baseURL: baseUrl,
filesize: contentLength,
filename: contentDispositionFilename || getPdfFilenameFromUrl(url),
metadata: metadata?.getRaw(),
authors: metadata?.get("dc:creator"),
numPages: pdfDocument.numPages,
URL: url
};
}
class GenericScripting {
constructor(sandboxBundleSrc) {
if (sandboxBundleSrc?.constructor?.name === "Function") {
sandboxBundleSrc = sandboxBundleSrc();
}
this._ready = new Promise((resolve, reject) => {
const sandbox = import(/*webpackIgnore: true*/sandboxBundleSrc);
sandbox.then(pdfjsSandbox => {
resolve(pdfjsSandbox.QuickJSSandbox());
}).catch(reject);
});
}
async createSandbox(data) {
const sandbox = await this._ready;
sandbox.create(data);
}
async dispatchEventInSandbox(event) {
const sandbox = await this._ready;
setTimeout(() => sandbox.dispatchEvent(event), 0);
}
async destroySandbox() {
const sandbox = await this._ready;
sandbox.nukeSandbox();
}
}
;// ./web/genericcom.js
function initCom(app) {}
class Preferences extends BasePreferences {
async _writeToStorage(prefObj) {
try {
localStorage.setItem("pdfjs.preferences", JSON.stringify(prefObj));
} catch (safariSecurityException) {}
}
async _readFromStorage(prefObj) {
try {
return {
prefs: JSON.parse(localStorage.getItem("pdfjs.preferences"))
};
} catch (safariSecurityException) {
return {};
}
}
}
class ExternalServices extends BaseExternalServices {
async createL10n() {
return new genericl10n_GenericL10n(AppOptions.get("localeProperties")?.lang);
}
createScripting() {
return new GenericScripting(AppOptions.get("sandboxBundleSrc"));
}
}
class MLManager {
async isEnabledFor(_name) {
return false;
}
async deleteModel(_service) {
return null;
}
isReady(_name) {
return false;
}
guess(_data) {}
toggleService(_name, _enabled) {}
static getFakeMLManager(options) {
return new FakeMLManager(options);
}
}
class FakeMLManager {
eventBus = null;
hasProgress = false;
constructor({
enableGuessAltText,
enableAltTextModelDownload
}) {
this.enableGuessAltText = enableGuessAltText;
this.enableAltTextModelDownload = enableAltTextModelDownload;
}
setEventBus(eventBus, abortSignal) {
this.eventBus = eventBus;
}
async isEnabledFor(_name) {
return this.enableGuessAltText;
}
async deleteModel(_name) {
this.enableAltTextModelDownload = false;
return null;
}
async loadModel(_name) {}
async downloadModel(_name) {
this.hasProgress = true;
const {
promise,
resolve
} = Promise.withResolvers();
const total = 1e8;
const end = 1.5 * total;
const increment = 5e6;
let loaded = 0;
const id = setInterval(() => {
loaded += increment;
if (loaded <= end) {
this.eventBus.dispatch("loadaiengineprogress", {
source: this,
detail: {
total,
totalLoaded: loaded,
finished: loaded + increment >= end
}
});
return;
}
clearInterval(id);
this.hasProgress = false;
this.enableAltTextModelDownload = true;
resolve(true);
}, 900);
return promise;
}
isReady(_name) {
return this.enableAltTextModelDownload;
}
guess({
request: {
data
}
}) {
return new Promise(resolve => {
setTimeout(() => {
resolve(data ? {
output: "Fake alt text."
} : {
error: true
});
}, 3000);
});
}
toggleService(_name, enabled) {
this.enableGuessAltText = enabled;
}
}
;// ./web/new_alt_text_manager.js
class NewAltTextManager {
#boundCancel = this.#cancel.bind(this);
#createAutomaticallyButton;
#currentEditor = null;
#cancelButton;
#descriptionContainer;
#dialog;
#disclaimer;
#downloadModel;
#downloadModelDescription;
#eventBus;
#firstTime = false;
#guessedAltText;
#hasAI = null;
#isEditing = null;
#imagePreview;
#imageData;
#isAILoading = false;
#wasAILoading = false;
#learnMore;
#notNowButton;
#overlayManager;
#textarea;
#title;
#uiManager;
#previousAltText = null;
constructor({
descriptionContainer,
dialog,
imagePreview,
cancelButton,
disclaimer,
notNowButton,
saveButton,
textarea,
learnMore,
errorCloseButton,
createAutomaticallyButton,
downloadModel,
downloadModelDescription,
title
}, overlayManager, eventBus) {
this.#cancelButton = cancelButton;
this.#createAutomaticallyButton = createAutomaticallyButton;
this.#descriptionContainer = descriptionContainer;
this.#dialog = dialog;
this.#disclaimer = disclaimer;
this.#notNowButton = notNowButton;
this.#imagePreview = imagePreview;
this.#textarea = textarea;
this.#learnMore = learnMore;
this.#title = title;
this.#downloadModel = downloadModel;
this.#downloadModelDescription = downloadModelDescription;
this.#overlayManager = overlayManager;
this.#eventBus = eventBus;
dialog.addEventListener("close", this.#close.bind(this));
dialog.addEventListener("contextmenu", event => {
if (event.target !== this.#textarea) {
event.preventDefault();
}
});
cancelButton.addEventListener("click", this.#boundCancel);
notNowButton.addEventListener("click", this.#boundCancel);
saveButton.addEventListener("click", this.#save.bind(this));
errorCloseButton.addEventListener("click", () => {
this.#toggleError(false);
});
createAutomaticallyButton.addEventListener("click", async () => {
const checked = createAutomaticallyButton.getAttribute("aria-pressed") !== "true";
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.alt_text.ai_generation_check",
data: {
status: checked
}
});
if (this.#uiManager) {
this.#uiManager.setPreference("enableGuessAltText", checked);
await this.#uiManager.mlManager.toggleService("altText", checked);
}
this.#toggleGuessAltText(checked, false);
});
textarea.addEventListener("focus", () => {
this.#wasAILoading = this.#isAILoading;
this.#toggleLoading(false);
this.#toggleTitleAndDisclaimer();
});
textarea.addEventListener("blur", () => {
if (!textarea.value) {
this.#toggleLoading(this.#wasAILoading);
}
this.#toggleTitleAndDisclaimer();
});
textarea.addEventListener("input", () => {
this.#toggleTitleAndDisclaimer();
});
eventBus._on("enableguessalttext", ({
value
}) => {
this.#toggleGuessAltText(value, false);
});
this.#overlayManager.register(dialog);
this.#learnMore.addEventListener("click", () => {
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.alt_text.info",
data: {
topic: "alt_text"
}
});
});
}
#toggleLoading(value) {
if (!this.#uiManager || this.#isAILoading === value) {
return;
}
this.#isAILoading = value;
this.#descriptionContainer.classList.toggle("loading", value);
}
#toggleError(value) {
if (!this.#uiManager) {
return;
}
this.#dialog.classList.toggle("error", value);
}
async #toggleGuessAltText(value, isInitial = false) {
if (!this.#uiManager) {
return;
}
this.#dialog.classList.toggle("aiDisabled", !value);
this.#createAutomaticallyButton.setAttribute("aria-pressed", value);
if (value) {
const {
altTextLearnMoreUrl
} = this.#uiManager.mlManager;
if (altTextLearnMoreUrl) {
this.#learnMore.href = altTextLearnMoreUrl;
}
this.#mlGuessAltText(isInitial);
} else {
this.#toggleLoading(false);
this.#isAILoading = false;
this.#toggleTitleAndDisclaimer();
}
}
#toggleNotNow() {
this.#notNowButton.classList.toggle("hidden", !this.#firstTime);
this.#cancelButton.classList.toggle("hidden", this.#firstTime);
}
#toggleAI(value) {
if (!this.#uiManager || this.#hasAI === value) {
return;
}
this.#hasAI = value;
this.#dialog.classList.toggle("noAi", !value);
this.#toggleTitleAndDisclaimer();
}
#toggleTitleAndDisclaimer() {
const visible = this.#isAILoading || this.#guessedAltText && this.#guessedAltText === this.#textarea.value;
this.#disclaimer.hidden = !visible;
const isEditing = this.#isAILoading || !!this.#textarea.value;
if (this.#isEditing === isEditing) {
return;
}
this.#isEditing = isEditing;
this.#title.setAttribute("data-l10n-id", isEditing ? "pdfjs-editor-new-alt-text-dialog-edit-label" : "pdfjs-editor-new-alt-text-dialog-add-label");
}
async #mlGuessAltText(isInitial) {
if (this.#isAILoading) {
return;
}
if (this.#textarea.value) {
return;
}
if (isInitial && this.#previousAltText !== null) {
return;
}
this.#guessedAltText = this.#currentEditor.guessedAltText;
if (this.#previousAltText === null && this.#guessedAltText) {
this.#addAltText(this.#guessedAltText);
return;
}
this.#toggleLoading(true);
this.#toggleTitleAndDisclaimer();
let hasError = false;
try {
const altText = await this.#currentEditor.mlGuessAltText(this.#imageData, false);
if (altText) {
this.#guessedAltText = altText;
this.#wasAILoading = this.#isAILoading;
if (this.#isAILoading) {
this.#addAltText(altText);
}
}
} catch (e) {
console.error(e);
hasError = true;
}
this.#toggleLoading(false);
this.#toggleTitleAndDisclaimer();
if (hasError && this.#uiManager) {
this.#toggleError(true);
}
}
#addAltText(altText) {
if (!this.#uiManager || this.#textarea.value) {
return;
}
this.#textarea.value = altText;
this.#toggleTitleAndDisclaimer();
}
#setProgress() {
this.#downloadModel.classList.toggle("hidden", false);
const callback = async ({
detail: {
finished,
total,
totalLoaded
}
}) => {
const ONE_MEGA_BYTES = 1e6;
totalLoaded = Math.min(0.99 * total, totalLoaded);
const totalSize = this.#downloadModelDescription.ariaValueMax = Math.round(total / ONE_MEGA_BYTES);
const downloadedSize = this.#downloadModelDescription.ariaValueNow = Math.round(totalLoaded / ONE_MEGA_BYTES);
this.#downloadModelDescription.setAttribute("data-l10n-args", JSON.stringify({
totalSize,
downloadedSize
}));
if (!finished) {
return;
}
this.#eventBus._off("loadaiengineprogress", callback);
this.#downloadModel.classList.toggle("hidden", true);
this.#toggleAI(true);
if (!this.#uiManager) {
return;
}
const {
mlManager
} = this.#uiManager;
mlManager.toggleService("altText", true);
this.#toggleGuessAltText(await mlManager.isEnabledFor("altText"), true);
};
this.#eventBus._on("loadaiengineprogress", callback);
}
async editAltText(uiManager, editor, firstTime) {
if (this.#currentEditor || !editor) {
return;
}
if (firstTime && editor.hasAltTextData()) {
editor.altTextFinish();
return;
}
this.#firstTime = firstTime;
let {
mlManager
} = uiManager;
let hasAI = !!mlManager;
this.#toggleTitleAndDisclaimer();
if (mlManager && !mlManager.isReady("altText")) {
hasAI = false;
if (mlManager.hasProgress) {
this.#setProgress();
} else {
mlManager = null;
}
} else {
this.#downloadModel.classList.toggle("hidden", true);
}
const isAltTextEnabledPromise = mlManager?.isEnabledFor("altText");
this.#currentEditor = editor;
this.#uiManager = uiManager;
this.#uiManager.removeEditListeners();
({
altText: this.#previousAltText
} = editor.altTextData);
this.#textarea.value = this.#previousAltText ?? "";
const AI_MAX_IMAGE_DIMENSION = 224;
const MAX_PREVIEW_DIMENSION = 180;
let canvas, width, height;
if (mlManager) {
({
canvas,
width,
height,
imageData: this.#imageData
} = editor.copyCanvas(AI_MAX_IMAGE_DIMENSION, MAX_PREVIEW_DIMENSION, true));
if (hasAI) {
this.#toggleGuessAltText(await isAltTextEnabledPromise, true);
}
} else {
({
canvas,
width,
height
} = editor.copyCanvas(AI_MAX_IMAGE_DIMENSION, MAX_PREVIEW_DIMENSION, false));
}
canvas.setAttribute("role", "presentation");
const {
style
} = canvas;
style.width = `${width}px`;
style.height = `${height}px`;
this.#imagePreview.append(canvas);
this.#toggleNotNow();
this.#toggleAI(hasAI);
this.#toggleError(false);
try {
await this.#overlayManager.open(this.#dialog);
} catch (ex) {
this.#close();
throw ex;
}
}
#cancel() {
this.#currentEditor.altTextData = {
cancel: true
};
const altText = this.#textarea.value.trim();
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.alt_text.dismiss",
data: {
alt_text_type: altText ? "present" : "empty",
flow: this.#firstTime ? "image_add" : "alt_text_edit"
}
});
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.image_added",
data: {
alt_text_modal: true,
alt_text_type: "skipped"
}
});
this.#finish();
}
#finish() {
if (this.#overlayManager.active === this.#dialog) {
this.#overlayManager.close(this.#dialog);
}
}
#close() {
const canvas = this.#imagePreview.firstChild;
canvas.remove();
canvas.width = canvas.height = 0;
this.#imageData = null;
this.#toggleLoading(false);
this.#uiManager?.addEditListeners();
this.#currentEditor.altTextFinish();
this.#uiManager?.setSelected(this.#currentEditor);
this.#currentEditor = null;
this.#uiManager = null;
}
#extractWords(text) {
return new Set(text.toLowerCase().split(/[^\p{L}\p{N}]+/gu).filter(x => !!x));
}
#save() {
const altText = this.#textarea.value.trim();
this.#currentEditor.altTextData = {
altText,
decorative: false
};
this.#currentEditor.altTextData.guessedAltText = this.#guessedAltText;
if (this.#guessedAltText && this.#guessedAltText !== altText) {
const guessedWords = this.#extractWords(this.#guessedAltText);
const words = this.#extractWords(altText);
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.alt_text.user_edit",
data: {
total_words: guessedWords.size,
words_removed: guessedWords.difference(words).size,
words_added: words.difference(guessedWords).size
}
});
}
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.image_added",
data: {
alt_text_modal: true,
alt_text_type: altText ? "present" : "empty"
}
});
this.#currentEditor._reportTelemetry({
action: "pdfjs.image.alt_text.save",
data: {
alt_text_type: altText ? "present" : "empty",
flow: this.#firstTime ? "image_add" : "alt_text_edit"
}
});
this.#finish();
}
destroy() {
this.#uiManager = null;
this.#finish();
}
}
class ImageAltTextSettings {
#aiModelSettings;
#createModelButton;
#downloadModelButton;
#dialog;
#eventBus;
#mlManager;
#overlayManager;
#showAltTextDialogButton;
constructor({
dialog,
createModelButton,
aiModelSettings,
learnMore,
closeButton,
deleteModelButton,
downloadModelButton,
showAltTextDialogButton
}, overlayManager, eventBus, mlManager) {
this.#dialog = dialog;
this.#aiModelSettings = aiModelSettings;
this.#createModelButton = createModelButton;
this.#downloadModelButton = downloadModelButton;
this.#showAltTextDialogButton = showAltTextDialogButton;
this.#overlayManager = overlayManager;
this.#eventBus = eventBus;
this.#mlManager = mlManager;
const {
altTextLearnMoreUrl
} = mlManager;
if (altTextLearnMoreUrl) {
learnMore.href = altTextLearnMoreUrl;
}
dialog.addEventListener("contextmenu", display_utils.noContextMenu);
createModelButton.addEventListener("click", async e => {
const checked = this.#togglePref("enableGuessAltText", e);
await mlManager.toggleService("altText", checked);
this.#reportTelemetry({
type: "stamp",
action: "pdfjs.image.alt_text.settings_ai_generation_check",
data: {
status: checked
}
});
});
showAltTextDialogButton.addEventListener("click", e => {
const checked = this.#togglePref("enableNewAltTextWhenAddingImage", e);
this.#reportTelemetry({
type: "stamp",
action: "pdfjs.image.alt_text.settings_edit_alt_text_check",
data: {
status: checked
}
});
});
deleteModelButton.addEventListener("click", this.#delete.bind(this, true));
downloadModelButton.addEventListener("click", this.#download.bind(this, true));
closeButton.addEventListener("click", this.#finish.bind(this));
learnMore.addEventListener("click", () => {
this.#reportTelemetry({
type: "stamp",
action: "pdfjs.image.alt_text.info",
data: {
topic: "ai_generation"
}
});
});
eventBus._on("enablealttextmodeldownload", ({
value
}) => {
if (value) {
this.#download(false);
} else {
this.#delete(false);
}
});
this.#overlayManager.register(dialog);
}
#reportTelemetry(data) {
this.#eventBus.dispatch("reporttelemetry", {
source: this,
details: {
type: "editing",
data
}
});
}
async #download(isFromUI = false) {
if (isFromUI) {
this.#downloadModelButton.disabled = true;
const span = this.#downloadModelButton.firstChild;
span.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-settings-downloading-model-button");
await this.#mlManager.downloadModel("altText");
span.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-settings-download-model-button");
this.#createModelButton.disabled = false;
this.#setPref("enableGuessAltText", true);
this.#mlManager.toggleService("altText", true);
this.#setPref("enableAltTextModelDownload", true);
this.#downloadModelButton.disabled = false;
}
this.#aiModelSettings.classList.toggle("download", false);
this.#createModelButton.setAttribute("aria-pressed", true);
}
async #delete(isFromUI = false) {
if (isFromUI) {
await this.#mlManager.deleteModel("altText");
this.#setPref("enableGuessAltText", false);
this.#setPref("enableAltTextModelDownload", false);
}
this.#aiModelSettings.classList.toggle("download", true);
this.#createModelButton.disabled = true;
this.#createModelButton.setAttribute("aria-pressed", false);
}
async open({
enableGuessAltText,
enableNewAltTextWhenAddingImage
}) {
const {
enableAltTextModelDownload
} = this.#mlManager;
this.#createModelButton.disabled = !enableAltTextModelDownload;
this.#createModelButton.setAttribute("aria-pressed", enableAltTextModelDownload && enableGuessAltText);
this.#showAltTextDialogButton.setAttribute("aria-pressed", enableNewAltTextWhenAddingImage);
this.#aiModelSettings.classList.toggle("download", !enableAltTextModelDownload);
await this.#overlayManager.open(this.#dialog);
this.#reportTelemetry({
type: "stamp",
action: "pdfjs.image.alt_text.settings_displayed"
});
}
#togglePref(name, {
target
}) {
const checked = target.getAttribute("aria-pressed") !== "true";
this.#setPref(name, checked);
target.setAttribute("aria-pressed", checked);
return checked;
}
#setPref(name, value) {
this.#eventBus.dispatch("setpreference", {
source: this,
name,
value
});
}
#finish() {
if (this.#overlayManager.active === this.#dialog) {
this.#overlayManager.close(this.#dialog);
}
}
}
;// ./web/alt_text_manager.js
class AltTextManager {
#clickAC = null;
#currentEditor = null;
#cancelButton;
#dialog;
#eventBus;
#hasUsedPointer = false;
#optionDescription;
#optionDecorative;
#overlayManager;
#saveButton;
#textarea;
#uiManager;
#previousAltText = null;
#resizeAC = null;
#svgElement = null;
#rectElement = null;
#container;
#telemetryData = null;
constructor({
dialog,
optionDescription,
optionDecorative,
textarea,
cancelButton,
saveButton
}, container, overlayManager, eventBus) {
this.#dialog = dialog;
this.#optionDescription = optionDescription;
this.#optionDecorative = optionDecorative;
this.#textarea = textarea;
this.#cancelButton = cancelButton;
this.#saveButton = saveButton;
this.#overlayManager = overlayManager;
this.#eventBus = eventBus;
this.#container = container;
const onUpdateUIState = this.#updateUIState.bind(this);
dialog.addEventListener("close", this.#close.bind(this));
dialog.addEventListener("contextmenu", event => {
if (event.target !== this.#textarea) {
event.preventDefault();
}
});
cancelButton.addEventListener("click", this.#finish.bind(this));
saveButton.addEventListener("click", this.#save.bind(this));
optionDescription.addEventListener("change", onUpdateUIState);
optionDecorative.addEventListener("change", onUpdateUIState);
this.#overlayManager.register(dialog);
}
#createSVGElement() {
if (this.#svgElement) {
return;
}
const svgFactory = new pdf.DOMSVGFactory();
const svg = this.#svgElement = svgFactory.createElement("svg");
svg.setAttribute("width", "0");
svg.setAttribute("height", "0");
const defs = svgFactory.createElement("defs");
svg.append(defs);
const mask = svgFactory.createElement("mask");
defs.append(mask);
mask.setAttribute("id", "alttext-manager-mask");
mask.setAttribute("maskContentUnits", "objectBoundingBox");
let rect = svgFactory.createElement("rect");
mask.append(rect);
rect.setAttribute("fill", "white");
rect.setAttribute("width", "1");
rect.setAttribute("height", "1");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect = this.#rectElement = svgFactory.createElement("rect");
mask.append(rect);
rect.setAttribute("fill", "black");
this.#dialog.append(svg);
}
async editAltText(uiManager, editor) {
if (this.#currentEditor || !editor) {
return;
}
this.#createSVGElement();
this.#hasUsedPointer = false;
this.#clickAC = new AbortController();
const clickOpts = {
signal: this.#clickAC.signal
},
onClick = this.#onClick.bind(this);
for (const element of [this.#optionDescription, this.#optionDecorative, this.#textarea, this.#saveButton, this.#cancelButton]) {
element.addEventListener("click", onClick, clickOpts);
}
const {
altText,
decorative
} = editor.altTextData;
if (decorative === true) {
this.#optionDecorative.checked = true;
this.#optionDescription.checked = false;
} else {
this.#optionDecorative.checked = false;
this.#optionDescription.checked = true;
}
this.#previousAltText = this.#textarea.value = altText?.trim() || "";
this.#updateUIState();
this.#currentEditor = editor;
this.#uiManager = uiManager;
this.#uiManager.removeEditListeners();
this.#resizeAC = new AbortController();
this.#eventBus._on("resize", this.#setPosition.bind(this), {
signal: this.#resizeAC.signal
});
try {
await this.#overlayManager.open(this.#dialog);
this.#setPosition();
} catch (ex) {
this.#close();
throw ex;
}
}
#setPosition() {
if (!this.#currentEditor) {
return;
}
const dialog = this.#dialog;
const {
style
} = dialog;
const {
x: containerX,
y: containerY,
width: containerW,
height: containerH
} = this.#container.getBoundingClientRect();
const {
innerWidth: windowW,
innerHeight: windowH
} = window;
const {
width: dialogW,
height: dialogH
} = dialog.getBoundingClientRect();
const {
x,
y,
width,
height
} = this.#currentEditor.getClientDimensions();
const MARGIN = 10;
const isLTR = this.#uiManager.direction === "ltr";
const xs = Math.max(x, containerX);
const xe = Math.min(x + width, containerX + containerW);
const ys = Math.max(y, containerY);
const ye = Math.min(y + height, containerY + containerH);
this.#rectElement.setAttribute("width", `${(xe - xs) / windowW}`);
this.#rectElement.setAttribute("height", `${(ye - ys) / windowH}`);
this.#rectElement.setAttribute("x", `${xs / windowW}`);
this.#rectElement.setAttribute("y", `${ys / windowH}`);
let left = null;
let top = Math.max(y, 0);
top += Math.min(windowH - (top + dialogH), 0);
if (isLTR) {
if (x + width + MARGIN + dialogW < windowW) {
left = x + width + MARGIN;
} else if (x > dialogW + MARGIN) {
left = x - dialogW - MARGIN;
}
} else if (x > dialogW + MARGIN) {
left = x - dialogW - MARGIN;
} else if (x + width + MARGIN + dialogW < windowW) {
left = x + width + MARGIN;
}
if (left === null) {
top = null;
left = Math.max(x, 0);
left += Math.min(windowW - (left + dialogW), 0);
if (y > dialogH + MARGIN) {
top = y - dialogH - MARGIN;
} else if (y + height + MARGIN + dialogH < windowH) {
top = y + height + MARGIN;
}
}
if (top !== null) {
dialog.classList.add("positioned");
if (isLTR) {
style.left = `${left}px`;
} else {
style.right = `${windowW - left - dialogW}px`;
}
style.top = `${top}px`;
} else {
dialog.classList.remove("positioned");
style.left = "";
style.top = "";
}
}
#finish() {
if (this.#overlayManager.active === this.#dialog) {
this.#overlayManager.close(this.#dialog);
}
}
#close() {
this.#currentEditor._reportTelemetry(this.#telemetryData || {
action: "alt_text_cancel",
alt_text_keyboard: !this.#hasUsedPointer
});
this.#telemetryData = null;
this.#removeOnClickListeners();
this.#uiManager?.addEditListeners();
this.#resizeAC?.abort();
this.#resizeAC = null;
this.#currentEditor.altTextFinish();
this.#currentEditor = null;
this.#uiManager = null;
}
#updateUIState() {
this.#textarea.disabled = this.#optionDecorative.checked;
}
#save() {
const altText = this.#textarea.value.trim();
const decorative = this.#optionDecorative.checked;
this.#currentEditor.altTextData = {
altText,
decorative
};
this.#telemetryData = {
action: "alt_text_save",
alt_text_description: !!altText,
alt_text_edit: !!this.#previousAltText && this.#previousAltText !== altText,
alt_text_decorative: decorative,
alt_text_keyboard: !this.#hasUsedPointer
};
this.#finish();
}
#onClick(evt) {
if (evt.detail === 0) {
return;
}
this.#hasUsedPointer = true;
this.#removeOnClickListeners();
}
#removeOnClickListeners() {
this.#clickAC?.abort();
this.#clickAC = null;
}
destroy() {
this.#uiManager = null;
this.#finish();
this.#svgElement?.remove();
this.#svgElement = this.#rectElement = null;
}
}
;// ./web/annotation_editor_params.js
class AnnotationEditorParams {
constructor(options, eventBus) {
this.eventBus = eventBus;
this.#bindListeners(options);
}
#bindListeners({
editorFreeTextFontSize,
editorFreeTextColor,
editorInkColor,
editorInkThickness,
editorInkOpacity,
editorStampAddImage,
editorFreeHighlightThickness,
editorHighlightShowAll
}) {
const dispatchEvent = (typeStr, value) => {
this.eventBus.dispatch("switchannotationeditorparams", {
source: this,
type: util.AnnotationEditorParamsType[typeStr],
value
});
};
editorFreeTextFontSize.addEventListener("input", function () {
dispatchEvent("FREETEXT_SIZE", this.valueAsNumber);
});
editorFreeTextColor.addEventListener("input", function () {
dispatchEvent("FREETEXT_COLOR", this.value);
});
editorInkColor.addEventListener("input", function () {
dispatchEvent("INK_COLOR", this.value);
});
editorInkThickness.addEventListener("input", function () {
dispatchEvent("INK_THICKNESS", this.valueAsNumber);
});
editorInkOpacity.addEventListener("input", function () {
dispatchEvent("INK_OPACITY", this.valueAsNumber);
});
editorStampAddImage.addEventListener("click", () => {
this.eventBus.dispatch("reporttelemetry", {
source: this,
details: {
type: "editing",
data: {
action: "pdfjs.image.add_image_click"
}
}
});
dispatchEvent("CREATE");
});
editorFreeHighlightThickness.addEventListener("input", function () {
dispatchEvent("HIGHLIGHT_THICKNESS", this.valueAsNumber);
});
editorHighlightShowAll.addEventListener("click", function () {
const checked = this.getAttribute("aria-pressed") === "true";
this.setAttribute("aria-pressed", !checked);
dispatchEvent("HIGHLIGHT_SHOW_ALL", !checked);
});
this.eventBus._on("annotationeditorparamschanged", evt => {
for (const [type, value] of evt.details) {
switch (type) {
case util.AnnotationEditorParamsType.FREETEXT_SIZE:
editorFreeTextFontSize.value = value;
break;
case util.AnnotationEditorParamsType.FREETEXT_COLOR:
editorFreeTextColor.value = value;
break;
case util.AnnotationEditorParamsType.INK_COLOR:
editorInkColor.value = value;
break;
case util.AnnotationEditorParamsType.INK_THICKNESS:
editorInkThickness.value = value;
break;
case util.AnnotationEditorParamsType.INK_OPACITY:
editorInkOpacity.value = value;
break;
case util.AnnotationEditorParamsType.HIGHLIGHT_THICKNESS:
editorFreeHighlightThickness.value = value;
break;
case util.AnnotationEditorParamsType.HIGHLIGHT_FREE:
editorFreeHighlightThickness.disabled = !value;
break;
case util.AnnotationEditorParamsType.HIGHLIGHT_SHOW_ALL:
editorHighlightShowAll.setAttribute("aria-pressed", value);
break;
}
}
});
}
}
;// ./web/caret_browsing.js
const PRECISION = 1e-1;
class CaretBrowsingMode {
#mainContainer;
#toolBarHeight = 0;
#viewerContainer;
constructor(abortSignal, mainContainer, viewerContainer, toolbarContainer) {
this.#mainContainer = mainContainer;
this.#viewerContainer = viewerContainer;
if (!toolbarContainer) {
return;
}
this.#toolBarHeight = toolbarContainer.getBoundingClientRect().height;
const toolbarObserver = new ResizeObserver(entries => {
for (const entry of entries) {
if (entry.target === toolbarContainer) {
this.#toolBarHeight = Math.floor(entry.borderBoxSize[0].blockSize);
break;
}
}
});
toolbarObserver.observe(toolbarContainer);
abortSignal.addEventListener("abort", () => toolbarObserver.disconnect(), {
once: true
});
}
#isOnSameLine(rect1, rect2) {
const top1 = rect1.y;
const bot1 = rect1.bottom;
const mid1 = rect1.y + rect1.height / 2;
const top2 = rect2.y;
const bot2 = rect2.bottom;
const mid2 = rect2.y + rect2.height / 2;
return top1 <= mid2 && mid2 <= bot1 || top2 <= mid1 && mid1 <= bot2;
}
#isUnderOver(rect, x, y, isUp) {
const midY = rect.y + rect.height / 2;
return (isUp ? y >= midY : y <= midY) && rect.x - PRECISION <= x && x <= rect.right + PRECISION;
}
#isVisible(rect) {
return rect.top >= this.#toolBarHeight && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth);
}
#getCaretPosition(selection, isUp) {
const {
focusNode,
focusOffset
} = selection;
const range = document.createRange();
range.setStart(focusNode, focusOffset);
range.setEnd(focusNode, focusOffset);
const rect = range.getBoundingClientRect();
return [rect.x, isUp ? rect.top : rect.bottom];
}
static #caretPositionFromPoint(x, y) {
if (!document.caretPositionFromPoint) {
const {
startContainer: offsetNode,
startOffset: offset
} = document.caretRangeFromPoint(x, y);
return {
offsetNode,
offset
};
}
return document.caretPositionFromPoint(x, y);
}
#setCaretPositionHelper(selection, caretX, select, element, rect) {
rect ||= element.getBoundingClientRect();
if (caretX <= rect.x + PRECISION) {
if (select) {
selection.extend(element.firstChild, 0);
} else {
selection.setPosition(element.firstChild, 0);
}
return;
}
if (rect.right - PRECISION <= caretX) {
const {
lastChild
} = element;
if (select) {
selection.extend(lastChild, lastChild.length);
} else {
selection.setPosition(lastChild, lastChild.length);
}
return;
}
const midY = rect.y + rect.height / 2;
let caretPosition = CaretBrowsingMode.#caretPositionFromPoint(caretX, midY);
let parentElement = caretPosition.offsetNode?.parentElement;
if (parentElement && parentElement !== element) {
const elementsAtPoint = document.elementsFromPoint(caretX, midY);
const savedVisibilities = [];
for (const el of elementsAtPoint) {
if (el === element) {
break;
}
const {
style
} = el;
savedVisibilities.push([el, style.visibility]);
style.visibility = "hidden";
}
caretPosition = CaretBrowsingMode.#caretPositionFromPoint(caretX, midY);
parentElement = caretPosition.offsetNode?.parentElement;
for (const [el, visibility] of savedVisibilities) {
el.style.visibility = visibility;
}
}
if (parentElement !== element) {
if (select) {
selection.extend(element.firstChild, 0);
} else {
selection.setPosition(element.firstChild, 0);
}
return;
}
if (select) {
selection.extend(caretPosition.offsetNode, caretPosition.offset);
} else {
selection.setPosition(caretPosition.offsetNode, caretPosition.offset);
}
}
#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX) {
if (this.#isVisible(newLineElementRect)) {
this.#setCaretPositionHelper(selection, caretX, select, newLineElement, newLineElementRect);
return;
}
this.#mainContainer.addEventListener("scrollend", this.#setCaretPositionHelper.bind(this, selection, caretX, select, newLineElement, null), {
once: true
});
newLineElement.scrollIntoView();
}
#getNodeOnNextPage(textLayer, isUp) {
while (true) {
const page = textLayer.closest(".page");
const pageNumber = parseInt(page.getAttribute("data-page-number"));
const nextPage = isUp ? pageNumber - 1 : pageNumber + 1;
textLayer = this.#viewerContainer.querySelector(`.page[data-page-number="${nextPage}"] .textLayer`);
if (!textLayer) {
return null;
}
const walker = document.createTreeWalker(textLayer, NodeFilter.SHOW_TEXT);
const node = isUp ? walker.lastChild() : walker.firstChild();
if (node) {
return node;
}
}
}
moveCaret(isUp, select) {
const selection = document.getSelection();
if (selection.rangeCount === 0) {
return;
}
const {
focusNode
} = selection;
const focusElement = focusNode.nodeType !== Node.ELEMENT_NODE ? focusNode.parentElement : focusNode;
const root = focusElement.closest(".textLayer");
if (!root) {
return;
}
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
walker.currentNode = focusNode;
const focusRect = focusElement.getBoundingClientRect();
let newLineElement = null;
const nodeIterator = (isUp ? walker.previousSibling : walker.nextSibling).bind(walker);
while (nodeIterator()) {
const element = walker.currentNode.parentElement;
if (!this.#isOnSameLine(focusRect, element.getBoundingClientRect())) {
newLineElement = element;
break;
}
}
if (!newLineElement) {
const node = this.#getNodeOnNextPage(root, isUp);
if (!node) {
return;
}
if (select) {
const lastNode = (isUp ? walker.firstChild() : walker.lastChild()) || focusNode;
selection.extend(lastNode, isUp ? 0 : lastNode.length);
const range = document.createRange();
range.setStart(node, isUp ? node.length : 0);
range.setEnd(node, isUp ? node.length : 0);
selection.addRange(range);
return;
}
const [caretX] = this.#getCaretPosition(selection, isUp);
const {
parentElement
} = node;
this.#setCaretPosition(select, selection, parentElement, parentElement.getBoundingClientRect(), caretX);
return;
}
const [caretX, caretY] = this.#getCaretPosition(selection, isUp);
const newLineElementRect = newLineElement.getBoundingClientRect();
if (this.#isUnderOver(newLineElementRect, caretX, caretY, isUp)) {
this.#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX);
return;
}
while (nodeIterator()) {
const element = walker.currentNode.parentElement;
const elementRect = element.getBoundingClientRect();
if (!this.#isOnSameLine(newLineElementRect, elementRect)) {
break;
}
if (this.#isUnderOver(elementRect, caretX, caretY, isUp)) {
this.#setCaretPosition(select, selection, element, elementRect, caretX);
return;
}
}
this.#setCaretPosition(select, selection, newLineElement, newLineElementRect, caretX);
}
}
;// ./web/download_manager.js
function download(blobUrl, filename) {
const a = document.createElement("a");
if (!a.click) {
throw new Error('DownloadManager: "a.click()" is not supported.');
}
a.href = blobUrl;
a.target = "_parent";
if ("download" in a) {
a.download = filename;
}
(document.body || document.documentElement).append(a);
a.click();
a.remove();
}
class DownloadManager {
#openBlobUrls = new WeakMap();
downloadData(data, filename, contentType) {
const blobUrl = URL.createObjectURL(new Blob([data], {
type: contentType
}));
download(blobUrl, filename);
}
openOrDownloadData(data, filename, dest = null) {
const isPdfData = (0,display_utils.isPdfFile)(filename);
const contentType = isPdfData ? "application/pdf" : "";
if (isPdfData) {
let blobUrl = this.#openBlobUrls.get(data);
if (!blobUrl) {
blobUrl = URL.createObjectURL(new Blob([data], {
type: contentType
}));
this.#openBlobUrls.set(data, blobUrl);
}
try {
window.open(blobUrl);
return true;
} catch (ex) {
ngx_console.NgxConsole.error("openOrDownloadData:", ex);
URL.revokeObjectURL(blobUrl);
this.#openBlobUrls.delete(data);
}
}
this.downloadData(data, filename, contentType);
return false;
}
download(data, url, filename) {
let blobUrl;
if (data) {
blobUrl = URL.createObjectURL(new Blob([data], {
type: "application/pdf"
}));
} else {
if (!(0,util.createValidAbsoluteUrl)(url, "http://example.com")) {
console.error(`download - not a valid URL: ${url}`);
return;
}
blobUrl = url + "#pdfjs.action=download";
}
download(blobUrl, filename);
}
}
;// ./web/editor_undo_bar.js
class EditorUndoBar {
#closeButton = null;
#container;
#eventBus = null;
#focusTimeout = null;
#initController = null;
isOpen = false;
#message;
#showController = null;
#undoButton;
static #l10nMessages = Object.freeze({
highlight: "pdfjs-editor-undo-bar-message-highlight",
freetext: "pdfjs-editor-undo-bar-message-freetext",
stamp: "pdfjs-editor-undo-bar-message-stamp",
ink: "pdfjs-editor-undo-bar-message-ink",
_multiple: "pdfjs-editor-undo-bar-message-multiple"
});
constructor({
container,
message,
undoButton,
closeButton
}, eventBus) {
this.#container = container;
this.#message = message;
this.#undoButton = undoButton;
this.#closeButton = closeButton;
this.#eventBus = eventBus;
}
destroy() {
this.#initController?.abort();
this.#initController = null;
this.hide();
}
show(undoAction, messageData) {
if (!this.#initController) {
this.#initController = new AbortController();
const opts = {
signal: this.#initController.signal
};
const boundHide = this.hide.bind(this);
this.#container.addEventListener("contextmenu", display_utils.noContextMenu, opts);
this.#closeButton.addEventListener("click", boundHide, opts);
this.#eventBus._on("beforeprint", boundHide, opts);
this.#eventBus._on("download", boundHide, opts);
}
this.hide();
if (typeof messageData === "string") {
this.#message.setAttribute("data-l10n-id", EditorUndoBar.#l10nMessages[messageData]);
} else {
this.#message.setAttribute("data-l10n-id", EditorUndoBar.#l10nMessages._multiple);
this.#message.setAttribute("data-l10n-args", JSON.stringify({
count: messageData
}));
}
this.isOpen = true;
this.#container.hidden = false;
this.#showController = new AbortController();
this.#undoButton.addEventListener("click", () => {
undoAction();
this.hide();
}, {
signal: this.#showController.signal
});
this.#focusTimeout = setTimeout(() => {
this.#container.focus();
this.#focusTimeout = null;
}, 100);
}
hide() {
if (!this.isOpen) {
return;
}
this.isOpen = false;
this.#container.hidden = true;
this.#showController?.abort();
this.#showController = null;
if (this.#focusTimeout) {
clearTimeout(this.#focusTimeout);
this.#focusTimeout = null;
}
}
}
;// ./web/overlay_manager.js
class OverlayManager {
#overlays = new WeakMap();
#active = null;
get active() {
return this.#active;
}
async register(dialog, canForceClose = false) {
if (typeof dialog !== "object") {
throw new Error("Not enough parameters.");
} else if (this.#overlays.has(dialog)) {
throw new Error("The overlay is already registered.");
}
this.#overlays.set(dialog, {
canForceClose
});
dialog.addEventListener("cancel", evt => {
this.#active = null;
});
}
unregister(dialog) {
if (!this.#overlays.has(dialog)) {
throw new Error("The overlay does not exist.");
}
this.#overlays.delete(dialog);
}
async open(dialog) {
if (!this.#overlays.has(dialog)) {
throw new Error("The overlay does not exist.");
} else if (this.#active) {
if (this.#active === dialog) {
throw new Error("The overlay is already active.");
} else if (this.#overlays.get(dialog).canForceClose) {
await this.close();
} else {
throw new Error("Another overlay is currently active.");
}
}
this.#active = dialog;
dialog.showModal();
dialog.classList.remove("hidden");
}
async close(dialog = this.#active, silent = false) {
if (!this.#overlays.has(dialog)) {
if (silent) {
return;
}
throw new Error("The overlay does not exist.");
} else if (!this.#active) {
if (silent) {
return;
}
throw new Error("The overlay is currently not active.");
} else if (this.#active !== dialog) {
if (silent) {
return;
}
throw new Error("Another overlay is currently active.");
}
dialog.close();
this.#active = null;
}
}
;// ./web/password_prompt.js
class PasswordPrompt {
#activeCapability = null;
#updateCallback = null;
#reason = null;
constructor(options, overlayManager, isViewerEmbedded = false) {
this.dialog = options.dialog;
this.label = options.label;
this.input = options.input;
this.submitButton = options.submitButton;
this.cancelButton = options.cancelButton;
this.overlayManager = overlayManager;
this._isViewerEmbedded = isViewerEmbedded;
this.submitButton.addEventListener("click", this.#verify.bind(this));
this.cancelButton.addEventListener("click", this.close.bind(this));
this.input.addEventListener("keydown", e => {
if (e.keyCode === 13) {
this.#verify();
}
});
this.overlayManager.register(this.dialog, true);
this.dialog.addEventListener("close", this.#cancel.bind(this));
}
async open() {
await this.#activeCapability?.promise;
this.#activeCapability = Promise.withResolvers();
try {
await this.overlayManager.open(this.dialog);
this.input.type = "password";
this.input.focus();
} catch (ex) {
this.#activeCapability.resolve();
throw ex;
}
const passwordIncorrect = this.#reason === util.PasswordResponses.INCORRECT_PASSWORD;
if (!this._isViewerEmbedded || passwordIncorrect) {
this.input.focus();
}
this.label.setAttribute("data-l10n-id", passwordIncorrect ? "pdfjs-password-invalid" : "pdfjs-password-label");
}
async close() {
if (this.overlayManager.active === this.dialog) {
this.overlayManager.close(this.dialog);
this.input.value = "";
this.input.type = "text";
}
}
#verify() {
const password = this.input.value;
if (password?.length > 0) {
this.#invokeCallback(password);
}
}
#cancel() {
this.#invokeCallback(new Error("PasswordPrompt cancelled."));
this.#activeCapability.resolve();
}
#invokeCallback(password) {
if (!this.#updateCallback) {
return;
}
this.close();
this.input.value = "";
this.#updateCallback(password);
this.#updateCallback = null;
}
async setUpdateCallback(updateCallback, reason) {
if (this.#activeCapability) {
await this.#activeCapability.promise;
}
this.#updateCallback = updateCallback;
this.#reason = reason;
}
}
;// ./web/base_tree_viewer.js
const TREEITEM_OFFSET_TOP = -100;
const TREEITEM_SELECTED_CLASS = "selected";
class BaseTreeViewer {
constructor(options) {
this.container = options.container;
this.eventBus = options.eventBus;
this._l10n = options.l10n;
this.reset();
}
reset() {
this._pdfDocument = null;
this._lastToggleIsShow = true;
this._currentTreeItem = null;
this.container.textContent = "";
this.container.classList.remove("treeWithDeepNesting");
}
_dispatchEvent(count) {
throw new Error("Not implemented: _dispatchEvent");
}
_bindLink(element, params) {
throw new Error("Not implemented: _bindLink");
}
_normalizeTextContent(str) {
return removeNullCharacters(str, true) || "\u2013";
}
_addToggleButton(div, hidden = false) {
const toggler = document.createElement("div");
toggler.className = "treeItemToggler";
if (hidden) {
toggler.classList.add("treeItemsHidden");
}
toggler.onclick = evt => {
evt.stopPropagation();
toggler.classList.toggle("treeItemsHidden");
if (evt.shiftKey) {
const shouldShowAll = !toggler.classList.contains("treeItemsHidden");
this._toggleTreeItem(div, shouldShowAll);
}
};
div.prepend(toggler);
}
_toggleTreeItem(root, show = false) {
this._l10n.pause();
this._lastToggleIsShow = show;
for (const toggler of root.querySelectorAll(".treeItemToggler")) {
toggler.classList.toggle("treeItemsHidden", !show);
}
this._l10n.resume();
}
_toggleAllTreeItems() {
this._toggleTreeItem(this.container, !this._lastToggleIsShow);
}
_finishRendering(fragment, count, hasAnyNesting = false) {
if (hasAnyNesting) {
this.container.classList.add("treeWithDeepNesting");
this._lastToggleIsShow = !fragment.querySelector(".treeItemsHidden");
}
this._l10n.pause();
this.container.append(fragment);
this._l10n.resume();
this._dispatchEvent(count);
}
render(params) {
throw new Error("Not implemented: render");
}
_updateCurrentTreeItem(treeItem = null) {
if (this._currentTreeItem) {
this._currentTreeItem.classList.remove(TREEITEM_SELECTED_CLASS);
this._currentTreeItem = null;
}
if (treeItem) {
treeItem.classList.add(TREEITEM_SELECTED_CLASS);
this._currentTreeItem = treeItem;
}
}
_scrollToCurrentTreeItem(treeItem) {
if (!treeItem) {
return;
}
this._l10n.pause();
let currentNode = treeItem.parentNode;
while (currentNode && currentNode !== this.container) {
if (currentNode.classList.contains("treeItem")) {
const toggler = currentNode.firstElementChild;
toggler?.classList.remove("treeItemsHidden");
}
currentNode = currentNode.parentNode;
}
this._l10n.resume();
this._updateCurrentTreeItem(treeItem);
this.container.scrollTo(treeItem.offsetLeft, treeItem.offsetTop + TREEITEM_OFFSET_TOP);
}
}
;// ./web/pdf_attachment_viewer.js
class PDFAttachmentViewer extends BaseTreeViewer {
constructor(options) {
super(options);
this.downloadManager = options.downloadManager;
this.eventBus._on("fileattachmentannotation", this.#appendAttachment.bind(this));
}
reset(keepRenderedCapability = false) {
super.reset();
this._attachments = null;
if (!keepRenderedCapability) {
this._renderedCapability = Promise.withResolvers();
}
this._pendingDispatchEvent = false;
}
async _dispatchEvent(attachmentsCount) {
this._renderedCapability.resolve();
if (attachmentsCount === 0 && !this._pendingDispatchEvent) {
this._pendingDispatchEvent = true;
await waitOnEventOrTimeout({
target: this.eventBus,
name: "annotationlayerrendered",
delay: 1000
});
if (!this._pendingDispatchEvent) {
return;
}
}
this._pendingDispatchEvent = false;
this.eventBus.dispatch("attachmentsloaded", {
source: this,
attachmentsCount
});
}
_bindLink(element, {
content,
description,
filename
}) {
if (description) {
element.title = description;
}
element.onclick = () => {
this.downloadManager.openOrDownloadData(content, filename);
return false;
};
}
render({
attachments,
keepRenderedCapability = false
}) {
if (this._attachments) {
this.reset(keepRenderedCapability);
}
this._attachments = attachments || null;
if (!attachments) {
this._dispatchEvent(0);
return;
}
const fragment = document.createDocumentFragment();
let attachmentsCount = 0;
for (const name in attachments) {
const item = attachments[name];
const div = document.createElement("div");
div.className = "treeItem";
const element = document.createElement("a");
this._bindLink(element, item);
element.textContent = this._normalizeTextContent(item.filename);
div.append(element);
fragment.append(div);
attachmentsCount++;
}
this._finishRendering(fragment, attachmentsCount);
}
#appendAttachment(item) {
const renderedPromise = this._renderedCapability.promise;
renderedPromise.then(() => {
if (renderedPromise !== this._renderedCapability.promise) {
return;
}
const attachments = this._attachments || Object.create(null);
for (const name in attachments) {
if (item.filename === name) {
return;
}
}
attachments[item.filename] = item;
this.render({
attachments,
keepRenderedCapability: true
});
});
}
}
;// ./web/grab_to_pan.js
const CSS_CLASS_GRAB = "grab-to-pan-grab";
class GrabToPan {
#activateAC = null;
#mouseDownAC = null;
#scrollAC = null;
constructor({
element
}) {
this.element = element;
this.document = element.ownerDocument;
const overlay = this.overlay = document.createElement("div");
overlay.className = "grab-to-pan-grabbing";
}
activate() {
if (!this.#activateAC) {
this.#activateAC = new AbortController();
this.element.addEventListener("mousedown", this.#onMouseDown.bind(this), {
capture: true,
signal: this.#activateAC.signal
});
this.element.classList.add(CSS_CLASS_GRAB);
}
}
deactivate() {
if (this.#activateAC) {
this.#activateAC.abort();
this.#activateAC = null;
this.#endPan();
this.element.classList.remove(CSS_CLASS_GRAB);
}
}
toggle() {
if (this.#activateAC) {
this.deactivate();
} else {
this.activate();
}
}
ignoreTarget(node) {
if (document.querySelector(".stf__item")) {
return true;
}
return node.matches("a[href], a[href] *, input, textarea, button, button *, select, option");
}
#onMouseDown(event) {
if (event.button !== 0 || this.ignoreTarget(event.target)) {
return;
}
if (event.originalTarget) {
try {
event.originalTarget.tagName;
} catch {
return;
}
}
this.scrollLeftStart = this.element.scrollLeft;
this.scrollTopStart = this.element.scrollTop;
this.clientXStart = event.clientX;
this.clientYStart = event.clientY;
if (isOverPerfectScrollbar(this.clientXStart, this.clientYStart, "ps__rail-x")) {
return;
}
if (isOverPerfectScrollbar(this.clientXStart, this.clientYStart, "ps__rail-y")) {
return;
}
this.#mouseDownAC = new AbortController();
const boundEndPan = this.#endPan.bind(this),
mouseOpts = {
capture: true,
signal: this.#mouseDownAC.signal
};
this.document.addEventListener("mousemove", this.#onMouseMove.bind(this), mouseOpts);
this.document.addEventListener("mouseup", boundEndPan, mouseOpts);
this.#scrollAC = new AbortController();
this.element.addEventListener("scroll", boundEndPan, {
capture: true,
signal: this.#scrollAC.signal
});
(0,display_utils.stopEvent)(event);
const focusedElement = document.activeElement;
if (focusedElement && !focusedElement.contains(event.target)) {
focusedElement.blur();
}
}
#onMouseMove(event) {
this.#scrollAC?.abort();
this.#scrollAC = null;
if (!(event.buttons & 1)) {
this.#endPan();
return;
}
const xDiff = event.clientX - this.clientXStart;
const yDiff = event.clientY - this.clientYStart;
this.element.scrollTo({
top: this.scrollTopStart - yDiff,
left: this.scrollLeftStart - xDiff,
behavior: "instant"
});
if (!this.overlay.parentNode) {
document.body.append(this.overlay);
}
}
#endPan() {
this.#mouseDownAC?.abort();
this.#mouseDownAC = null;
this.#scrollAC?.abort();
this.#scrollAC = null;
this.overlay.remove();
}
}
function isOverPerfectScrollbar(x, y, divName) {
const perfectScrollbar = document.getElementsByClassName(divName);
if (perfectScrollbar && perfectScrollbar.length === 1) {
var {
top,
right,
bottom,
left
} = perfectScrollbar[0].getBoundingClientRect();
if (y >= top && y <= bottom) {
if (x <= right && x >= left) {
return true;
}
}
}
return false;
}
;// ./web/pdf_cursor_tools.js
class PDFCursorTools {
#active = CursorTool.SELECT;
#prevActive = null;
constructor({
container,
eventBus,
cursorToolOnLoad = CursorTool.SELECT
}) {
this.container = container;
this.eventBus = eventBus;
this.#addEventListeners();
Promise.resolve().then(() => {
this.switchTool(cursorToolOnLoad);
});
}
get activeTool() {
return this.#active;
}
switchTool(tool) {
if (this.#prevActive !== null) {
return;
}
this.#switchTool(tool);
}
#switchTool(tool, disabled = false) {
if (tool === this.#active) {
if (this.#prevActive !== null) {
this.eventBus.dispatch("cursortoolchanged", {
source: this,
tool,
disabled
});
}
return;
}
const disableActiveTool = () => {
switch (this.#active) {
case CursorTool.SELECT:
break;
case CursorTool.HAND:
this._handTool.deactivate();
break;
case CursorTool.ZOOM:
}
};
switch (tool) {
case CursorTool.SELECT:
disableActiveTool();
break;
case CursorTool.HAND:
disableActiveTool();
this._handTool.activate();
break;
case CursorTool.ZOOM:
default:
ngx_console.NgxConsole.error(`switchTool: "${tool}" is an unsupported value.`);
return;
}
this.#active = tool;
this.eventBus.dispatch("cursortoolchanged", {
source: this,
tool,
disabled
});
}
#addEventListeners() {
this.eventBus._on("switchcursortool", evt => {
if (!evt.reset) {
this.switchTool(evt.tool);
} else if (this.#prevActive !== null) {
annotationEditorMode = util.AnnotationEditorType.NONE;
presentationModeState = PresentationModeState.NORMAL;
enableActive();
}
});
let annotationEditorMode = util.AnnotationEditorType.NONE,
presentationModeState = PresentationModeState.NORMAL;
const disableActive = () => {
this.#prevActive ??= this.#active;
this.#switchTool(CursorTool.SELECT, true);
};
const enableActive = () => {
if (this.#prevActive !== null && annotationEditorMode === util.AnnotationEditorType.NONE && presentationModeState === PresentationModeState.NORMAL) {
this.#switchTool(this.#prevActive);
this.#prevActive = null;
}
};
this.eventBus._on("annotationeditormodechanged", ({
mode
}) => {
annotationEditorMode = mode;
if (mode === util.AnnotationEditorType.NONE) {
enableActive();
} else {
disableActive();
}
});
this.eventBus._on("presentationmodechanged", ({
state
}) => {
presentationModeState = state;
if (state === PresentationModeState.NORMAL) {
enableActive();
} else if (state === PresentationModeState.FULLSCREEN) {
disableActive();
}
});
}
get _handTool() {
return (0,util.shadow)(this, "_handTool", new GrabToPan({
element: this.container
}));
}
}
;// ./web/pdf_document_properties.js
const NON_METRIC_LOCALES = ["en-us", "en-lr", "my"];
const US_PAGE_NAMES = {
"8.5x11": "pdfjs-document-properties-page-size-name-letter",
"8.5x14": "pdfjs-document-properties-page-size-name-legal"
};
const METRIC_PAGE_NAMES = {
"297x420": "pdfjs-document-properties-page-size-name-a-three",
"210x297": "pdfjs-document-properties-page-size-name-a-four"
};
function getPageName(size, isPortrait, pageNames) {
const width = isPortrait ? size.width : size.height;
const height = isPortrait ? size.height : size.width;
return pageNames[`${width}x${height}`];
}
class PDFDocumentProperties {
#fieldData = null;
constructor({
dialog,
fields,
closeButton
}, overlayManager, eventBus, l10n, fileNameLookup) {
this.dialog = dialog;
this.fields = fields;
this.overlayManager = overlayManager;
this.l10n = l10n;
this._fileNameLookup = fileNameLookup;
this.eventBus = eventBus;
this.#reset();
closeButton.addEventListener("click", this.close.bind(this));
this.overlayManager.register(this.dialog);
eventBus._on("pagechanging", evt => {
this._currentPageNumber = evt.pageNumber;
});
eventBus._on("rotationchanging", evt => {
this._pagesRotation = evt.pagesRotation;
});
}
async open() {
await Promise.all([this.overlayManager.open(this.dialog), this._dataAvailableCapability.promise]);
this.eventBus.dispatch("propertiesdialogopen", this);
const currentPageNumber = this._currentPageNumber;
const pagesRotation = this._pagesRotation;
if (this.#fieldData && currentPageNumber === this.#fieldData._currentPageNumber && pagesRotation === this.#fieldData._pagesRotation) {
this.#updateUI();
return;
}
const [{
info,
contentLength
}, pdfPage] = await Promise.all([this.pdfDocument.getMetadata(), this.pdfDocument.getPage(currentPageNumber)]);
const [fileName, fileSize, creationDate, modificationDate, pageSize, isLinearized] = await Promise.all([this._fileNameLookup(), this.#parseFileSize(contentLength), this.#parseDate(info.CreationDate), this.#parseDate(info.ModDate), this.#parsePageSize(getPageSizeInches(pdfPage), pagesRotation), this.#parseLinearization(info.IsLinearized)]);
this.#fieldData = Object.freeze({
fileName,
fileSize,
title: info.Title,
author: info.Author,
subject: info.Subject,
keywords: info.Keywords,
creationDate,
modificationDate,
creator: info.Creator,
producer: info.Producer,
version: info.PDFFormatVersion,
pageCount: this.pdfDocument.numPages,
pageSize,
linearized: isLinearized,
_currentPageNumber: currentPageNumber,
_pagesRotation: pagesRotation
});
this.#updateUI();
const {
length
} = await this.pdfDocument.getDownloadInfo();
if (contentLength === length) {
return;
}
const data = Object.assign(Object.create(null), this.#fieldData);
data.fileSize = await this.#parseFileSize(length);
this.#fieldData = Object.freeze(data);
this.#updateUI();
}
async close() {
this.overlayManager.close(this.dialog, true);
this.eventBus.dispatch("propertiesdialogclose", this);
}
setDocument(pdfDocument) {
if (this.pdfDocument) {
this.#reset();
this.#updateUI();
}
if (!pdfDocument) {
return;
}
this.pdfDocument = pdfDocument;
this._dataAvailableCapability.resolve();
}
#reset() {
this.pdfDocument = null;
this.#fieldData = null;
this._dataAvailableCapability = Promise.withResolvers();
this._currentPageNumber = 1;
this._pagesRotation = 0;
}
#updateUI() {
if (this.#fieldData && this.overlayManager.active !== this.dialog) {
return;
}
for (const id in this.fields) {
const content = this.#fieldData?.[id];
this.fields[id].textContent = content || content === 0 ? content : "-";
}
}
async #parseFileSize(b = 0) {
const kb = b / 1024,
mb = kb / 1024;
return kb ? this.l10n.get(mb >= 1 ? "pdfjs-document-properties-size-mb" : "pdfjs-document-properties-size-kb", {
mb,
kb,
b
}) : undefined;
}
async #parsePageSize(pageSizeInches, pagesRotation) {
if (!pageSizeInches) {
return undefined;
}
if (pagesRotation % 180 !== 0) {
pageSizeInches = {
width: pageSizeInches.height,
height: pageSizeInches.width
};
}
const isPortrait = isPortraitOrientation(pageSizeInches),
nonMetric = NON_METRIC_LOCALES.includes(this.l10n.getLanguage());
let sizeInches = {
width: Math.round(pageSizeInches.width * 100) / 100,
height: Math.round(pageSizeInches.height * 100) / 100
};
let sizeMillimeters = {
width: Math.round(pageSizeInches.width * 25.4 * 10) / 10,
height: Math.round(pageSizeInches.height * 25.4 * 10) / 10
};
let nameId = getPageName(sizeInches, isPortrait, US_PAGE_NAMES) || getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES);
if (!nameId && !(Number.isInteger(sizeMillimeters.width) && Number.isInteger(sizeMillimeters.height))) {
const exactMillimeters = {
width: pageSizeInches.width * 25.4,
height: pageSizeInches.height * 25.4
};
const intMillimeters = {
width: Math.round(sizeMillimeters.width),
height: Math.round(sizeMillimeters.height)
};
if (Math.abs(exactMillimeters.width - intMillimeters.width) < 0.1 && Math.abs(exactMillimeters.height - intMillimeters.height) < 0.1) {
nameId = getPageName(intMillimeters, isPortrait, METRIC_PAGE_NAMES);
if (nameId) {
sizeInches = {
width: Math.round(intMillimeters.width / 25.4 * 100) / 100,
height: Math.round(intMillimeters.height / 25.4 * 100) / 100
};
sizeMillimeters = intMillimeters;
}
}
}
const [{
width,
height
}, unit, name, orientation] = await Promise.all([nonMetric ? sizeInches : sizeMillimeters, this.l10n.get(nonMetric ? "pdfjs-document-properties-page-size-unit-inches" : "pdfjs-document-properties-page-size-unit-millimeters"), nameId && this.l10n.get(nameId), this.l10n.get(isPortrait ? "pdfjs-document-properties-page-size-orientation-portrait" : "pdfjs-document-properties-page-size-orientation-landscape")]);
return this.l10n.get(name ? "pdfjs-document-properties-page-size-dimension-name-string" : "pdfjs-document-properties-page-size-dimension-string", {
width,
height,
unit,
name,
orientation
});
}
async #parseDate(inputDate) {
const dateObj = pdf.PDFDateString.toDateObject(inputDate);
return dateObj ? this.l10n.get("pdfjs-document-properties-date-time-string", {
dateObj: dateObj.valueOf()
}) : undefined;
}
#parseLinearization(isLinearized) {
return this.l10n.get(isLinearized ? "pdfjs-document-properties-linearized-yes" : "pdfjs-document-properties-linearized-no");
}
}
;// ./web/pdf_find_utils.js
const CharacterType = {
SPACE: 0,
ALPHA_LETTER: 1,
PUNCT: 2,
HAN_LETTER: 3,
KATAKANA_LETTER: 4,
HIRAGANA_LETTER: 5,
HALFWIDTH_KATAKANA_LETTER: 6,
THAI_LETTER: 7
};
function isAlphabeticalScript(charCode) {
return charCode < 0x2e80;
}
function isAscii(charCode) {
return (charCode & 0xff80) === 0;
}
function isAsciiAlpha(charCode) {
return charCode >= 0x61 && charCode <= 0x7a || charCode >= 0x41 && charCode <= 0x5a;
}
function isAsciiDigit(charCode) {
return charCode >= 0x30 && charCode <= 0x39;
}
function isAsciiSpace(charCode) {
return charCode === 0x20 || charCode === 0x09 || charCode === 0x0d || charCode === 0x0a;
}
function isHan(charCode) {
return charCode >= 0x3400 && charCode <= 0x9fff || charCode >= 0xf900 && charCode <= 0xfaff;
}
function isKatakana(charCode) {
return charCode >= 0x30a0 && charCode <= 0x30ff;
}
function isHiragana(charCode) {
return charCode >= 0x3040 && charCode <= 0x309f;
}
function isHalfwidthKatakana(charCode) {
return charCode >= 0xff60 && charCode <= 0xff9f;
}
function isThai(charCode) {
return (charCode & 0xff80) === 0x0e00;
}
function getCharacterType(charCode) {
if (isAlphabeticalScript(charCode)) {
if (isAscii(charCode)) {
if (isAsciiSpace(charCode)) {
return CharacterType.SPACE;
} else if (isAsciiAlpha(charCode) || isAsciiDigit(charCode) || charCode === 0x5f) {
return CharacterType.ALPHA_LETTER;
}
return CharacterType.PUNCT;
} else if (isThai(charCode)) {
return CharacterType.THAI_LETTER;
} else if (charCode === 0xa0) {
return CharacterType.SPACE;
}
return CharacterType.ALPHA_LETTER;
}
if (isHan(charCode)) {
return CharacterType.HAN_LETTER;
} else if (isKatakana(charCode)) {
return CharacterType.KATAKANA_LETTER;
} else if (isHiragana(charCode)) {
return CharacterType.HIRAGANA_LETTER;
} else if (isHalfwidthKatakana(charCode)) {
return CharacterType.HALFWIDTH_KATAKANA_LETTER;
}
return CharacterType.ALPHA_LETTER;
}
let NormalizeWithNFKC;
function getNormalizeWithNFKC() {
NormalizeWithNFKC ||= ` ¨ª¯²-µ¸-º¼-¾IJ-ijĿ-ŀʼnſDŽ-njDZ-dzʰ-ʸ˘-˝ˠ-ˤʹͺ;΄-΅·ϐ-ϖϰ-ϲϴ-ϵϹևٵ-ٸक़-य़ড়-ঢ়য়ਲ਼ਸ਼ਖ਼-ਜ਼ਫ਼ଡ଼-ଢ଼ำຳໜ-ໝ༌གྷཌྷདྷབྷཛྷཀྵჼᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵪᵸᶛ-ᶿẚ-ẛάέήίόύώΆ᾽-῁ΈΉ῍-῏ΐΊ῝-῟ΰΎ῭-`ΌΏ´-῾ - ‑‗․-… ″-‴‶-‷‼‾⁇-⁉⁗ ⁰-ⁱ⁴-₎ₐ-ₜ₨℀-℃℅-ℇ℉-ℓℕ-№ℙ-ℝ℠-™ℤΩℨK-ℭℯ-ℱℳ-ℹ℻-⅀ⅅ-ⅉ⅐-ⅿ↉∬-∭∯-∰〈-〉①-⓪⨌⩴-⩶⫝̸ⱼ-ⱽⵯ⺟⻳⼀-⿕ 〶〸-〺゛-゜ゟヿㄱ-ㆎ㆒-㆟㈀-㈞㈠-㉇㉐-㉾㊀-㏿ꚜ-ꚝꝰꟲ-ꟴꟸ-ꟹꭜ-ꭟꭩ豈-嗀塚晴凞-羽蘒諸逸-都飯-舘並-龎ff-stﬓ-ﬗיִײַ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-﷼︐-︙︰-﹄﹇-﹒﹔-﹦﹨-﹫ﹰ-ﹲﹴﹶ-ﻼ!-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ¢-₩`;
return NormalizeWithNFKC;
}
;// ./web/pdf_find_controller.js
const FindState = {
FOUND: 0,
NOT_FOUND: 1,
WRAPPED: 2,
PENDING: 3
};
const FIND_TIMEOUT = 250;
const MATCH_SCROLL_OFFSET_TOP = -50;
const MATCH_SCROLL_OFFSET_LEFT = -400;
const CHARACTERS_TO_NORMALIZE = {
"\u2010": "-",
"\u2018": "'",
"\u2019": "'",
"\u201A": "'",
"\u201B": "'",
"\u201C": '"',
"\u201D": '"',
"\u201E": '"',
"\u201F": '"',
"\u00BC": "1/4",
"\u00BD": "1/2",
"\u00BE": "3/4"
};
const DIACRITICS_EXCEPTION = new Set([0x3099, 0x309a, 0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca, 0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3, 0x2d7f, 0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed, 0x0c56, 0x0f71, 0x0f72, 0x0f7a, 0x0f7b, 0x0f7c, 0x0f7d, 0x0f80, 0x0f74]);
let DIACRITICS_EXCEPTION_STR;
const DIACRITICS_REG_EXP = /\p{M}+/gu;
const SPECIAL_CHARS_REG_EXP = /([.*+?^${}()|[\]\\])|(\p{P})|(\s+)|(\p{M})|(\p{L})/gu;
const NOT_DIACRITIC_FROM_END_REG_EXP = /([^\p{M}])\p{M}*$/u;
const NOT_DIACRITIC_FROM_START_REG_EXP = /^\p{M}*([^\p{M}])/u;
const SYLLABLES_REG_EXP = /[\uAC00-\uD7AF\uFA6C\uFACF-\uFAD1\uFAD5-\uFAD7]+/g;
const SYLLABLES_LENGTHS = new Map();
const FIRST_CHAR_SYLLABLES_REG_EXP = "[\\u1100-\\u1112\\ud7a4-\\ud7af\\ud84a\\ud84c\\ud850\\ud854\\ud857\\ud85f]";
const NFKC_CHARS_TO_NORMALIZE = new Map();
let noSyllablesRegExp = null;
let withSyllablesRegExp = null;
function normalize(text) {
const syllablePositions = [];
let m;
while ((m = SYLLABLES_REG_EXP.exec(text)) !== null) {
let {
index
} = m;
for (const char of m[0]) {
let len = SYLLABLES_LENGTHS.get(char);
if (!len) {
len = char.normalize("NFD").length;
SYLLABLES_LENGTHS.set(char, len);
}
syllablePositions.push([len, index++]);
}
}
let normalizationRegex;
if (syllablePositions.length === 0 && noSyllablesRegExp) {
normalizationRegex = noSyllablesRegExp;
} else if (syllablePositions.length > 0 && withSyllablesRegExp) {
normalizationRegex = withSyllablesRegExp;
} else {
const replace = Object.keys(CHARACTERS_TO_NORMALIZE).join("");
const toNormalizeWithNFKC = getNormalizeWithNFKC();
const CJK = "(?:\\p{Ideographic}|[\u3040-\u30FF])";
const HKDiacritics = "(?:\u3099|\u309A)";
const CompoundWord = "\\p{Ll}-\\n\\p{Lu}";
const regexp = `([${replace}])|([${toNormalizeWithNFKC}])|(${HKDiacritics}\\n)|(\\p{M}+(?:-\\n)?)|(${CompoundWord})|(\\S-\\n)|(${CJK}\\n)|(\\n)`;
if (syllablePositions.length === 0) {
normalizationRegex = noSyllablesRegExp = new RegExp(regexp + "|(\\u0000)", "gum");
} else {
normalizationRegex = withSyllablesRegExp = new RegExp(regexp + `|(${FIRST_CHAR_SYLLABLES_REG_EXP})`, "gum");
}
}
const rawDiacriticsPositions = [];
while ((m = DIACRITICS_REG_EXP.exec(text)) !== null) {
rawDiacriticsPositions.push([m[0].length, m.index]);
}
let normalized = text.normalize("NFD");
const positions = [0, 0];
let rawDiacriticsIndex = 0;
let syllableIndex = 0;
let shift = 0;
let shiftOrigin = 0;
let eol = 0;
let hasDiacritics = false;
normalized = normalized.replace(normalizationRegex, (match, p1, p2, p3, p4, p5, p6, p7, p8, p9, i) => {
i -= shiftOrigin;
if (p1) {
const replacement = CHARACTERS_TO_NORMALIZE[p1];
const jj = replacement.length;
for (let j = 1; j < jj; j++) {
positions.push(i - shift + j, shift - j);
}
shift -= jj - 1;
return replacement;
}
if (p2) {
let replacement = NFKC_CHARS_TO_NORMALIZE.get(p2);
if (!replacement) {
replacement = p2.normalize("NFKC");
NFKC_CHARS_TO_NORMALIZE.set(p2, replacement);
}
const jj = replacement.length;
for (let j = 1; j < jj; j++) {
positions.push(i - shift + j, shift - j);
}
shift -= jj - 1;
return replacement;
}
if (p3) {
hasDiacritics = true;
if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) {
++rawDiacriticsIndex;
} else {
positions.push(i - 1 - shift + 1, shift - 1);
shift -= 1;
shiftOrigin += 1;
}
positions.push(i - shift + 1, shift);
shiftOrigin += 1;
eol += 1;
return p3.charAt(0);
}
if (p4) {
const hasTrailingDashEOL = p4.endsWith("\n");
const len = hasTrailingDashEOL ? p4.length - 2 : p4.length;
hasDiacritics = true;
let jj = len;
if (i + eol === rawDiacriticsPositions[rawDiacriticsIndex]?.[1]) {
jj -= rawDiacriticsPositions[rawDiacriticsIndex][0];
++rawDiacriticsIndex;
}
for (let j = 1; j <= jj; j++) {
positions.push(i - 1 - shift + j, shift - j);
}
shift -= jj;
shiftOrigin += jj;
if (hasTrailingDashEOL) {
i += len - 1;
positions.push(i - shift + 1, 1 + shift);
shift += 1;
shiftOrigin += 1;
eol += 1;
return p4.slice(0, len);
}
return p4;
}
if (p5) {
shiftOrigin += 1;
eol += 1;
return p5.replace("\n", "");
}
if (p6) {
const len = p6.length - 2;
positions.push(i - shift + len, 1 + shift);
shift += 1;
shiftOrigin += 1;
eol += 1;
return p6.slice(0, -2);
}
if (p7) {
const len = p7.length - 1;
positions.push(i - shift + len, shift);
shiftOrigin += 1;
eol += 1;
return p7.slice(0, -1);
}
if (p8) {
positions.push(i - shift + 1, shift - 1);
shift -= 1;
shiftOrigin += 1;
eol += 1;
return " ";
}
if (i + eol === syllablePositions[syllableIndex]?.[1]) {
const newCharLen = syllablePositions[syllableIndex][0] - 1;
++syllableIndex;
for (let j = 1; j <= newCharLen; j++) {
positions.push(i - (shift - j), shift - j);
}
shift -= newCharLen;
shiftOrigin += newCharLen;
}
return p9;
});
positions.push(normalized.length, shift);
const starts = new Uint32Array(positions.length >> 1);
const shifts = new Int32Array(positions.length >> 1);
for (let i = 0, ii = positions.length; i < ii; i += 2) {
starts[i >> 1] = positions[i];
shifts[i >> 1] = positions[i + 1];
}
return [normalized, [starts, shifts], hasDiacritics];
}
function getOriginalIndex(diffs, pos, len) {
if (!diffs) {
return [pos, len];
}
const [starts, shifts] = diffs;
const start = pos;
const end = pos + len - 1;
let i = binarySearchFirstItem(starts, x => x >= start);
if (starts[i] > start) {
--i;
}
let j = binarySearchFirstItem(starts, x => x >= end, i);
if (starts[j] > end) {
--j;
}
const oldStart = start + shifts[i];
const oldEnd = end + shifts[j];
const oldLen = oldEnd + 1 - oldStart;
return [oldStart, oldLen];
}
class PDFFindController {
#state = null;
#updateMatchesCountOnProgress = true;
#visitedPagesCount = 0;
constructor({
linkService,
eventBus,
updateMatchesCountOnProgress = true,
pageViewMode,
listenToEventBus
}) {
this._linkService = linkService;
this._eventBus = eventBus;
this.#updateMatchesCountOnProgress = updateMatchesCountOnProgress;
this._pageViewMode = pageViewMode;
this.onIsPageVisible = null;
this.#reset();
this._eventBus.on("pageviewmodechanged", ({
pageViewMode
}) => {
this._pageViewMode = pageViewMode;
});
if (listenToEventBus) {
eventBus._on("find", this.#onFind.bind(this));
eventBus._on("findbarclose", this.#onFindBarClose.bind(this));
}
}
get highlightMatches() {
return this._highlightMatches;
}
get pageMatches() {
return this._pageMatches;
}
get pageMatchesLength() {
return this._pageMatchesLength;
}
get selected() {
return this._selected;
}
get state() {
return this.#state;
}
setDocument(pdfDocument) {
if (this._pdfDocument) {
this.#reset();
}
if (!pdfDocument) {
return;
}
this._pdfDocument = pdfDocument;
this._firstPageCapability.resolve();
}
ngxFind(pdfFindParameters) {
const findResultPromises = [];
this._findResultResolvers = [];
for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
const {
promise,
resolve
} = Promise.withResolvers();
findResultPromises[i] = promise;
this._findResultResolvers[i] = resolve;
}
this.#onFind(pdfFindParameters);
return findResultPromises;
}
ngxFindNext() {
const state = {
...this.#state,
type: "again",
findPrevious: false
};
this.#onFind(state);
}
ngxFindPrevious() {
const state = {
...this.#state,
type: "again",
findPrevious: true
};
this.#onFind(state);
}
#onFind(state) {
if (!state) {
return;
}
const pdfDocument = this._pdfDocument;
const {
type
} = state;
if (this.#state === null || this.#shouldDirtyMatch(state)) {
this._dirtyMatch = true;
}
this.#state = state;
if (type !== "highlightallchange") {
this.#updateUIState(FindState.PENDING);
} else {
const hasMatches = this._pageMatches.some(matches => matches.length > 0);
const emptyQuery = !this.state?.query;
const reportAsFound = emptyQuery || hasMatches;
this.#updateUIState(reportAsFound ? FindState.FOUND : FindState.NOT_FOUND);
}
this._firstPageCapability.promise.then(() => {
if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
return;
}
this.#extractText();
const findbarClosed = !this._highlightMatches;
const pendingTimeout = !!this._findTimeout;
if (this._findTimeout) {
clearTimeout(this._findTimeout);
this._findTimeout = null;
}
if (!type) {
this._findTimeout = setTimeout(() => {
this.#nextMatch();
this._findTimeout = null;
}, FIND_TIMEOUT);
} else if (this._dirtyMatch) {
this.#nextMatch();
} else if (type === "again") {
this.#nextMatch();
if (findbarClosed && this.#state.highlightAll) {
this.#updateAllPages();
}
} else if (type === "highlightallchange") {
if (pendingTimeout) {
this.#nextMatch();
} else {
this._highlightMatches = true;
}
this.#updateAllPages();
} else {
this.#nextMatch();
}
});
}
scrollMatchIntoView({
element = null,
selectedLeft = 0,
pageIndex = -1,
matchIndex = -1
}) {
if (this.#state?.dontScrollIntoView) {
return;
}
if (!this._scrollMatches || !element) {
return;
}
this._scrollMatches = false;
const spot = {
top: MATCH_SCROLL_OFFSET_TOP,
left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT
};
scrollIntoView(element, spot, true, this._pageViewMode === "infinite-scroll");
}
#reset() {
this._highlightMatches = false;
this._scrollMatches = false;
this._pdfDocument = null;
this._pageMatches = [];
this._pageMatchesLength = [];
this.#visitedPagesCount = 0;
this.#state = null;
this._selected = {
pageIdx: -1,
matchIdx: -1
};
this._offset = {
pageIdx: null,
matchIdx: null,
wrapped: false
};
this._extractTextPromises = [];
this._findResultResolvers = [];
this._pageContents = [];
this._pageDiffs = [];
this._hasDiacritics = [];
this._matchesCountTotal = 0;
this._pagesToSearch = null;
this._pendingFindMatches = new Set();
this._resumePageIdx = null;
this._dirtyMatch = false;
clearTimeout(this._findTimeout);
this._findTimeout = null;
this._firstPageCapability = Promise.withResolvers();
}
get #query() {
const {
query
} = this.#state;
if (typeof query === "string") {
if (query !== this._rawQuery) {
this._rawQuery = query;
[this._normalizedQuery] = normalize(query);
}
return this._normalizedQuery;
}
return (query || []).filter(q => !!q).map(q => normalize(q)[0]);
}
#shouldDirtyMatch(state) {
const newQuery = state.query,
prevQuery = this.#state.query;
const newType = typeof newQuery,
prevType = typeof prevQuery;
if (newType !== prevType) {
return true;
}
if (newType === "string") {
if (newQuery !== prevQuery) {
return true;
}
} else if (JSON.stringify(newQuery) !== JSON.stringify(prevQuery)) {
return true;
}
switch (state.type) {
case "again":
const pageNumber = this._selected.pageIdx + 1;
const linkService = this._linkService;
return pageNumber >= 1 && pageNumber <= linkService.pagesCount && pageNumber !== linkService.page && !(this.onIsPageVisible?.(pageNumber) ?? true);
case "highlightallchange":
return false;
}
return true;
}
#isEntireWord(content, startIdx, length) {
let match = content.slice(0, startIdx).match(NOT_DIACRITIC_FROM_END_REG_EXP);
if (match) {
const first = content.charCodeAt(startIdx);
const limit = match[1].charCodeAt(0);
if (getCharacterType(first) === getCharacterType(limit)) {
return false;
}
}
match = content.slice(startIdx + length).match(NOT_DIACRITIC_FROM_START_REG_EXP);
if (match) {
const last = content.charCodeAt(startIdx + length - 1);
const limit = match[1].charCodeAt(0);
if (getCharacterType(last) === getCharacterType(limit)) {
return false;
}
}
return true;
}
_convertToRegExpString(query, hasDiacritics) {
const {
matchDiacritics
} = this.#state;
let isUnicode = false;
query = query.replaceAll(SPECIAL_CHARS_REG_EXP, (match, p1, p2, p3, p4, p5) => {
if (p1) {
return `[ ]*\\${p1}[ ]*`;
}
if (p2) {
return `[ ]*${p2}[ ]*`;
}
if (p3) {
return "[ ]+";
}
if (matchDiacritics) {
return p4 || p5;
}
if (p4) {
return DIACRITICS_EXCEPTION.has(p4.charCodeAt(0)) ? p4 : "";
}
if (hasDiacritics) {
isUnicode = true;
return `${p5}\\p{M}*`;
}
return p5;
});
const trailingSpaces = "[ ]*";
if (query.endsWith(trailingSpaces)) {
query = query.slice(0, query.length - trailingSpaces.length);
}
if (matchDiacritics) {
if (hasDiacritics) {
DIACRITICS_EXCEPTION_STR ||= String.fromCharCode(...DIACRITICS_EXCEPTION);
isUnicode = true;
query = `${query}(?=[${DIACRITICS_EXCEPTION_STR}]|[^\\p{M}]|$)`;
}
}
return [isUnicode, query];
}
_calculateMatch(pageIndex) {
const query = this.#query;
if (query.length === 0) {
return;
}
const pageContent = this._pageContents[pageIndex];
const matcherResult = this.match(query, pageContent, pageIndex);
const matches = this._pageMatches[pageIndex] = [];
const matchesLength = this._pageMatchesLength[pageIndex] = [];
const diffs = this._pageDiffs[pageIndex];
matcherResult?.forEach(({
index,
length
}) => {
const [matchPos, matchLen] = getOriginalIndex(diffs, index, length);
if (matchLen) {
matches.push(matchPos);
matchesLength.push(matchLen);
}
});
if (this.#state.highlightAll) {
this.#updatePage(pageIndex);
}
if (this._resumePageIdx === pageIndex) {
this._resumePageIdx = null;
this.#nextPageMatch();
}
const pageMatchesCount = matches.length;
this._matchesCountTotal += pageMatchesCount;
if (this.#updateMatchesCountOnProgress) {
if (pageMatchesCount > 0) {
this.#updateUIResultsCount(pageIndex);
}
} else if (++this.#visitedPagesCount === this._linkService.pagesCount) {
this.#updateUIResultsCount(pageIndex);
}
}
match(query, pageContent, pageIndex) {
const hasDiacritics = this._hasDiacritics[pageIndex];
let isUnicode = false;
const {
caseSensitive,
findMultiple,
matchRegExp
} = this.#state;
if (findMultiple && typeof query === "string") {
query = query.trim().split(/\s+/);
}
if (matchRegExp && typeof query === "string") {
query = new RegExp(query, caseSensitive ? "g" : "gi");
isUnicode = hasDiacritics;
} else if (typeof query === "string") {
[isUnicode, query] = this._convertToRegExpString(query, hasDiacritics);
} else {
query = query.sort().reverse().map(q => {
const [isUnicodePart, queryPart] = this._convertToRegExpString(q, hasDiacritics);
isUnicode ||= isUnicodePart;
return `(${queryPart})`;
}).join("|");
}
if (!query) {
return undefined;
}
const {
entireWord
} = this.#state;
const flags = `g${isUnicode ? "u" : ""}${caseSensitive ? "" : "i"}`;
query = new RegExp(query, flags);
const matches = [];
let match;
while ((match = query.exec(pageContent)) !== null) {
if (entireWord && !this.#isEntireWord(pageContent, match.index, match[0].length)) {
continue;
}
matches.push({
index: match.index,
length: match[0].length
});
}
return matches;
}
#extractText() {
if (this._extractTextPromises.length > 0) {
return;
}
let deferred = Promise.resolve();
const textOptions = {
disableNormalization: true
};
for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
const {
promise,
resolve
} = Promise.withResolvers();
this._extractTextPromises[i] = promise;
deferred = deferred.then(() => {
return this._pdfDocument.getPage(i + 1).then(pdfPage => pdfPage.getTextContent(textOptions)).then(textContent => {
const strBuf = [];
for (const textItem of textContent.items) {
strBuf.push(textItem.str);
if (textItem.hasEOL) {
strBuf.push("\n");
}
}
[this._pageContents[i], this._pageDiffs[i], this._hasDiacritics[i]] = normalize(strBuf.join(""));
resolve();
}, reason => {
ngx_console.NgxConsole.error(`Unable to get text content for page ${i + 1}`, reason);
this._pageContents[i] = "";
this._pageDiffs[i] = null;
this._hasDiacritics[i] = false;
resolve();
});
});
}
}
#updatePage(index) {
if (this._scrollMatches && this._selected.pageIdx === index) {
if (!this.state.dontScrollIntoView) {
this._linkService.page = index + 1;
}
}
this._eventBus.dispatch("updatetextlayermatches", {
source: this,
pageIndex: index
});
}
#updateAllPages() {
this._eventBus.dispatch("updatetextlayermatches", {
source: this,
pageIndex: -1
});
}
#nextMatch() {
const previous = this.#state.findPrevious;
const currentPageIndex = this._linkService.page - 1;
const numPages = this._linkService.pagesCount;
this._highlightMatches = true;
if (this._dirtyMatch) {
this._dirtyMatch = false;
this._selected.pageIdx = this._selected.matchIdx = -1;
this._offset.pageIdx = currentPageIndex;
this._offset.matchIdx = null;
this._offset.wrapped = false;
this._resumePageIdx = null;
this._pageMatches.length = 0;
this._pageMatchesLength.length = 0;
this.#visitedPagesCount = 0;
this._matchesCountTotal = 0;
this.#updateAllPages();
for (let i = 0; i < numPages; i++) {
if (this._pendingFindMatches.has(i)) {
continue;
}
this._pendingFindMatches.add(i);
this._extractTextPromises[i].then(() => {
this._pendingFindMatches.delete(i);
this._calculateMatch(i);
});
}
}
const query = this.#query;
if (query.length === 0) {
this.#updateUIState(FindState.FOUND);
return;
}
if (this._resumePageIdx) {
return;
}
const offset = this._offset;
this._pagesToSearch = numPages;
if (offset.matchIdx !== null) {
const numPageMatches = this._pageMatches[offset.pageIdx].length;
if (!previous && offset.matchIdx + 1 < numPageMatches || previous && offset.matchIdx > 0) {
offset.matchIdx = previous ? offset.matchIdx - 1 : offset.matchIdx + 1;
this.#updateMatch(true);
return;
}
this.#advanceOffsetPage(previous);
}
this.#nextPageMatch();
}
#matchesReady(matches) {
const offset = this._offset;
const numMatches = matches.length;
const previous = this.#state.findPrevious;
if (numMatches) {
offset.matchIdx = previous ? numMatches - 1 : 0;
this.#updateMatch(true);
return true;
}
this.#advanceOffsetPage(previous);
if (offset.wrapped) {
offset.matchIdx = null;
if (this._pagesToSearch < 0) {
this.#updateMatch(false);
return true;
}
}
return false;
}
#nextPageMatch() {
if (this._resumePageIdx !== null) {
ngx_console.NgxConsole.error("There can only be one pending page.");
}
let matches = null;
do {
const pageIdx = this._offset.pageIdx;
matches = this._pageMatches[pageIdx];
if (!matches) {
this._resumePageIdx = pageIdx;
break;
}
} while (!this.#matchesReady(matches));
}
#advanceOffsetPage(previous) {
const offset = this._offset;
const numPages = this._linkService.pagesCount;
offset.pageIdx = previous ? offset.pageIdx - 1 : offset.pageIdx + 1;
offset.matchIdx = null;
this._pagesToSearch--;
if (offset.pageIdx >= numPages || offset.pageIdx < 0) {
offset.pageIdx = previous ? numPages - 1 : 0;
offset.wrapped = true;
}
}
#updateMatch(found = false) {
let state = FindState.NOT_FOUND;
const wrapped = this._offset.wrapped;
this._offset.wrapped = false;
if (found) {
const previousPage = this._selected.pageIdx;
this._selected.pageIdx = this._offset.pageIdx;
this._selected.matchIdx = this._offset.matchIdx;
state = wrapped ? FindState.WRAPPED : FindState.FOUND;
if (previousPage !== -1 && previousPage !== this._selected.pageIdx) {
this.#updatePage(previousPage);
}
}
this.#updateUIState(state, this.#state.findPrevious);
if (this._selected.pageIdx !== -1) {
this._scrollMatches = true;
this.#updatePage(this._selected.pageIdx);
}
}
#onFindBarClose(evt) {
const pdfDocument = this._pdfDocument;
this._firstPageCapability.promise.then(() => {
if (!this._pdfDocument || pdfDocument && this._pdfDocument !== pdfDocument) {
return;
}
if (this._findTimeout) {
clearTimeout(this._findTimeout);
this._findTimeout = null;
}
if (this._resumePageIdx) {
this._resumePageIdx = null;
this._dirtyMatch = true;
}
this.#updateUIState(FindState.FOUND);
this._highlightMatches = false;
this.#updateAllPages();
});
}
#requestMatchesCount() {
const {
pageIdx,
matchIdx
} = this._selected;
let current = 0,
total = this._matchesCountTotal;
if (matchIdx !== -1) {
for (let i = 0; i < pageIdx; i++) {
current += this._pageMatches[i]?.length || 0;
}
current += matchIdx + 1;
}
if (current < 1 || current > total) {
current = total = 0;
}
return {
current,
total
};
}
#updateUIResultsCount(pageIndex) {
this._eventBus.dispatch("updatefindmatchescount", {
source: this,
matchesCount: this.#requestMatchesCount()
});
const pageMatchesCount = this._pageMatches[pageIndex].length;
if (this._findResultResolvers && this._findResultResolvers[pageIndex]) {
this._findResultResolvers[pageIndex](pageMatchesCount);
this._findResultResolvers[pageIndex] = null;
}
}
#updateUIState(state, previous = false) {
if (!this.#updateMatchesCountOnProgress && (this.#visitedPagesCount !== this._linkService.pagesCount || state === FindState.PENDING)) {
return;
}
this._eventBus.dispatch("updatefindcontrolstate", {
source: this,
state,
previous,
entireWord: this.#state?.entireWord ?? null,
matchesCount: this.#requestMatchesCount(),
rawQuery: this.#state?.query ?? null
});
}
}
;// ./web/pdf_find_bar.js
const MATCHES_COUNT_LIMIT = 1000;
class PDFFindBar {
#mainContainer;
#resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this));
constructor(options, mainContainer, eventBus) {
this.opened = false;
this.bar = options.bar;
this.toggleButton = options.toggleButton;
this.findField = options.findField;
this.highlightAll = options.highlightAllCheckbox;
this.currentPage = options.findCurrentPageCheckbox;
this.pageRange = options.findPageRangeField;
this.caseSensitive = options.caseSensitiveCheckbox;
this.findMultipleCheckbox = options.findMultipleCheckbox;
this.matchRegExpCheckbox = options.matchRegExpCheckbox;
this.matchDiacritics = options.matchDiacriticsCheckbox;
this.entireWord = options.entireWordCheckbox;
this.findMsg = options.findMsg;
this.findResultsCount = options.findResultsCount;
this.findPreviousButton = options.findPreviousButton;
this.findNextButton = options.findNextButton;
this.eventBus = eventBus;
this.#mainContainer = mainContainer;
const checkedInputs = new Map(Array.from([[this.highlightAll, "highlightallchange"], [this.caseSensitive, "casesensitivitychange"], [this.entireWord, "entirewordchange"], [this.matchDiacritics, "diacriticmatchingchange"], [this.findMultipleCheckbox, "findmultiplechange"]]).filter(([elem]) => elem && elem instanceof Element));
if (this.toggleButton) {
if (!isInsideNgxExtendedPdfViewer(this.toggleButton)) {
this.toggleButton.addEventListener("click", () => {
this.toggle();
});
}
}
this.findField.addEventListener("input", () => {
this.dispatchEvent("");
});
this.findField.parentElement.addEventListener("keydown", event => {
const {
keyCode
} = event;
if (keyCode !== 13 && keyCode !== 27) {
event.stopPropagation();
}
if (event.metaKey && event.key === "ArrowDown") {
event.preventDefault();
}
if (event.metaKey && event.key === "ArrowUp") {
event.preventDefault();
}
});
this.bar.addEventListener("keydown", event => {
const {
keyCode,
shiftKey,
target
} = event;
switch (keyCode) {
case 13:
if (target === this.findField) {
this.dispatchEvent("again", shiftKey);
} else if (checkedInputs.has(target)) {
target.checked = !target.checked;
this.dispatchEvent(checkedInputs.get(target));
}
break;
case 27:
this.close();
event.stopPropagation();
break;
}
});
this.findPreviousButton.addEventListener("click", () => {
this.dispatchEvent("again", true);
});
this.findNextButton.addEventListener("click", () => {
this.dispatchEvent("again", false);
});
for (const [elem, evtName] of checkedInputs) {
elem.addEventListener("click", () => {
this.dispatchEvent(evtName);
});
}
this.matchRegExpCheckbox?.addEventListener("click", () => {
if (this.matchRegExpCheckbox.checked) {
this.findMultipleCheckbox.checked = false;
this.findMultipleCheckbox.disabled = true;
this.matchDiacritics.checked = false;
this.matchDiacritics.disabled = true;
this.entireWord.checked = false;
this.entireWord.disabled = true;
} else {
this.findMultipleCheckbox.disabled = false;
this.matchDiacritics.disabled = false;
this.entireWord.disabled = false;
}
this.dispatchEvent("findregexpchange");
});
}
reset() {
this.updateUIState();
}
dispatchEvent(type, findPrev = false) {
this.eventBus.dispatch("find", {
source: this,
type,
query: this.findField.value,
caseSensitive: this.caseSensitive.checked,
findMultiple: this.findMultipleCheckbox?.checked,
matchRegExp: this.matchRegExpCheckbox?.checked,
entireWord: this.entireWord.checked,
highlightAll: this.highlightAll.checked,
findPrevious: findPrev,
matchDiacritics: this.matchDiacritics.checked
});
}
updateUIState(state, previous, matchesCount) {
const {
findField,
findMsg
} = this;
let findMsgId = "",
status = "";
switch (state) {
case FindState.FOUND:
break;
case FindState.PENDING:
status = "pending";
break;
case FindState.NOT_FOUND:
findMsgId = "pdfjs-find-not-found";
status = "notFound";
break;
case FindState.WRAPPED:
findMsgId = previous ? "pdfjs-find-reached-top" : "pdfjs-find-reached-bottom";
break;
}
findField.setAttribute("data-status", status);
findField.setAttribute("aria-invalid", state === FindState.NOT_FOUND);
findMsg.setAttribute("data-status", status);
if (findMsgId) {
findMsg.setAttribute("data-l10n-id", findMsgId);
} else {
findMsg.removeAttribute("data-l10n-id");
findMsg.textContent = "";
}
this.updateResultsCount(matchesCount);
}
updateResultsCount({
current = 0,
total = 0
} = {}) {
const {
findResultsCount
} = this;
if (total > 0) {
const limit = MATCHES_COUNT_LIMIT;
findResultsCount.setAttribute("data-l10n-id", total > limit ? "pdfjs-find-match-count-limit" : "pdfjs-find-match-count");
findResultsCount.setAttribute("data-l10n-args", JSON.stringify({
limit,
current,
total
}));
} else {
findResultsCount.removeAttribute("data-l10n-id");
findResultsCount.textContent = "";
}
}
open() {
if (!this.opened) {
this.#resizeObserver.observe(this.#mainContainer);
this.#resizeObserver.observe(this.bar);
this.opened = true;
toggleExpandedBtn(this.toggleButton, true, this.bar);
}
this.findField.select();
this.findField.focus();
this.dispatchEvent("");
this.eventBus.dispatch("findbaropen", {
source: this
});
}
close() {
if (!this.opened) {
return;
}
this.#resizeObserver.disconnect();
this.opened = false;
toggleExpandedBtn(this.toggleButton, false, this.bar);
this.eventBus.dispatch("findbarclose", {
source: this
});
}
toggle() {
if (this.opened) {
this.close();
} else {
this.open();
}
}
#resizeObserverCallback() {
const {
bar
} = this;
bar.classList.remove("wrapContainers");
const findbarHeight = bar.clientHeight;
const inputContainerHeight = bar.firstElementChild.clientHeight;
if (findbarHeight > inputContainerHeight) {
bar.classList.add("wrapContainers");
}
}
}
;// ./web/pdf_history.js
const HASH_CHANGE_TIMEOUT = 1000;
const POSITION_UPDATED_THRESHOLD = 50;
const UPDATE_VIEWAREA_TIMEOUT = 1000;
function getCurrentHash() {
return document.location.hash;
}
class PDFHistory {
#eventAbortController = null;
constructor({
linkService,
eventBus
}) {
this.linkService = linkService;
this.eventBus = eventBus;
this._initialized = false;
this._fingerprint = "";
this.reset();
this.eventBus._on("pagesinit", () => {
this._isPagesLoaded = false;
this.eventBus._on("pagesloaded", evt => {
this._isPagesLoaded = !!evt.pagesCount;
}, {
once: true
});
});
}
initialize({
fingerprint,
resetHistory = false,
updateUrl = false
}) {
if (!fingerprint || typeof fingerprint !== "string") {
ngx_console.NgxConsole.error('PDFHistory.initialize: The "fingerprint" must be a non-empty string.');
return;
}
if (this._initialized) {
this.reset();
}
const reInitialized = this._fingerprint !== "" && this._fingerprint !== fingerprint;
this._fingerprint = fingerprint;
this._updateUrl = updateUrl === true;
this._initialized = true;
this.#bindEvents();
const state = window.history.state;
this._popStateInProgress = false;
this._blockHashChange = 0;
this._currentHash = getCurrentHash();
this._numPositionUpdates = 0;
this._uid = this._maxUid = 0;
this._destination = null;
this._position = null;
if (!this.#isValidState(state, true) || resetHistory) {
const {
hash,
page,
rotation
} = this.#parseCurrentHash(true);
if (!hash || reInitialized || resetHistory) {
this.#pushOrReplaceState(null, true);
return;
}
this.#pushOrReplaceState({
hash,
page,
rotation
}, true);
return;
}
const destination = state.destination;
this.#updateInternalState(destination, state.uid, true);
if (destination.rotation !== undefined) {
this._initialRotation = destination.rotation;
}
if (destination.dest) {
this._initialBookmark = JSON.stringify(destination.dest);
this._destination.page = null;
} else if (destination.hash) {
this._initialBookmark = destination.hash;
} else if (destination.page) {
this._initialBookmark = `page=${destination.page}`;
}
}
reset() {
if (this._initialized) {
this.#pageHide();
this._initialized = false;
this.#unbindEvents();
}
if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout);
this._updateViewareaTimeout = null;
}
this._initialBookmark = null;
this._initialRotation = null;
}
push({
namedDest = null,
explicitDest,
pageNumber
}) {
if (!this._initialized) {
return;
}
if (namedDest && typeof namedDest !== "string") {
ngx_console.NgxConsole.error("PDFHistory.push: " + `"${namedDest}" is not a valid namedDest parameter.`);
return;
} else if (!Array.isArray(explicitDest)) {
ngx_console.NgxConsole.error("PDFHistory.push: " + `"${explicitDest}" is not a valid explicitDest parameter.`);
return;
} else if (!this.#isValidPage(pageNumber)) {
if (pageNumber !== null || this._destination) {
ngx_console.NgxConsole.error("PDFHistory.push: " + `"${pageNumber}" is not a valid pageNumber parameter.`);
return;
}
}
const hash = namedDest || JSON.stringify(explicitDest);
if (!hash) {
return;
}
let forceReplace = false;
if (this._destination && (isDestHashesEqual(this._destination.hash, hash) || isDestArraysEqual(this._destination.dest, explicitDest))) {
if (this._destination.page) {
return;
}
forceReplace = true;
}
if (this._popStateInProgress && !forceReplace) {
return;
}
this.#pushOrReplaceState({
dest: explicitDest,
hash,
page: pageNumber,
rotation: this.linkService.rotation
}, forceReplace);
if (!this._popStateInProgress) {
this._popStateInProgress = true;
Promise.resolve().then(() => {
this._popStateInProgress = false;
});
}
}
pushPage(pageNumber) {
if (!this._initialized) {
return;
}
if (!this.#isValidPage(pageNumber)) {
ngx_console.NgxConsole.error(`PDFHistory.pushPage: "${pageNumber}" is not a valid page number.`);
return;
}
if (this._destination?.page === pageNumber) {
return;
}
if (this._popStateInProgress) {
return;
}
this.#pushOrReplaceState({
dest: null,
hash: `page=${pageNumber}`,
page: pageNumber,
rotation: this.linkService.rotation
});
if (!this._popStateInProgress) {
this._popStateInProgress = true;
Promise.resolve().then(() => {
this._popStateInProgress = false;
});
}
}
pushCurrentPosition() {
if (!this._initialized || this._popStateInProgress) {
return;
}
this.#tryPushCurrentPosition();
}
back() {
if (!this._initialized || this._popStateInProgress) {
return;
}
const state = window.history.state;
if (this.#isValidState(state) && state.uid > 0) {
window.history.back();
}
}
forward() {
if (!this._initialized || this._popStateInProgress) {
return;
}
const state = window.history.state;
if (this.#isValidState(state) && state.uid < this._maxUid) {
window.history.forward();
}
}
get popStateInProgress() {
return this._initialized && (this._popStateInProgress || this._blockHashChange > 0);
}
get initialBookmark() {
return this._initialized ? this._initialBookmark : null;
}
get initialRotation() {
return this._initialized ? this._initialRotation : null;
}
#pushOrReplaceState(destination, forceReplace = false) {
const shouldReplace = forceReplace || !this._destination;
const newState = {
fingerprint: this._fingerprint,
uid: shouldReplace ? this._uid : this._uid + 1,
destination
};
this.#updateInternalState(destination, newState.uid);
let newUrl;
if (this._updateUrl && destination?.hash) {
const baseUrl = document.location.href.split("#", 1)[0];
if (!baseUrl.startsWith("file://")) {
newUrl = `${baseUrl}#${destination.hash}`;
}
}
if (shouldReplace) {
window.history.replaceState(newState, "", newUrl);
} else {
window.history.pushState(newState, "", newUrl);
}
}
#tryPushCurrentPosition(temporary = false) {
if (!this._position) {
return;
}
let position = this._position;
if (temporary) {
position = Object.assign(Object.create(null), this._position);
position.temporary = true;
}
if (!this._destination) {
this.#pushOrReplaceState(position);
return;
}
if (this._destination.temporary) {
this.#pushOrReplaceState(position, true);
return;
}
if (this._destination.hash === position.hash) {
return;
}
if (!this._destination.page && (POSITION_UPDATED_THRESHOLD <= 0 || this._numPositionUpdates <= POSITION_UPDATED_THRESHOLD)) {
return;
}
let forceReplace = false;
if (this._destination.page >= position.first && this._destination.page <= position.page) {
if (this._destination.dest !== undefined || !this._destination.first) {
return;
}
forceReplace = true;
}
this.#pushOrReplaceState(position, forceReplace);
}
#isValidPage(val) {
return Number.isInteger(val) && val > 0 && val <= this.linkService.pagesCount;
}
#isValidState(state, checkReload = false) {
if (!state) {
return false;
}
if (state.fingerprint !== this._fingerprint) {
if (checkReload) {
if (typeof state.fingerprint !== "string" || state.fingerprint.length !== this._fingerprint.length) {
return false;
}
const [perfEntry] = performance.getEntriesByType("navigation");
if (perfEntry?.type !== "reload") {
return false;
}
} else {
return false;
}
}
if (!Number.isInteger(state.uid) || state.uid < 0) {
return false;
}
if (state.destination === null || typeof state.destination !== "object") {
return false;
}
return true;
}
#updateInternalState(destination, uid, removeTemporary = false) {
if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout);
this._updateViewareaTimeout = null;
}
if (removeTemporary && destination?.temporary) {
delete destination.temporary;
}
this._destination = destination;
this._uid = uid;
this._maxUid = Math.max(this._maxUid, uid);
this._numPositionUpdates = 0;
}
#parseCurrentHash(checkNameddest = false) {
const hash = unescape(getCurrentHash()).substring(1);
const params = parseQueryString(hash);
const nameddest = params.get("nameddest") || "";
let page = params.get("page") | 0;
if (!this.#isValidPage(page) || checkNameddest && nameddest.length > 0) {
page = null;
}
return {
hash,
page,
rotation: this.linkService.rotation
};
}
#updateViewarea({
location
}) {
if (this._updateViewareaTimeout) {
clearTimeout(this._updateViewareaTimeout);
this._updateViewareaTimeout = null;
}
this._position = {
hash: location.pdfOpenParams.substring(1),
page: this.linkService.page,
first: location.pageNumber,
rotation: location.rotation
};
if (this._popStateInProgress) {
return;
}
if (POSITION_UPDATED_THRESHOLD > 0 && this._isPagesLoaded && this._destination && !this._destination.page) {
this._numPositionUpdates++;
}
if (UPDATE_VIEWAREA_TIMEOUT > 0) {
this._updateViewareaTimeout = setTimeout(() => {
if (!this._popStateInProgress) {
this.#tryPushCurrentPosition(true);
}
this._updateViewareaTimeout = null;
}, UPDATE_VIEWAREA_TIMEOUT);
}
}
#popState({
state
}) {
const newHash = getCurrentHash(),
hashChanged = this._currentHash !== newHash;
this._currentHash = newHash;
if (!state) {
this._uid++;
const {
hash,
page,
rotation
} = this.#parseCurrentHash();
this.#pushOrReplaceState({
hash,
page,
rotation
}, true);
return;
}
if (!this.#isValidState(state)) {
return;
}
this._popStateInProgress = true;
if (hashChanged) {
this._blockHashChange++;
waitOnEventOrTimeout({
target: window,
name: "hashchange",
delay: HASH_CHANGE_TIMEOUT
}).then(() => {
this._blockHashChange--;
});
}
const destination = state.destination;
this.#updateInternalState(destination, state.uid, true);
if (isValidRotation(destination.rotation)) {
this.linkService.rotation = destination.rotation;
}
if (destination.dest) {
this.linkService.goToDestination(destination.dest);
} else if (destination.hash) {
if (this.linkService.setHash) {
this.linkService.setHash(destination.hash);
}
} else if (destination.page) {
this.linkService.page = destination.page;
}
Promise.resolve().then(() => {
this._popStateInProgress = false;
});
}
#pageHide() {
if (!this._destination || this._destination.temporary) {
this.#tryPushCurrentPosition();
}
}
#bindEvents() {
if (this.#eventAbortController) {
return;
}
this.#eventAbortController = new AbortController();
const {
signal
} = this.#eventAbortController;
this.eventBus._on("updateviewarea", this.#updateViewarea.bind(this), {
signal
});
window.addEventListener("popstate", this.#popState.bind(this), {
signal
});
window.addEventListener("pagehide", this.#pageHide.bind(this), {
signal
});
}
#unbindEvents() {
this.#eventAbortController?.abort();
this.#eventAbortController = null;
}
}
function isDestHashesEqual(destHash, pushHash) {
if (typeof destHash !== "string" || typeof pushHash !== "string") {
return false;
}
if (destHash === pushHash) {
return true;
}
const nameddest = parseQueryString(destHash).get("nameddest");
if (nameddest === pushHash) {
return true;
}
return false;
}
function isDestArraysEqual(firstDest, secondDest) {
function isEntryEqual(first, second) {
if (typeof first !== typeof second) {
return false;
}
if (Array.isArray(first) || Array.isArray(second)) {
return false;
}
if (first !== null && typeof first === "object" && second !== null) {
if (Object.keys(first).length !== Object.keys(second).length) {
return false;
}
for (const key in first) {
if (!isEntryEqual(first[key], second[key])) {
return false;
}
}
return true;
}
return first === second || Number.isNaN(first) && Number.isNaN(second);
}
if (!(Array.isArray(firstDest) && Array.isArray(secondDest))) {
return false;
}
if (firstDest.length !== secondDest.length) {
return false;
}
for (let i = 0, ii = firstDest.length; i < ii; i++) {
if (!isEntryEqual(firstDest[i], secondDest[i])) {
return false;
}
}
return true;
}
;// ./web/pdf_layer_viewer.js
class PDFLayerViewer extends BaseTreeViewer {
constructor(options) {
super(options);
this.eventBus._on("optionalcontentconfigchanged", evt => {
this.#updateLayers(evt.promise);
});
this.eventBus._on("resetlayers", () => {
this.#updateLayers();
});
this.eventBus._on("togglelayerstree", this._toggleAllTreeItems.bind(this));
}
reset() {
super.reset();
this._optionalContentConfig = null;
this._optionalContentVisibility?.clear();
this._optionalContentVisibility = null;
}
_dispatchEvent(layersCount) {
this.eventBus.dispatch("layersloaded", {
source: this,
layersCount
});
}
_bindLink(element, {
groupId,
input
}) {
const setVisibility = () => {
const visible = input.checked;
this._optionalContentConfig.setVisibility(groupId, visible);
const cached = this._optionalContentVisibility.get(groupId);
if (cached) {
cached.visible = visible;
}
this.eventBus.dispatch("optionalcontentconfig", {
source: this,
promise: Promise.resolve(this._optionalContentConfig)
});
};
element.onclick = evt => {
if (evt.target === input) {
setVisibility();
return true;
} else if (evt.target !== element) {
return true;
}
input.checked = !input.checked;
setVisibility();
return false;
};
}
_setNestedName(element, {
name = null
}) {
if (typeof name === "string") {
element.textContent = this._normalizeTextContent(name);
return;
}
element.setAttribute("data-l10n-id", "pdfjs-additional-layers");
element.style.fontStyle = "italic";
this._l10n.translateOnce(element);
}
_addToggleButton(div, {
name = null
}) {
super._addToggleButton(div, name === null);
}
_toggleAllTreeItems() {
if (!this._optionalContentConfig) {
return;
}
super._toggleAllTreeItems();
}
render({
optionalContentConfig,
pdfDocument
}) {
if (this._optionalContentConfig) {
this.reset();
}
this._optionalContentConfig = optionalContentConfig || null;
this._pdfDocument = pdfDocument || null;
const groups = optionalContentConfig?.getOrder();
if (!groups) {
this._dispatchEvent(0);
return;
}
this._optionalContentVisibility = new Map();
const fragment = document.createDocumentFragment(),
queue = [{
parent: fragment,
groups
}];
let layersCount = 0,
hasAnyNesting = false;
while (queue.length > 0) {
const levelData = queue.shift();
for (const groupId of levelData.groups) {
const div = document.createElement("div");
div.className = "treeItem";
const element = document.createElement("a");
div.append(element);
if (typeof groupId === "object") {
hasAnyNesting = true;
this._addToggleButton(div, groupId);
this._setNestedName(element, groupId);
const itemsDiv = document.createElement("div");
itemsDiv.className = "treeItems";
div.append(itemsDiv);
queue.push({
parent: itemsDiv,
groups: groupId.order
});
} else {
const group = optionalContentConfig.getGroup(groupId);
const input = document.createElement("input");
this._bindLink(element, {
groupId,
input
});
input.type = "checkbox";
input.checked = group.visible;
this._optionalContentVisibility.set(groupId, {
input,
visible: input.checked
});
const label = document.createElement("label");
label.textContent = this._normalizeTextContent(group.name);
label.append(input);
element.append(label);
layersCount++;
}
levelData.parent.append(div);
}
}
this._finishRendering(fragment, layersCount, hasAnyNesting);
}
async #updateLayers(promise = null) {
if (!this._optionalContentConfig) {
return;
}
const pdfDocument = this._pdfDocument;
const optionalContentConfig = await (promise || pdfDocument.getOptionalContentConfig({
intent: "display"
}));
if (pdfDocument !== this._pdfDocument) {
return;
}
if (promise) {
for (const [groupId, cached] of this._optionalContentVisibility) {
const group = optionalContentConfig.getGroup(groupId);
if (group && cached.visible !== group.visible) {
cached.input.checked = cached.visible = !cached.visible;
}
}
return;
}
this.eventBus.dispatch("optionalcontentconfig", {
source: this,
promise: Promise.resolve(optionalContentConfig)
});
this.render({
optionalContentConfig,
pdfDocument: this._pdfDocument
});
}
}
;// ./web/pdf_outline_viewer.js
class PDFOutlineViewer extends BaseTreeViewer {
constructor(options) {
super(options);
this.linkService = options.linkService;
this.downloadManager = options.downloadManager;
this.eventBus._on("toggleoutlinetree", this._toggleAllTreeItems.bind(this));
this.eventBus._on("currentoutlineitem", this._currentOutlineItem.bind(this));
this.eventBus._on("pagechanging", evt => {
this._currentPageNumber = evt.pageNumber;
});
this.eventBus._on("pagesloaded", evt => {
this._isPagesLoaded = !!evt.pagesCount;
this._currentOutlineItemCapability?.resolve(this._isPagesLoaded);
});
this.eventBus._on("sidebarviewchanged", evt => {
this._sidebarView = evt.view;
});
}
reset() {
super.reset();
this._outline = null;
this._pageNumberToDestHashCapability = null;
this._currentPageNumber = 1;
this._isPagesLoaded = null;
this._currentOutlineItemCapability?.resolve(false);
this._currentOutlineItemCapability = null;
}
_dispatchEvent(outlineCount) {
this._currentOutlineItemCapability = Promise.withResolvers();
if (outlineCount === 0 || this._pdfDocument?.loadingParams.disableAutoFetch) {
this._currentOutlineItemCapability.resolve(false);
} else if (this._isPagesLoaded !== null) {
this._currentOutlineItemCapability.resolve(this._isPagesLoaded);
}
this.eventBus.dispatch("outlineloaded", {
source: this,
outlineCount,
currentOutlineItemPromise: this._currentOutlineItemCapability.promise
});
}
_bindLink(element, {
url,
newWindow,
action,
attachment,
dest,
setOCGState
}) {
const {
linkService
} = this;
if (url) {
linkService.addLinkAttributes(element, url, newWindow);
return;
}
if (action) {
element.href = linkService.getAnchorUrl("");
element.onclick = () => {
linkService.executeNamedAction(action);
return false;
};
return;
}
if (attachment) {
element.href = linkService.getAnchorUrl("");
element.onclick = () => {
this.downloadManager.openOrDownloadData(attachment.content, attachment.filename);
return false;
};
return;
}
if (setOCGState) {
element.href = linkService.getAnchorUrl("");
element.onclick = () => {
linkService.executeSetOCGState(setOCGState);
return false;
};
return;
}
element.href = linkService.getDestinationHash(dest);
element.onclick = evt => {
this._updateCurrentTreeItem(evt.target.parentNode);
if (dest) {
linkService.goToDestination(dest);
}
return false;
};
}
_setStyles(element, {
bold,
italic
}) {
if (bold) {
element.style.fontWeight = "bold";
}
if (italic) {
element.style.fontStyle = "italic";
}
}
_addToggleButton(div, {
count,
items
}) {
let hidden = false;
if (count < 0) {
let totalCount = items.length;
if (totalCount > 0) {
const queue = [...items];
while (queue.length > 0) {
const {
count: nestedCount,
items: nestedItems
} = queue.shift();
if (nestedCount > 0 && nestedItems.length > 0) {
totalCount += nestedItems.length;
queue.push(...nestedItems);
}
}
}
if (Math.abs(count) === totalCount) {
hidden = true;
}
}
super._addToggleButton(div, hidden);
}
_toggleAllTreeItems() {
if (!this._outline) {
return;
}
super._toggleAllTreeItems();
}
render({
outline,
pdfDocument
}) {
if (this._outline) {
this.reset();
}
this._outline = outline || null;
this._pdfDocument = pdfDocument || null;
if (!outline) {
this._dispatchEvent(0);
return;
}
const fragment = document.createDocumentFragment();
const queue = [{
parent: fragment,
items: outline
}];
let outlineCount = 0,
hasAnyNesting = false;
while (queue.length > 0) {
const levelData = queue.shift();
for (const item of levelData.items) {
const div = document.createElement("div");
div.className = "treeItem";
const element = document.createElement("a");
this._bindLink(element, item);
this._setStyles(element, item);
element.textContent = this._normalizeTextContent(item.title);
div.append(element);
if (item.items.length > 0) {
hasAnyNesting = true;
this._addToggleButton(div, item);
const itemsDiv = document.createElement("div");
itemsDiv.className = "treeItems";
div.append(itemsDiv);
queue.push({
parent: itemsDiv,
items: item.items
});
}
levelData.parent.append(div);
outlineCount++;
}
}
this._finishRendering(fragment, outlineCount, hasAnyNesting);
}
async _currentOutlineItem() {
if (!this._isPagesLoaded) {
throw new Error("_currentOutlineItem: All pages have not been loaded.");
}
if (!this._outline || !this._pdfDocument) {
return;
}
const pageNumberToDestHash = await this._getPageNumberToDestHash(this._pdfDocument);
if (!pageNumberToDestHash) {
return;
}
this._updateCurrentTreeItem(null);
if (this._sidebarView !== SidebarView.OUTLINE) {
return;
}
for (let i = this._currentPageNumber; i > 0; i--) {
const destHash = pageNumberToDestHash.get(i);
if (!destHash) {
continue;
}
const linkElement = this.container.querySelector(`a[href="${destHash}"]`);
if (!linkElement) {
continue;
}
this._scrollToCurrentTreeItem(linkElement.parentNode);
break;
}
}
async _getPageNumberToDestHash(pdfDocument) {
if (this._pageNumberToDestHashCapability) {
return this._pageNumberToDestHashCapability.promise;
}
this._pageNumberToDestHashCapability = Promise.withResolvers();
const pageNumberToDestHash = new Map(),
pageNumberNesting = new Map();
const queue = [{
nesting: 0,
items: this._outline
}];
while (queue.length > 0) {
const levelData = queue.shift(),
currentNesting = levelData.nesting;
for (const {
dest,
items
} of levelData.items) {
let explicitDest, pageNumber;
if (typeof dest === "string") {
explicitDest = await pdfDocument.getDestination(dest);
if (pdfDocument !== this._pdfDocument) {
return null;
}
} else {
explicitDest = dest;
}
if (Array.isArray(explicitDest)) {
const [destRef] = explicitDest;
if (destRef && typeof destRef === "object") {
pageNumber = pdfDocument.cachedPageNumber(destRef);
} else if (Number.isInteger(destRef)) {
pageNumber = destRef + 1;
}
if (Number.isInteger(pageNumber) && (!pageNumberToDestHash.has(pageNumber) || currentNesting > pageNumberNesting.get(pageNumber))) {
const destHash = this.linkService.getDestinationHash(dest);
pageNumberToDestHash.set(pageNumber, destHash);
pageNumberNesting.set(pageNumber, currentNesting);
}
}
if (items.length > 0) {
queue.push({
nesting: currentNesting + 1,
items
});
}
}
}
this._pageNumberToDestHashCapability.resolve(pageNumberToDestHash.size > 0 ? pageNumberToDestHash : null);
return this._pageNumberToDestHashCapability.promise;
}
}
;// ./web/pdf_presentation_mode.js
const DELAY_BEFORE_HIDING_CONTROLS = 3000;
const ACTIVE_SELECTOR = "pdfPresentationMode";
const CONTROLS_SELECTOR = "pdfPresentationModeControls";
const MOUSE_SCROLL_COOLDOWN_TIME = 50;
const PAGE_SWITCH_THRESHOLD = 0.1;
const SWIPE_MIN_DISTANCE_THRESHOLD = 50;
const SWIPE_ANGLE_THRESHOLD = Math.PI / 6;
class PDFPresentationMode {
#state = PresentationModeState.UNKNOWN;
#args = null;
#fullscreenChangeAbortController = null;
#windowAbortController = null;
constructor({
container,
pdfViewer,
eventBus
}) {
this.container = container;
this.pdfViewer = pdfViewer;
this.eventBus = eventBus;
this.contextMenuOpen = false;
this.mouseScrollTimeStamp = 0;
this.mouseScrollDelta = 0;
this.touchSwipeState = null;
}
async request() {
const {
container,
pdfViewer
} = this;
if (this.active || !pdfViewer.pagesCount || !container.requestFullscreen) {
return false;
}
this.#addFullscreenChangeListeners();
this.#notifyStateChange(PresentationModeState.CHANGING);
const promise = container.requestFullscreen();
this.#args = {
pageNumber: pdfViewer.currentPageNumber,
scaleValue: pdfViewer.currentScaleValue,
scrollMode: pdfViewer.scrollMode,
spreadMode: null,
annotationEditorMode: null
};
if (pdfViewer.spreadMode !== SpreadMode.NONE && !(pdfViewer.pageViewsReady && pdfViewer.hasEqualPageSizes)) {
console.warn("Ignoring Spread modes when entering PresentationMode, " + "since the document may contain varying page sizes.");
this.#args.spreadMode = pdfViewer.spreadMode;
}
if (pdfViewer.annotationEditorMode !== util.AnnotationEditorType.DISABLE) {
this.#args.annotationEditorMode = pdfViewer.annotationEditorMode;
}
try {
await promise;
pdfViewer.focus();
return true;
} catch {
this.#removeFullscreenChangeListeners();
this.#notifyStateChange(PresentationModeState.NORMAL);
}
return false;
}
get active() {
return this.#state === PresentationModeState.CHANGING || this.#state === PresentationModeState.FULLSCREEN;
}
#mouseWheel(evt) {
if (!this.active) {
return;
}
evt.preventDefault();
const delta = normalizeWheelEventDelta(evt);
const currentTime = Date.now();
const storedTime = this.mouseScrollTimeStamp;
if (currentTime > storedTime && currentTime - storedTime < MOUSE_SCROLL_COOLDOWN_TIME) {
return;
}
if (this.mouseScrollDelta > 0 && delta < 0 || this.mouseScrollDelta < 0 && delta > 0) {
this.#resetMouseScrollState();
}
this.mouseScrollDelta += delta;
if (Math.abs(this.mouseScrollDelta) >= PAGE_SWITCH_THRESHOLD) {
const totalDelta = this.mouseScrollDelta;
this.#resetMouseScrollState();
const success = totalDelta > 0 ? this.pdfViewer.previousPage() : this.pdfViewer.nextPage();
if (success) {
this.mouseScrollTimeStamp = currentTime;
}
}
}
#notifyStateChange(state) {
this.#state = state;
this.eventBus.dispatch("presentationmodechanged", {
source: this,
state
});
}
#enter() {
this.#notifyStateChange(PresentationModeState.FULLSCREEN);
this.container.classList.add(ACTIVE_SELECTOR);
setTimeout(() => {
this.pdfViewer.scrollMode = ScrollMode.PAGE;
if (this.#args.spreadMode !== null) {
this.pdfViewer.spreadMode = SpreadMode.NONE;
}
this.pdfViewer.currentPageNumber = this.#args.pageNumber;
this.pdfViewer.currentScaleValue = "page-fit";
if (this.#args.annotationEditorMode !== null) {
this.pdfViewer.annotationEditorMode = {
mode: util.AnnotationEditorType.NONE
};
}
}, 0);
this.#addWindowListeners();
this.#showControls();
this.contextMenuOpen = false;
document.getSelection().empty();
}
#exit() {
const pageNumber = this.pdfViewer.currentPageNumber;
this.container.classList.remove(ACTIVE_SELECTOR);
setTimeout(() => {
this.#removeFullscreenChangeListeners();
this.#notifyStateChange(PresentationModeState.NORMAL);
this.pdfViewer.scrollMode = this.#args.scrollMode;
if (this.#args.spreadMode !== null) {
this.pdfViewer.spreadMode = this.#args.spreadMode;
}
this.pdfViewer.currentScaleValue = this.#args.scaleValue;
this.pdfViewer.currentPageNumber = pageNumber;
if (this.#args.annotationEditorMode !== null) {
this.pdfViewer.annotationEditorMode = {
mode: this.#args.annotationEditorMode
};
}
this.#args = null;
}, 0);
this.#removeWindowListeners();
this.#hideControls();
this.#resetMouseScrollState();
this.contextMenuOpen = false;
}
_prepareFullscreenMode() {
const domElement = document.getElementsByClassName("zoom")[0].parentElement;
const parent = domElement.parentElement;
this.ngxContainer = parent;
for (let i = 0; i < parent.childElementCount; i++) {
if (parent.children.item(i) === domElement) {
this.ngxContainerIndex = i;
}
}
parent.removeChild(domElement);
document.body.append(domElement);
const siblings = document.body.children;
for (let i = 0; i < siblings.length; i++) {
const s = siblings.item(i);
if (s !== domElement && s instanceof HTMLElement) {
s.classList.add("hidden-by-fullscreen");
}
}
document.getElementById("sidebarContainer").classList.add("hidden-by-fullscreen");
document.getElementsByClassName("toolbar")[0].classList.add("hidden-by-fullscreen");
}
_tidyUpFullscreenMode() {
if (this.ngxContainer) {
const domElement = document.getElementsByClassName("zoom")[0].parentElement;
document.body.removeChild(domElement);
if (this.ngxContainerIndex >= this.ngxContainer.childElementCount) {
this.ngxContainer.append(domElement);
} else {
this.ngxContainer.insertBefore(domElement, this.ngxContainer.children.item(this.ngxContainerIndex));
}
this.ngxContainer = undefined;
const siblings = document.body.children;
for (let i = 0; i < siblings.length; i++) {
const s = siblings.item(i);
if (s !== domElement && s instanceof HTMLElement) {
if (s.classList.contains("hidden-by-fullscreen")) {
s.classList.remove("hidden-by-fullscreen");
}
}
}
document.getElementById("sidebarContainer").classList.remove("hidden-by-fullscreen");
document.getElementsByClassName("toolbar")[0].classList.remove("hidden-by-fullscreen");
}
}
#mouseDown(evt) {
if (this.contextMenuOpen) {
this.contextMenuOpen = false;
evt.preventDefault();
return;
}
if (evt.button !== 0) {
return;
}
if (evt.target.href && evt.target.parentNode?.hasAttribute("data-internal-link")) {
return;
}
evt.preventDefault();
if (evt.shiftKey) {
this.pdfViewer.previousPage();
} else {
this.pdfViewer.nextPage();
}
}
#contextMenu() {
this.contextMenuOpen = true;
}
#showControls() {
if (this.controlsTimeout) {
clearTimeout(this.controlsTimeout);
} else {
this.container.classList.add(CONTROLS_SELECTOR);
}
this.controlsTimeout = setTimeout(() => {
this.container.classList.remove(CONTROLS_SELECTOR);
delete this.controlsTimeout;
}, DELAY_BEFORE_HIDING_CONTROLS);
}
#hideControls() {
if (!this.controlsTimeout) {
return;
}
clearTimeout(this.controlsTimeout);
this.container.classList.remove(CONTROLS_SELECTOR);
delete this.controlsTimeout;
}
#resetMouseScrollState() {
this.mouseScrollTimeStamp = 0;
this.mouseScrollDelta = 0;
}
#touchSwipe(evt) {
if (!this.active) {
return;
}
if (evt.touches.length > 1) {
this.touchSwipeState = null;
return;
}
switch (evt.type) {
case "touchstart":
this.touchSwipeState = {
startX: evt.touches[0].pageX,
startY: evt.touches[0].pageY,
endX: evt.touches[0].pageX,
endY: evt.touches[0].pageY
};
break;
case "touchmove":
if (this.touchSwipeState === null) {
return;
}
this.touchSwipeState.endX = evt.touches[0].pageX;
this.touchSwipeState.endY = evt.touches[0].pageY;
evt.preventDefault();
break;
case "touchend":
if (this.touchSwipeState === null) {
return;
}
let delta = 0;
const dx = this.touchSwipeState.endX - this.touchSwipeState.startX;
const dy = this.touchSwipeState.endY - this.touchSwipeState.startY;
const absAngle = Math.abs(Math.atan2(dy, dx));
if (Math.abs(dx) > SWIPE_MIN_DISTANCE_THRESHOLD && (absAngle <= SWIPE_ANGLE_THRESHOLD || absAngle >= Math.PI - SWIPE_ANGLE_THRESHOLD)) {
delta = dx;
} else if (Math.abs(dy) > SWIPE_MIN_DISTANCE_THRESHOLD && Math.abs(absAngle - Math.PI / 2) <= SWIPE_ANGLE_THRESHOLD) {
delta = dy;
}
if (delta > 0) {
this.pdfViewer.previousPage();
} else if (delta < 0) {
this.pdfViewer.nextPage();
}
break;
}
}
#addWindowListeners() {
if (this.#windowAbortController) {
return;
}
this.#windowAbortController = new AbortController();
const {
signal
} = this.#windowAbortController;
const touchSwipeBind = this.#touchSwipe.bind(this);
window.addEventListener("mousemove", this.#showControls.bind(this), {
signal
});
window.addEventListener("mousedown", this.#mouseDown.bind(this), {
signal
});
window.addEventListener("wheel", this.#mouseWheel.bind(this), {
passive: false,
signal
});
window.addEventListener("keydown", this.#resetMouseScrollState.bind(this), {
signal
});
window.addEventListener("contextmenu", this.#contextMenu.bind(this), {
signal
});
window.addEventListener("touchstart", touchSwipeBind, {
signal
});
window.addEventListener("touchmove", touchSwipeBind, {
signal
});
window.addEventListener("touchend", touchSwipeBind, {
signal
});
}
#removeWindowListeners() {
this.#windowAbortController?.abort();
this.#windowAbortController = null;
}
#addFullscreenChangeListeners() {
if (this.#fullscreenChangeAbortController) {
return;
}
this.#fullscreenChangeAbortController = new AbortController();
window.addEventListener("fullscreenchange", () => {
if (document.fullscreenElement) {
this.#enter();
} else {
this.#exit();
}
}, {
signal: this.#fullscreenChangeAbortController.signal
});
}
#removeFullscreenChangeListeners() {
this.#fullscreenChangeAbortController?.abort();
this.#fullscreenChangeAbortController = null;
}
}
;// ./web/xfa_layer_builder.js
class XfaLayerBuilder {
constructor({
pdfPage,
annotationStorage = null,
linkService,
xfaHtml = null
}) {
this.pdfPage = pdfPage;
this.annotationStorage = annotationStorage;
this.linkService = linkService;
this.xfaHtml = xfaHtml;
this.div = null;
this._cancelled = false;
}
async render(viewport, intent = "display") {
if (intent === "print") {
const parameters = {
viewport: viewport.clone({
dontFlip: true
}),
div: this.div,
xfaHtml: this.xfaHtml,
annotationStorage: this.annotationStorage,
linkService: this.linkService,
intent
};
this.div = document.createElement("div");
parameters.div = this.div;
return pdf.XfaLayer.render(parameters);
}
const xfaHtml = await this.pdfPage.getXfa();
if (this._cancelled || !xfaHtml) {
return {
textDivs: []
};
}
const parameters = {
viewport: viewport.clone({
dontFlip: true
}),
div: this.div,
xfaHtml,
annotationStorage: this.annotationStorage,
linkService: this.linkService,
intent
};
if (this.div) {
return pdf.XfaLayer.update(parameters);
}
this.div = document.createElement("div");
parameters.div = this.div;
return pdf.XfaLayer.render(parameters);
}
cancel() {
this._cancelled = true;
}
hide() {
if (!this.div) {
return;
}
this.div.hidden = true;
}
}
;// ./web/print_utils.js
function getXfaHtmlForPrinting(printContainer, pdfDocument) {
const xfaHtml = pdfDocument.allXfaHtml;
const linkService = new SimpleLinkService();
const scale = Math.round(pdf.PixelsPerInch.PDF_TO_CSS_UNITS * 100) / 100;
for (const xfaPage of xfaHtml.children) {
const page = document.createElement("div");
page.className = "xfaPrintedPage";
printContainer.append(page);
const builder = new XfaLayerBuilder({
pdfPage: null,
annotationStorage: pdfDocument.annotationStorage,
linkService,
xfaHtml: xfaPage
});
const viewport = (0,pdf.getXfaPageViewport)(xfaPage, {
scale
});
builder.render(viewport, "print");
page.append(builder.div);
}
}
;// ./node_modules/canvas-size/dist/canvas-size.esm.js
function canvasTest(settings) {
const size = settings.sizes.shift();
const width = Math.max(Math.ceil(size[0]), 1);
const height = Math.max(Math.ceil(size[1]), 1);
const fill = [width - 1, height - 1, 1, 1];
const testTimeStart = performance.now();
const isWorker = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
let cropCvs, testCvs;
if (isWorker) {
cropCvs = new OffscreenCanvas(1, 1);
testCvs = new OffscreenCanvas(width, height);
} else {
cropCvs = document.createElement("canvas");
cropCvs.width = 1;
cropCvs.height = 1;
testCvs = document.createElement("canvas");
testCvs.width = width;
testCvs.height = height;
}
const cropCtx = cropCvs.getContext("2d");
const testCtx = testCvs.getContext("2d");
if (testCtx) {
testCtx.fillRect.apply(testCtx, fill);
cropCtx.drawImage(testCvs, width - 1, height - 1, 1, 1, 0, 0, 1, 1);
}
const isTestPass = cropCtx && cropCtx.getImageData(0, 0, 1, 1).data[3] !== 0;
const testTime = parseInt(performance.now() - testTimeStart);
[cropCvs, testCvs].forEach(cvs => {
cvs.height = 0;
cvs.width = 0;
});
if (isWorker) {
postMessage({
width: width,
height: height,
testTime: testTime,
isTestPass: isTestPass
});
if (!isTestPass && settings.sizes.length) {
setTimeout(() => {
canvasTest(settings);
}, 0);
}
} else if (isTestPass) {
settings.onSuccess({
width: width,
height: height,
testTime: testTime
});
} else {
settings.onError({
width: width,
height: height,
testTime: testTime
});
if (settings.sizes.length) {
setTimeout(() => {
canvasTest(settings);
}, 0);
}
}
return isTestPass;
}
const testSizes = {
area: [16384, 14188, 11402, 11180, 10836, 8192, 4096, 1],
height: [8388607, 65535, 32767, 16384, 8192, 4096, 1],
width: [4194303, 65535, 32767, 16384, 8192, 4096, 1]
};
const defaults = {
max: null,
min: 1,
sizes: [],
step: 1024,
useWorker: false,
onError: Function.prototype,
onSuccess: Function.prototype
};
const workerJobs = {};
function createSizesArray(settings) {
const isArea = settings.width === settings.height;
const isWidth = settings.height === 1;
const isHeight = settings.width === 1;
const sizes = [];
if (!settings.width || !settings.height) {
settings.sizes.forEach(testSize => {
const width = isArea || isWidth ? testSize : 1;
const height = isArea || isHeight ? testSize : 1;
sizes.push([width, height]);
});
} else {
const testMin = settings.min || defaults.min;
const testStep = settings.step || defaults.step;
let testSize = Math.max(settings.width, settings.height);
while (testSize >= testMin) {
const width = isArea || isWidth ? testSize : 1;
const height = isArea || isHeight ? testSize : 1;
sizes.push([width, height]);
testSize -= testStep;
}
}
return sizes;
}
function handleMethod(settings) {
const isBrowser = typeof window !== "undefined";
const hasPromiseSupport = isBrowser && "Promise" in window;
const hasCanvasSupport = isBrowser && "HTMLCanvasElement" in window;
const hasOffscreenCanvasSupport = isBrowser && "OffscreenCanvas" in window;
const jobID = URL.createObjectURL(new Blob([])).slice(-36);
const totalTimeStart = performance.now();
const {
onError: onError,
onSuccess: onSuccess,
...settingsWithoutCallbacks
} = settings;
const getTotalTime = () => parseInt(performance.now() - totalTimeStart);
let worker = null;
if (!hasCanvasSupport) {
return false;
}
if (settings.useWorker && hasOffscreenCanvasSupport) {
const js = `\n var canvasTest = ${canvasTest.toString()};\n onmessage = function(e) {\n canvasTest(e.data);\n };\n `;
const blob = new Blob([js], {
type: "application/javascript"
});
const blobURL = URL.createObjectURL(blob);
worker = new Worker(blobURL);
URL.revokeObjectURL(blobURL);
worker.onmessage = function (e) {
const {
width: width,
height: height,
testTime: testTime,
isTestPass: isTestPass
} = e.data;
const results = {
width: width,
height: height,
testTime: testTime,
totalTime: getTotalTime()
};
if (isTestPass) {
workerJobs[jobID].onSuccess(results);
delete workerJobs[jobID];
} else {
workerJobs[jobID].onError(results);
}
};
}
if (hasPromiseSupport) {
return new Promise(resolve => {
const promiseSettings = {
...settings,
onError(_ref) {
let {
width: width,
height: height,
testTime: testTime
} = _ref;
const results = {
width: width,
height: height,
testTime: testTime,
totalTime: getTotalTime()
};
let isLastTest;
if (settings.sizes.length === 0) {
isLastTest = true;
} else {
const [[lastWidth, lastHeight]] = settings.sizes.slice(-1);
isLastTest = width === lastWidth && height === lastHeight;
}
onError(results);
if (isLastTest) {
resolve({
...results,
success: false
});
}
},
onSuccess(_ref2) {
let {
width: width,
height: height,
testTime: testTime
} = _ref2;
const results = {
width: width,
height: height,
testTime: testTime,
totalTime: getTotalTime()
};
onSuccess(results);
resolve({
...results,
success: true
});
}
};
if (worker) {
const {
onError: onError,
onSuccess: onSuccess
} = promiseSettings;
workerJobs[jobID] = {
onError: onError,
onSuccess: onSuccess
};
worker.postMessage(settingsWithoutCallbacks);
} else {
canvasTest(promiseSettings);
}
});
} else {
if (worker) {
workerJobs[jobID] = {
onError: onError,
onSuccess: onSuccess
};
worker.postMessage(settingsWithoutCallbacks);
} else {
return canvasTest(settings);
}
}
}
const canvasSize = {
maxArea() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const sizes = createSizesArray({
width: options.max,
height: options.max,
min: options.min,
step: options.step,
sizes: [...testSizes.area]
});
const settings = {
...defaults,
...options,
sizes: sizes
};
return handleMethod(settings);
},
maxHeight() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const sizes = createSizesArray({
width: 1,
height: options.max,
min: options.min,
step: options.step,
sizes: [...testSizes.height]
});
const settings = {
...defaults,
...options,
sizes: sizes
};
return handleMethod(settings);
},
maxWidth() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const sizes = createSizesArray({
width: options.max,
height: 1,
min: options.min,
step: options.step,
sizes: [...testSizes.width]
});
const settings = {
...defaults,
...options,
sizes: sizes
};
return handleMethod(settings);
},
test() {
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
const settings = {
...defaults,
...options
};
settings.sizes = [...settings.sizes];
if (settings.width && settings.height) {
settings.sizes = [[settings.width, settings.height]];
}
return handleMethod(settings);
}
};
;// ./web/max_canvas_size.js
class MaxCanvasSize {
static maxWidth = null;
static maxHeight = null;
static maxArea = null;
static async determineMaxArea() {
const {
success,
width,
height
} = await canvasSize.maxArea({
useWorker: true
});
if (!success) {
MaxCanvasSize.maxWidth = 4096;
MaxCanvasSize.maxArea = 16777216;
return 4096;
}
MaxCanvasSize.maxArea = width * height;
return MaxCanvasSize.maxArea;
}
static async determineMaxWidth() {
const {
width
} = await canvasSize.maxWidth({
useWorker: true
});
MaxCanvasSize.maxWidth = width;
return MaxCanvasSize.maxWidth;
}
static async determineMaxHeight() {
const {
height
} = await canvasSize.maxHeight({
useWorker: true
});
MaxCanvasSize.maxHeight = height;
return MaxCanvasSize.maxHeight;
}
static async determineMaxDimensions() {
if (MaxCanvasSize.maxWidth) {
return MaxCanvasSize.maxWidth;
}
await this.determineMaxArea();
await this.determineMaxHeight();
await this.determineMaxWidth();
return MaxCanvasSize.maxWidth;
}
static async reduceToMaxCanvasSize(width, height) {
let divisor = 1;
if (width >= 4096 || height >= 4096) {
await this.determineMaxDimensions();
divisor = Math.max(width / MaxCanvasSize.maxWidth, height / MaxCanvasSize.maxHeight, Math.sqrt(width * height / MaxCanvasSize.maxArea));
if (divisor > 1) {
divisor = Math.ceil(1 + 100 * divisor) / 100;
}
}
return divisor;
}
}
;// ./web/pdf_print_service.js
let activeService = null;
let dialog = null;
let overlayManager = null;
let viewerApp = {
initialized: false
};
async function renderPage(activeServiceOnEntry, pdfDocument, pageNumber, size, printResolution, optionalContentConfigPromise, printAnnotationStoragePromise) {
const scratchCanvas = activeService.scratchCanvas;
let PRINT_UNITS = printResolution / pdf.PixelsPerInch.PDF;
const canvasWidth = Math.floor(size.width * PRINT_UNITS);
const canvasHeight = Math.floor(size.height * PRINT_UNITS);
const divisor = await MaxCanvasSize.reduceToMaxCanvasSize(canvasWidth, canvasHeight);
if (divisor > 1) {
const reduction = Math.round((divisor - 1) * 100);
PRINT_UNITS = Math.ceil(0.95 * PRINT_UNITS / divisor);
const dpi = PRINT_UNITS * pdf.PixelsPerInch.PDF;
(0,util.warn)(`Page ${pageNumber}: Reduced the maximum resolution by ${reduction}% because the browser can't render larger canvases. The resolution is now ${dpi} DPI.`);
}
scratchCanvas.width = Math.floor(size.width * PRINT_UNITS);
scratchCanvas.height = Math.floor(size.height * PRINT_UNITS);
const ctx = scratchCanvas.getContext("2d");
ctx.save();
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, scratchCanvas.width, scratchCanvas.height);
ctx.restore();
return Promise.all([pdfDocument.getPage(pageNumber), printAnnotationStoragePromise]).then(function ([pdfPage, printAnnotationStorage]) {
const renderContext = {
canvasContext: ctx,
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport: pdfPage.getViewport({
scale: 1,
rotation: size.rotation
}),
intent: "print",
annotationMode: util.AnnotationMode.ENABLE_STORAGE,
optionalContentConfigPromise,
printAnnotationStorage
};
const renderTask = pdfPage.render(renderContext);
return renderTask.promise.catch(reason => {
if (!(reason instanceof pdf.RenderingCancelledException)) {
console.error(reason);
}
throw reason;
});
});
}
class PDFPrintService {
constructor({
pdfDocument,
pagesOverview,
printResolution,
printAnnotationStoragePromise = null,
eventBus,
cspPolicyService
}, isInPDFPrintRange, filteredPageCount) {
this.pdfDocument = pdfDocument;
this.pagesOverview = pagesOverview;
this._printResolution = printResolution || 150;
this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
intent: "print"
});
this._printAnnotationStoragePromise = printAnnotationStoragePromise || Promise.resolve();
this.currentPage = -1;
this.scratchCanvas = document.createElement("canvas");
this.eventBus = eventBus;
this.filteredPageCount = filteredPageCount;
this.isInPDFPrintRange = isInPDFPrintRange;
this.cspPolicyService = cspPolicyService;
}
layout() {
this.throwIfInactive();
const body = document.querySelector("body");
body.setAttribute("data-pdfjsprinting", true);
const html = document.querySelector("html");
html.setAttribute("data-pdfjsprinting", true);
const {
width,
height
} = this.pagesOverview[0];
const hasEqualPageSizes = this.pagesOverview.every(size => size.width === width && size.height === height);
if (!hasEqualPageSizes) {
ngx_console.NgxConsole.warn("Not all pages have the same size. The printed result may be incorrect!");
}
this.pageStyleSheet = document.createElement("style");
const textContent = `@page { size: ${width}pt ${height}pt;}`;
if (this.cspPolicyService) {
this.cspPolicyService.addTrustedCSS(this.pageStyleSheet, textContent);
} else {
this.pageStyleSheet.textContent = textContent;
}
body.append(this.pageStyleSheet);
}
destroy() {
if (activeService !== this) {
return;
}
this.printContainer.remove();
this.printContainer = null;
const body = document.querySelector("body");
body.removeAttribute("data-pdfjsprinting");
const html = document.querySelector("html");
html.removeAttribute("data-pdfjsprinting");
if (this.pageStyleSheet) {
this.pageStyleSheet.remove();
this.pageStyleSheet = null;
}
this.scratchCanvas.width = this.scratchCanvas.height = 0;
this.scratchCanvas = null;
activeService = null;
if (overlayManager.active === dialog) {
overlayManager.close(dialog);
overlayManager.unregister(dialog);
overlayPromise = undefined;
}
}
renderPages() {
if (this.pdfDocument.isPureXfa) {
getXfaHtmlForPrinting(this.printContainer, this.pdfDocument);
return Promise.resolve();
}
const pageCount = this.pagesOverview.length;
const renderNextPage = (resolve, reject) => {
this.throwIfInactive();
while (true) {
++this.currentPage;
if (this.currentPage >= pageCount) {
break;
}
if (!this.isInPDFPrintRange || this.isInPDFPrintRange(this.currentPage)) {
break;
}
}
if (this.currentPage >= pageCount) {
renderProgress(this.filteredPageCount ?? pageCount, this.filteredPageCount ?? pageCount, this.eventBus);
resolve();
return;
}
const index = this.currentPage;
renderProgress(index, this.filteredPageCount ?? pageCount, this.eventBus);
renderPage(this, this.pdfDocument, index + 1, this.pagesOverview[index], this._printResolution, this._optionalContentConfigPromise, this._printAnnotationStoragePromise).then(this.useRenderedPage.bind(this)).then(function () {
renderNextPage(resolve, reject);
}, reject);
};
return new Promise(renderNextPage);
}
useRenderedPage() {
this.throwIfInactive();
const img = document.createElement("img");
this.scratchCanvas.toBlob(blob => {
img.src = URL.createObjectURL(blob);
});
const wrapper = document.createElement("div");
wrapper.className = "printedPage";
wrapper.append(img);
this.printContainer.append(wrapper);
const {
promise,
resolve,
reject
} = Promise.withResolvers();
img.onload = resolve;
img.onerror = reject;
promise.catch(() => {}).then(() => {
URL.revokeObjectURL(img.src);
});
return promise;
}
performPrint() {
this.throwIfInactive();
return new Promise(resolve => {
setTimeout(() => {
if (!this.active) {
resolve();
return;
}
print.call(window);
const isIOS = navigator.platform && ["iPad Simulator", "iPhone Simulator", "iPod Simulator", "iPad", "iPhone", "iPod"].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
setTimeout(resolve, isIOS ? 1500 : 20);
}, 0);
});
}
get active() {
return this === activeService;
}
throwIfInactive() {
if (!this.active) {
throw new Error("This print request was cancelled or completed.");
}
}
}
const print = window.print;
function printPdf() {
if (!this.enablePrint) {
return;
}
if (activeService) {
ngx_console.NgxConsole.warn("Ignored this.printPDF() because of a pending print job.");
return;
}
ensureOverlay().then(function () {
if (activeService) {
overlayManager.open(dialog);
}
});
try {
dispatchEvent("beforeprint");
} finally {
if (!activeService) {
ngx_console.NgxConsole.error("Expected print service to be initialized.");
ensureOverlay().then(function () {
if (overlayManager.active === dialog) {
overlayManager.close(dialog);
}
});
return;
}
const activeServiceOnEntry = activeService;
const printContainer = document.createElement("div");
printContainer.id = "printContainer";
document.body.append(printContainer);
activeServiceOnEntry.printContainer = printContainer;
activeService.renderPages().then(function () {
const progressIndicator = document.getElementById("printServiceDialog");
if (progressIndicator) {
progressIndicator.classList.add("hidden");
}
return activeServiceOnEntry.performPrint();
}).catch(function () {}).then(function () {
if (activeServiceOnEntry.active) {
abort();
}
});
}
}
function dispatchEvent(eventType) {
const event = new CustomEvent(eventType, {
bubbles: false,
cancelable: false,
detail: "custom"
});
window.dispatchEvent(event);
}
function abort() {
if (activeService) {
activeService.destroy();
dispatchEvent("afterprint");
}
}
function renderProgress(index, total, eventBus) {
dialog = document.getElementById("printServiceDialog");
const progress = Math.round(100 * index / total);
const progressBar = dialog.querySelector("progress");
const progressPerc = dialog.querySelector(".relative-progress");
progressBar.value = progress;
progressPerc.setAttribute("data-l10n-args", JSON.stringify({
progress
}));
eventBus.dispatch("progress", {
source: this,
type: "print",
total,
page: index,
percent: 100 * index / total
});
}
window.addEventListener("keydown", function (event) {
if (event.keyCode === 80 && (event.ctrlKey || event.metaKey) && !event.altKey && (!event.shiftKey || window.chrome || window.opera)) {
window.print();
event.preventDefault();
event.stopImmediatePropagation();
}
}, true);
if ("onbeforeprint" in window) {
const stopPropagationIfNeeded = function (event) {
if (event.detail !== "custom") {
event.stopImmediatePropagation();
}
};
window.addEventListener("beforeprint", stopPropagationIfNeeded);
window.addEventListener("afterprint", stopPropagationIfNeeded);
}
let overlayPromise;
function ensureOverlay() {
if (!overlayPromise) {
overlayManager = viewerApp.overlayManager;
if (!overlayManager) {
throw new Error("The overlay manager has not yet been initialized.");
}
dialog = document.getElementById("printServiceDialog");
overlayPromise = overlayManager.register(dialog, true);
document.getElementById("printCancel").onclick = abort;
dialog.addEventListener("close", abort);
}
return overlayPromise;
}
class PDFPrintServiceFactory {
static enablePrint = true;
static initGlobals(app) {
viewerApp = app;
}
static get supportsPrinting() {
return (0,util.shadow)(this, "supportsPrinting", true);
}
static createPrintService(params) {
if (activeService) {
throw new Error("The print service is created and active.");
}
if (!PDFPrintServiceFactory.enablePrint) {
console.debug("The print service is disabled.");
}
return activeService = new PDFPrintService(params, PDFPrintServiceFactory.isInPDFPrintRange, PDFPrintServiceFactory.filteredPageCount);
}
}
;// ./web/pdf_rendering_queue.js
const CLEANUP_TIMEOUT = 30000;
class PDFRenderingQueue {
constructor() {
this.pdfViewer = null;
this.pdfThumbnailViewer = null;
this.onIdle = null;
this.highestPriorityPage = null;
this.idleTimeout = null;
this.printing = false;
this.isThumbnailViewEnabled = false;
Object.defineProperty(this, "hasViewer", {
value: () => !!this.pdfViewer
});
}
setViewer(pdfViewer) {
this.pdfViewer = pdfViewer;
}
setThumbnailViewer(pdfThumbnailViewer) {
this.pdfThumbnailViewer = pdfThumbnailViewer;
}
isHighestPriority(view) {
return this.highestPriorityPage === view.renderingId;
}
renderHighestPriority(currentlyVisiblePages) {
if (this.idleTimeout) {
clearTimeout(this.idleTimeout);
this.idleTimeout = null;
}
if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
return;
}
if (this.isThumbnailViewEnabled && this.pdfThumbnailViewer?.forceRendering()) {
return;
}
if (this.printing) {
return;
}
if (this.onIdle) {
this.idleTimeout = setTimeout(this.onIdle.bind(this), CLEANUP_TIMEOUT);
}
}
getHighestPriority(visible, views, scrolledDown, preRenderExtra = false) {
const visibleViews = visible.views,
numVisible = visibleViews.length;
if (numVisible === 0) {
return null;
}
for (let i = 0; i < numVisible; i++) {
const view = visibleViews[i].view;
if (!this.isViewFinished(view)) {
return view;
}
}
const firstId = visible.first.id,
lastId = visible.last.id;
if (lastId - firstId + 1 > numVisible) {
const visibleIds = visible.ids;
for (let i = 1, ii = lastId - firstId; i < ii; i++) {
const holeId = scrolledDown ? firstId + i : lastId - i;
if (visibleIds.has(holeId)) {
continue;
}
const holeView = views[holeId - 1];
if (!this.isViewFinished(holeView)) {
return holeView;
}
}
}
let preRenderIndex = scrolledDown ? lastId : firstId - 2;
let preRenderView = views[preRenderIndex];
if (preRenderView && !this.isViewFinished(preRenderView)) {
return preRenderView;
}
if (preRenderExtra) {
preRenderIndex += scrolledDown ? 1 : -1;
preRenderView = views[preRenderIndex];
if (preRenderView && !this.isViewFinished(preRenderView)) {
return preRenderView;
}
}
return null;
}
isViewFinished(view) {
return view.renderingState === RenderingStates.FINISHED;
}
renderView(view) {
switch (view.renderingState) {
case RenderingStates.FINISHED:
return false;
case RenderingStates.PAUSED:
this.highestPriorityPage = view.renderingId;
view.resume();
break;
case RenderingStates.RUNNING:
this.highestPriorityPage = view.renderingId;
break;
case RenderingStates.INITIAL:
this.highestPriorityPage = view.renderingId;
view.draw().finally(() => {
this.renderHighestPriority();
}).catch(reason => {
if (reason instanceof pdf.RenderingCancelledException) {
return;
}
ngx_console.NgxConsole.error("renderView:", reason);
});
break;
}
return true;
}
}
;// ./web/pdf_scripting_manager.js
class PDFScriptingManager {
#closeCapability = null;
#destroyCapability = null;
#docProperties = null;
#eventAbortController = null;
#eventBus = null;
#externalServices = null;
#pdfDocument = null;
#pdfViewer = null;
#ready = false;
#scripting = null;
#willPrintCapability = null;
constructor({
eventBus,
externalServices = null,
docProperties = null
}) {
this.#eventBus = eventBus;
this.#externalServices = externalServices;
this.#docProperties = docProperties;
}
setViewer(pdfViewer) {
this.#pdfViewer = pdfViewer;
}
async setDocument(pdfDocument) {
if (this.#pdfDocument) {
await this.#destroyScripting();
}
this.#pdfDocument = pdfDocument;
if (!pdfDocument) {
return;
}
const [objects, calculationOrder, docActions] = await Promise.all([pdfDocument.getFieldObjects(), pdfDocument.getCalculationOrderIds(), pdfDocument.getJSActions()]);
if (!objects && !docActions) {
await this.#destroyScripting();
return;
}
if (pdfDocument !== this.#pdfDocument) {
return;
}
try {
this.#scripting = this.#initScripting();
} catch (error) {
ngx_console.NgxConsole.error("setDocument:", error);
await this.#destroyScripting();
return;
}
const eventBus = this.#eventBus;
this.#eventAbortController = new AbortController();
const {
signal
} = this.#eventAbortController;
eventBus._on("updatefromsandbox", event => {
if (event?.source === window) {
this.#updateFromSandbox(event.detail);
}
}, {
signal
});
eventBus._on("dispatcheventinsandbox", event => {
this.#scripting?.dispatchEventInSandbox(event.detail);
}, {
signal
});
eventBus._on("pagechanging", ({
pageNumber,
previous
}) => {
if (pageNumber === previous) {
return;
}
this.#dispatchPageClose(previous);
this.#dispatchPageOpen(pageNumber);
}, {
signal
});
eventBus._on("pagerendered", ({
pageNumber
}) => {
if (!this._pageOpenPending.has(pageNumber)) {
return;
}
if (pageNumber !== this.#pdfViewer.currentPageNumber) {
return;
}
this.#dispatchPageOpen(pageNumber);
}, {
signal
});
eventBus._on("pagesdestroy", async () => {
await this.#dispatchPageClose(this.#pdfViewer.currentPageNumber);
await this.#scripting?.dispatchEventInSandbox({
id: "doc",
name: "WillClose"
});
this.#closeCapability?.resolve();
}, {
signal
});
try {
const docProperties = await this.#docProperties(pdfDocument);
if (pdfDocument !== this.#pdfDocument) {
return;
}
await this.#scripting.createSandbox({
objects,
calculationOrder,
appInfo: {
platform: navigator.platform,
language: navigator.language
},
docInfo: {
...docProperties,
actions: docActions
}
});
eventBus.dispatch("sandboxcreated", {
source: this
});
} catch (error) {
ngx_console.NgxConsole.error("setDocument:", error);
await this.#destroyScripting();
return;
}
await this.#scripting?.dispatchEventInSandbox({
id: "doc",
name: "Open"
});
await this.#dispatchPageOpen(this.#pdfViewer.currentPageNumber, true);
Promise.resolve().then(() => {
if (pdfDocument === this.#pdfDocument) {
this.#ready = true;
}
});
}
async dispatchWillSave() {
return this.#scripting?.dispatchEventInSandbox({
id: "doc",
name: "WillSave"
});
}
async dispatchDidSave() {
return this.#scripting?.dispatchEventInSandbox({
id: "doc",
name: "DidSave"
});
}
async dispatchWillPrint() {
if (!this.#scripting) {
return;
}
await this.#willPrintCapability?.promise;
this.#willPrintCapability = Promise.withResolvers();
try {
await this.#scripting.dispatchEventInSandbox({
id: "doc",
name: "WillPrint"
});
} catch (ex) {
this.#willPrintCapability.resolve();
this.#willPrintCapability = null;
throw ex;
}
await this.#willPrintCapability.promise;
}
async dispatchDidPrint() {
return this.#scripting?.dispatchEventInSandbox({
id: "doc",
name: "DidPrint"
});
}
get destroyPromise() {
return this.#destroyCapability?.promise || null;
}
get ready() {
return this.#ready;
}
get _pageOpenPending() {
return (0,util.shadow)(this, "_pageOpenPending", new Set());
}
get _visitedPages() {
return (0,util.shadow)(this, "_visitedPages", new Map());
}
async #updateFromSandbox(detail) {
const pdfViewer = this.#pdfViewer;
const isInPresentationMode = pdfViewer.isInPresentationMode || pdfViewer.isChangingPresentationMode;
const {
id,
siblings,
command,
value
} = detail;
if (!id) {
switch (command) {
case "clear":
ngx_console.NgxConsole.clear();
break;
case "error":
ngx_console.NgxConsole.error(value);
break;
case "layout":
if (!isInPresentationMode) {
const modes = apiPageLayoutToViewerModes(value);
pdfViewer.spreadMode = modes.spreadMode;
}
break;
case "page-num":
pdfViewer.currentPageNumber = value + 1;
break;
case "print":
await pdfViewer.pagesPromise;
this.#eventBus.dispatch("print", {
source: this
});
break;
case "println":
ngx_console.NgxConsole.log(value);
break;
case "zoom":
if (!isInPresentationMode) {
pdfViewer.currentScaleValue = value;
}
break;
case "SaveAs":
this.#eventBus.dispatch("download", {
source: this
});
break;
case "FirstPage":
pdfViewer.currentPageNumber = 1;
break;
case "LastPage":
pdfViewer.currentPageNumber = pdfViewer.pagesCount;
break;
case "NextPage":
pdfViewer.nextPage();
break;
case "PrevPage":
pdfViewer.previousPage();
break;
case "ZoomViewIn":
if (!isInPresentationMode) {
pdfViewer.increaseScale();
}
break;
case "ZoomViewOut":
if (!isInPresentationMode) {
pdfViewer.decreaseScale();
}
break;
case "WillPrintFinished":
this.#willPrintCapability?.resolve();
this.#willPrintCapability = null;
break;
}
return;
}
if (isInPresentationMode && detail.focus) {
return;
}
delete detail.id;
delete detail.siblings;
const ids = siblings ? [id, ...siblings] : [id];
for (const elementId of ids) {
const element = document.querySelector(`[data-element-id="${elementId}"]`);
if (element) {
element.dispatchEvent(new CustomEvent("updatefromsandbox", {
detail
}));
} else {
this.#pdfDocument?.annotationStorage.setValue(elementId, detail);
}
}
}
async #dispatchPageOpen(pageNumber, initialize = false) {
const pdfDocument = this.#pdfDocument,
visitedPages = this._visitedPages;
if (initialize) {
this.#closeCapability = Promise.withResolvers();
}
if (!this.#closeCapability) {
return;
}
const pageView = this.#pdfViewer.getPageView(pageNumber - 1);
if (pageView?.renderingState !== RenderingStates.FINISHED) {
this._pageOpenPending.add(pageNumber);
return;
}
this._pageOpenPending.delete(pageNumber);
const actionsPromise = (async () => {
const actions = await (!visitedPages.has(pageNumber) ? pageView.pdfPage?.getJSActions() : null);
if (pdfDocument !== this.#pdfDocument) {
return;
}
await this.#scripting?.dispatchEventInSandbox({
id: "page",
name: "PageOpen",
pageNumber,
actions
});
})();
visitedPages.set(pageNumber, actionsPromise);
}
async #dispatchPageClose(pageNumber) {
const pdfDocument = this.#pdfDocument,
visitedPages = this._visitedPages;
if (!this.#closeCapability) {
return;
}
if (this._pageOpenPending.has(pageNumber)) {
return;
}
const actionsPromise = visitedPages.get(pageNumber);
if (!actionsPromise) {
return;
}
visitedPages.set(pageNumber, null);
await actionsPromise;
if (pdfDocument !== this.#pdfDocument) {
return;
}
await this.#scripting?.dispatchEventInSandbox({
id: "page",
name: "PageClose",
pageNumber
});
}
#initScripting() {
this.#destroyCapability = Promise.withResolvers();
if (this.#scripting) {
throw new Error("#initScripting: Scripting already exists.");
}
return this.#externalServices.createScripting();
}
async #destroyScripting() {
if (!this.#scripting) {
this.#pdfDocument = null;
this.#destroyCapability?.resolve();
return;
}
if (this.#closeCapability) {
await Promise.race([this.#closeCapability.promise, new Promise(resolve => {
setTimeout(resolve, 1000);
})]).catch(() => {});
this.#closeCapability = null;
}
this.#pdfDocument = null;
try {
await this.#scripting.destroySandbox();
} catch {}
this.#willPrintCapability?.reject(new Error("Scripting destroyed."));
this.#willPrintCapability = null;
this.#eventAbortController?.abort();
this.#eventAbortController = null;
this._pageOpenPending.clear();
this._visitedPages.clear();
this.#scripting = null;
this.#ready = false;
this.#destroyCapability?.resolve();
}
}
;// ./web/pdf_sidebar.js
const SIDEBAR_WIDTH_VAR = "--sidebar-width";
const SIDEBAR_MIN_WIDTH = 200;
const SIDEBAR_RESIZING_CLASS = "sidebarResizing";
const UI_NOTIFICATION_CLASS = "pdfSidebarNotification";
class PDFSidebar {
#isRTL = false;
#mouseAC = null;
#outerContainerWidth = null;
#width = null;
constructor({
elements,
eventBus,
l10n
}) {
this.isOpen = false;
this.active = SidebarView.THUMBS;
this.isInitialViewSet = false;
this.isInitialEventDispatched = false;
this.onToggled = null;
this.onUpdateThumbnails = null;
this.outerContainer = elements.outerContainer;
this.sidebarContainer = elements.sidebarContainer;
this.toggleButton = elements.toggleButton;
this.resizer = elements.resizer;
this.thumbnailButton = elements.thumbnailButton;
this.outlineButton = elements.outlineButton;
this.attachmentsButton = elements.attachmentsButton;
this.layersButton = elements.layersButton;
this.thumbnailView = elements.thumbnailView;
this.outlineView = elements.outlineView;
this.attachmentsView = elements.attachmentsView;
this.layersView = elements.layersView;
this._currentOutlineItemButton = elements.currentOutlineItemButton;
this.eventBus = eventBus;
this.#isRTL = l10n.getDirection() === "rtl";
this.#addEventListeners();
}
reset() {
this.isInitialViewSet = false;
this.isInitialEventDispatched = false;
this.#hideUINotification(true);
this.switchView(SidebarView.THUMBS);
this.outlineButton.disabled = false;
this.attachmentsButton.disabled = false;
this.layersButton.disabled = false;
this.outlineButton.hidden = false;
this.attachmentsButton.hidden = false;
this.layersButton.hidden = false;
this._currentOutlineItemButton.disabled = true;
}
get visibleView() {
return this.isOpen ? this.active : SidebarView.NONE;
}
setInitialView(view = SidebarView.NONE) {
if (this.isInitialViewSet) {
return;
}
this.isInitialViewSet = true;
if (view === SidebarView.NONE || view === SidebarView.UNKNOWN) {
this.#dispatchEvent();
return;
}
this.switchView(view, true);
if (!this.isInitialEventDispatched) {
this.#dispatchEvent();
}
}
switchView(view, forceOpen = false) {
const isViewChanged = view !== this.active;
let forceRendering = false;
switch (view) {
case SidebarView.NONE:
if (this.isOpen) {
this.close();
}
return;
case SidebarView.THUMBS:
if (this.isOpen && isViewChanged) {
forceRendering = true;
}
break;
case SidebarView.OUTLINE:
if (this.outlineButton.disabled) {
return;
}
break;
case SidebarView.ATTACHMENTS:
if (this.attachmentsButton.disabled) {
return;
}
break;
case SidebarView.LAYERS:
if (this.layersButton.disabled) {
return;
}
break;
default:
ngx_console.NgxConsole.error(`PDFSidebar.switchView: "${view}" is not a valid view.`);
return;
}
this.active = view;
toggleCheckedBtn(this.thumbnailButton, view === SidebarView.THUMBS, this.thumbnailView);
toggleCheckedBtn(this.outlineButton, view === SidebarView.OUTLINE, this.outlineView);
toggleCheckedBtn(this.attachmentsButton, view === SidebarView.ATTACHMENTS, this.attachmentsView);
toggleCheckedBtn(this.layersButton, view === SidebarView.LAYERS, this.layersView);
if (forceOpen && !this.isOpen) {
this.open();
return;
}
if (forceRendering) {
this.onUpdateThumbnails();
this.onToggled();
}
if (isViewChanged) {
this.#dispatchEvent();
}
}
open() {
if (this.isOpen) {
return;
}
this.isOpen = true;
toggleExpandedBtn(this.toggleButton, true);
this.outerContainer.classList.add("sidebarMoving", "sidebarOpen");
if (this.active === SidebarView.THUMBS) {
this.onUpdateThumbnails();
}
this.onToggled();
this.#dispatchEvent();
this.#hideUINotification();
}
close(evt = null) {
if (!this.isOpen) {
return;
}
this.isOpen = false;
toggleExpandedBtn(this.toggleButton, false);
this.outerContainer.classList.add("sidebarMoving");
this.outerContainer.classList.remove("sidebarOpen");
this.onToggled();
this.#dispatchEvent();
if (evt?.detail > 0) {
this.toggleButton.blur();
}
}
toggle(evt = null) {
if (this.isOpen) {
this.close(evt);
} else {
this.open();
}
}
#dispatchEvent() {
if (this.isInitialViewSet) {
this.isInitialEventDispatched ||= true;
}
this.eventBus.dispatch("sidebarviewchanged", {
source: this,
view: this.visibleView
});
}
#showUINotification() {
this.toggleButton.setAttribute("data-l10n-id", "pdfjs-toggle-sidebar-notification-button");
if (!this.isOpen) {
this.toggleButton.classList.add(UI_NOTIFICATION_CLASS);
}
}
#hideUINotification(reset = false) {
if (this.isOpen || reset) {
this.toggleButton.classList.remove(UI_NOTIFICATION_CLASS);
}
if (reset) {
this.toggleButton.setAttribute("data-l10n-id", "pdfjs-toggle-sidebar-button");
}
}
#addEventListeners() {
const {
eventBus,
outerContainer
} = this;
this.sidebarContainer.addEventListener("transitionend", evt => {
if (evt.target === this.sidebarContainer) {
outerContainer.classList.remove("sidebarMoving");
eventBus.dispatch("resize", {
source: this
});
}
});
if (!isInsideNgxExtendedPdfViewer(this.toggleButton)) {
this.toggleButton.addEventListener("click", evt => {
this.toggle(evt);
});
}
this.thumbnailButton.addEventListener("click", () => {
this.switchView(SidebarView.THUMBS);
});
this.outlineButton.addEventListener("click", () => {
this.switchView(SidebarView.OUTLINE);
});
this.outlineButton.addEventListener("dblclick", () => {
eventBus.dispatch("toggleoutlinetree", {
source: this
});
});
this.attachmentsButton.addEventListener("click", () => {
this.switchView(SidebarView.ATTACHMENTS);
});
this.layersButton.addEventListener("click", () => {
this.switchView(SidebarView.LAYERS);
});
this.layersButton.addEventListener("dblclick", () => {
eventBus.dispatch("resetlayers", {
source: this
});
});
this._currentOutlineItemButton.addEventListener("click", () => {
eventBus.dispatch("currentoutlineitem", {
source: this
});
});
const onTreeLoaded = (count, button, view) => {
button.disabled = !count;
button.hidden = !count;
if (count) {
this.#showUINotification();
} else if (this.active === view) {
this.switchView(SidebarView.THUMBS);
}
};
eventBus._on("outlineloaded", evt => {
onTreeLoaded(evt.outlineCount, this.outlineButton, SidebarView.OUTLINE);
if (evt.enableCurrentOutlineItemButton) {
if (evt.currentOutlineItemPromise) {
evt.currentOutlineItemPromise.then(enabled => {
if (!this.isInitialViewSet) {
return;
}
this._currentOutlineItemButton.disabled = !enabled;
});
}
}
});
eventBus._on("attachmentsloaded", evt => {
onTreeLoaded(evt.attachmentsCount, this.attachmentsButton, SidebarView.ATTACHMENTS);
});
eventBus._on("layersloaded", evt => {
onTreeLoaded(evt.layersCount, this.layersButton, SidebarView.LAYERS);
});
eventBus._on("presentationmodechanged", evt => {
if (evt.state === PresentationModeState.NORMAL && this.visibleView === SidebarView.THUMBS) {
this.onUpdateThumbnails();
}
});
this.resizer.addEventListener("mousedown", evt => {
if (evt.button !== 0) {
return;
}
outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
this.#mouseAC = new AbortController();
const opts = {
signal: this.#mouseAC.signal
};
window.addEventListener("mousemove", this.#mouseMove.bind(this), opts);
window.addEventListener("mouseup", this.#mouseUp.bind(this), opts);
window.addEventListener("blur", this.#mouseUp.bind(this), opts);
});
eventBus._on("resize", evt => {
if (evt.source !== window) {
return;
}
this.#outerContainerWidth = null;
if (!this.#width) {
return;
}
if (!this.isOpen) {
this.#updateWidth(this.#width);
return;
}
outerContainer.classList.add(SIDEBAR_RESIZING_CLASS);
const updated = this.#updateWidth(this.#width);
Promise.resolve().then(() => {
outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
if (updated) {
eventBus.dispatch("resize", {
source: this
});
}
});
});
}
get outerContainerWidth() {
return this.#outerContainerWidth ||= this.outerContainer.clientWidth;
}
#updateWidth(width = 0) {
const maxWidth = Math.floor(this.outerContainerWidth / 2);
if (width > maxWidth) {
width = maxWidth;
}
if (width < SIDEBAR_MIN_WIDTH) {
width = SIDEBAR_MIN_WIDTH;
}
if (width === this.#width) {
return false;
}
this.#width = width;
docStyle.setProperty(SIDEBAR_WIDTH_VAR, `${width}px`);
return true;
}
#mouseMove(evt) {
let width = evt.clientX;
if (this.#isRTL) {
width = this.outerContainerWidth - width;
}
this.#updateWidth(width);
}
#mouseUp(evt) {
this.outerContainer.classList.remove(SIDEBAR_RESIZING_CLASS);
this.eventBus.dispatch("resize", {
source: this
});
this.#mouseAC?.abort();
this.#mouseAC = null;
}
}
;// ./web/pdf_thumbnail_view.js
const DRAW_UPSCALE_FACTOR = 2;
const MAX_NUM_SCALING_STEPS = 3;
const THUMBNAIL_WIDTH = 98;
class TempImageFactory {
static #tempCanvas = null;
static getCanvas(width, height) {
const tempCanvas = this.#tempCanvas ||= document.createElement("canvas");
tempCanvas.width = width;
tempCanvas.height = height;
const ctx = tempCanvas.getContext("2d", {
alpha: false
});
ctx.save();
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillRect(0, 0, width, height);
ctx.restore();
return [tempCanvas, tempCanvas.getContext("2d")];
}
static destroyCanvas() {
const tempCanvas = this.#tempCanvas;
if (tempCanvas) {
tempCanvas.width = 0;
tempCanvas.height = 0;
}
this.#tempCanvas = null;
}
}
class PDFThumbnailView {
constructor({
container,
eventBus,
id,
defaultViewport,
optionalContentConfigPromise,
linkService,
renderingQueue,
pageColors,
enableHWA
}) {
this.id = id;
this.renderingId = "thumbnail" + id;
this.pageLabel = null;
this.pdfPage = null;
this.rotation = 0;
this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation;
this._optionalContentConfigPromise = optionalContentConfigPromise || null;
this.pageColors = pageColors || null;
this.enableHWA = enableHWA || false;
this.eventBus = eventBus;
this.linkService = linkService;
this.renderingQueue = renderingQueue;
this.eventBus = eventBus;
this.renderTask = null;
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
eventBus.dispatch("rendercustomthumbnail", {
pdfThumbnailView: this,
linkService,
id,
container,
thumbPageTitlePromiseOrPageL10nArgs: this.#pageL10nArgs
});
if (container.querySelector(`.${this.renderingId}`)) {
this._placeholderImg = container.querySelector(`.${this.renderingId} .thumbnailImage`);
} else {
this.createThumbnail(this, linkService, id, container, this.#pageL10nArgs);
}
}
createThumbnail(pdfThumbnailView, linkService, id, container, pageL10nArgs) {
const anchor = document.createElement("a");
anchor.href = linkService.getAnchorUrl("#page=" + id);
anchor.setAttribute("data-l10n-id", "pdfjs-thumb-page-title");
anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
anchor.className = this.renderingId;
anchor.onclick = function () {
linkService.goToPage(id);
return false;
};
this.anchor = anchor;
const div = document.createElement("div");
div.className = "thumbnail";
div.setAttribute("data-page-number", this.id);
this.div = div;
this.#updateDims();
const img = document.createElement("div");
img.className = "thumbnailImage";
this._placeholderImg = img;
div.append(img);
anchor.append(div);
container.append(anchor);
}
#updateDims() {
const {
width,
height
} = this.viewport;
const ratio = width / height;
this.canvasWidth = THUMBNAIL_WIDTH;
this.canvasHeight = this.canvasWidth / ratio | 0;
this.scale = this.canvasWidth / width;
const {
style
} = this.div;
style.setProperty("--thumbnail-width", `${this.canvasWidth}px`);
style.setProperty("--thumbnail-height", `${this.canvasHeight}px`);
}
setPdfPage(pdfPage) {
this.pdfPage = pdfPage;
this.pdfPageRotate = pdfPage.rotate;
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = pdfPage.getViewport({
scale: 1,
rotation: totalRotation
});
this.reset();
}
reset() {
this.cancelRendering();
this.renderingState = RenderingStates.INITIAL;
this.div.removeAttribute("data-loaded");
this.image?.replaceWith(this._placeholderImg);
this.#updateDims();
if (this.image) {
this.image.removeAttribute("src");
delete this.image;
}
}
update({
rotation = null
}) {
if (typeof rotation === "number") {
this.rotation = rotation;
}
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = this.viewport.clone({
scale: 1,
rotation: totalRotation
});
this.reset();
}
cancelRendering() {
if (this.renderTask) {
this.renderTask.cancel();
this.renderTask = null;
}
this.resume = null;
}
#getPageDrawContext(upscaleFactor = 1, enableHWA = this.enableHWA) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d", {
alpha: false,
willReadFrequently: !enableHWA
});
const outputScale = new pdf.OutputScale();
canvas.width = upscaleFactor * this.canvasWidth * outputScale.sx | 0;
canvas.height = upscaleFactor * this.canvasHeight * outputScale.sy | 0;
const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null;
return {
ctx,
canvas,
transform
};
}
#convertCanvasToImage(canvas) {
if (this.renderingState !== RenderingStates.FINISHED) {
throw new Error("#convertCanvasToImage: Rendering has not finished.");
}
const reducedCanvas = this.#reduceImage(canvas);
const image = document.createElement("img");
image.className = "thumbnailImage";
image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas");
image.setAttribute("data-l10n-args", this.#pageL10nArgs);
image.src = reducedCanvas.toDataURL();
this.image = image;
this.div.setAttribute("data-loaded", true);
this._placeholderImg.replaceWith(image);
reducedCanvas.width = 0;
reducedCanvas.height = 0;
}
async #finishRenderTask(renderTask, canvas, error = null) {
if (renderTask === this.renderTask) {
this.renderTask = null;
}
if (error instanceof pdf.RenderingCancelledException) {
return;
}
this.renderingState = RenderingStates.FINISHED;
this.#convertCanvasToImage(canvas);
if (error) {
throw error;
}
this.eventBus.dispatch("thumbnailRendered", this.id);
}
async draw() {
if (this.renderingState !== RenderingStates.INITIAL) {
ngx_console.NgxConsole.error("Must be in new state before drawing");
return undefined;
}
const {
pdfPage
} = this;
if (!pdfPage) {
this.renderingState = RenderingStates.FINISHED;
throw new Error("pdfPage is not loaded");
}
this.renderingState = RenderingStates.RUNNING;
const {
ctx,
canvas,
transform
} = this.#getPageDrawContext(DRAW_UPSCALE_FACTOR);
const drawViewport = this.viewport.clone({
scale: DRAW_UPSCALE_FACTOR * this.scale
});
const renderContinueCallback = cont => {
if (!this.renderingQueue.isHighestPriority(this)) {
this.renderingState = RenderingStates.PAUSED;
this.resume = () => {
this.renderingState = RenderingStates.RUNNING;
cont();
};
return;
}
cont();
};
const renderContext = {
canvasContext: ctx,
transform,
viewport: drawViewport,
optionalContentConfigPromise: this._optionalContentConfigPromise,
pageColors: this.pageColors
};
const renderTask = this.renderTask = pdfPage.render(renderContext);
renderTask.onContinue = renderContinueCallback;
const resultPromise = renderTask.promise.then(() => this.#finishRenderTask(renderTask, canvas), error => this.#finishRenderTask(renderTask, canvas, error));
resultPromise.finally(() => {
canvas.width = 0;
canvas.height = 0;
this.eventBus.dispatch("thumbnailrendered", {
source: this,
pageNumber: this.id,
pdfPage: this.pdfPage
});
});
return resultPromise;
}
setImage(pageView) {
if (this.renderingState !== RenderingStates.INITIAL) {
return;
}
const {
thumbnailCanvas: canvas,
pdfPage,
scale
} = pageView;
if (!canvas) {
return;
}
if (!this.pdfPage) {
this.setPdfPage(pdfPage);
}
if (scale < this.scale) {
return;
}
this.renderingState = RenderingStates.FINISHED;
this.#convertCanvasToImage(canvas);
}
#reduceImage(img) {
const {
ctx,
canvas
} = this.#getPageDrawContext(1, true);
if (img.width <= 2 * canvas.width) {
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
return canvas;
}
let reducedWidth = canvas.width << MAX_NUM_SCALING_STEPS;
let reducedHeight = canvas.height << MAX_NUM_SCALING_STEPS;
const [reducedImage, reducedImageCtx] = TempImageFactory.getCanvas(reducedWidth, reducedHeight);
while (reducedWidth > img.width || reducedHeight > img.height) {
reducedWidth >>= 1;
reducedHeight >>= 1;
}
reducedImageCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, reducedWidth, reducedHeight);
while (reducedWidth > 2 * canvas.width) {
reducedImageCtx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, reducedWidth >> 1, reducedHeight >> 1);
reducedWidth >>= 1;
reducedHeight >>= 1;
}
ctx.drawImage(reducedImage, 0, 0, reducedWidth, reducedHeight, 0, 0, canvas.width, canvas.height);
return canvas;
}
get #pageL10nArgs() {
return JSON.stringify({
page: this.pageLabel ?? this.id
});
}
setPageLabel(label) {
this.pageLabel = typeof label === "string" ? label : null;
this.anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
if (this.renderingState !== RenderingStates.FINISHED) {
return;
}
this.image?.setAttribute("data-l10n-args", this.#pageL10nArgs);
}
}
;// ./web/pdf_thumbnail_viewer.js
const THUMBNAIL_SCROLL_MARGIN = -19;
const THUMBNAIL_SELECTED_CLASS = "selected";
class PDFThumbnailViewer {
constructor({
container,
eventBus,
linkService,
renderingQueue,
pageColors,
abortSignal,
enableHWA
}) {
this.container = container;
this.eventBus = eventBus;
this.linkService = linkService;
this.renderingQueue = renderingQueue;
this.pageColors = pageColors || null;
this.enableHWA = enableHWA || false;
this.scroll = watchScroll(this.container, this.#scrollUpdated.bind(this), abortSignal);
this.#resetView();
}
#scrollUpdated() {
this.renderingQueue.renderHighestPriority();
}
getThumbnail(index) {
return this._thumbnails[index];
}
#getVisibleThumbs() {
return getVisibleElements({
scrollEl: this.container,
views: this._thumbnails
});
}
scrollThumbnailIntoView(pageNumber) {
if (!this.pdfDocument) {
return;
}
const thumbnailView = this._thumbnails[pageNumber - 1];
if (!thumbnailView) {
ngx_console.NgxConsole.error('scrollThumbnailIntoView: Invalid "pageNumber" parameter.');
return;
}
if (pageNumber !== this._currentPageNumber) {
const prevThumbnailView = this._thumbnails[this._currentPageNumber - 1];
prevThumbnailView.div.classList.remove(THUMBNAIL_SELECTED_CLASS);
thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
}
const {
first,
last,
views
} = this.#getVisibleThumbs();
if (views.length > 0) {
let shouldScroll = false;
if (pageNumber <= first.id || pageNumber >= last.id) {
shouldScroll = true;
} else {
for (const {
id,
percent
} of views) {
if (id !== pageNumber) {
continue;
}
shouldScroll = percent < 100;
break;
}
}
if (shouldScroll) {
scrollIntoView(thumbnailView.div, {
top: THUMBNAIL_SCROLL_MARGIN
});
}
}
this._currentPageNumber = pageNumber;
}
get pagesRotation() {
return this._pagesRotation;
}
set pagesRotation(rotation) {
if (!isValidRotation(rotation)) {
throw new Error("Invalid thumbnails rotation angle.");
}
if (!this.pdfDocument) {
return;
}
if (this._pagesRotation === rotation) {
return;
}
this._pagesRotation = rotation;
const updateArgs = {
rotation
};
for (const thumbnail of this._thumbnails) {
thumbnail.update(updateArgs);
}
}
cleanup() {
for (const thumbnail of this._thumbnails) {
if (thumbnail.renderingState !== RenderingStates.FINISHED) {
thumbnail.reset();
}
}
TempImageFactory.destroyCanvas();
}
#resetView() {
this._thumbnails = [];
this._currentPageNumber = 1;
this._pageLabels = null;
this._pagesRotation = 0;
this.container.textContent = "";
}
setDocument(pdfDocument) {
this.initialized = false;
if (this.pdfDocument) {
this.#cancelRendering();
this.#resetView();
}
this.pdfDocument = pdfDocument;
if (!pdfDocument) {
return;
}
if (this.initialized) {
return;
}
this.initialized = true;
const firstPagePromise = pdfDocument.getPage(1);
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
intent: "display"
});
firstPagePromise.then(firstPdfPage => {
const pagesCount = pdfDocument.numPages;
const viewport = firstPdfPage.getViewport({
scale: 1
});
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
const thumbnail = new PDFThumbnailView({
container: this.container,
eventBus: this.eventBus,
id: pageNum,
defaultViewport: viewport.clone(),
optionalContentConfigPromise,
linkService: this.linkService,
renderingQueue: this.renderingQueue,
pageColors: this.pageColors,
eventBus: this.eventBus,
enableHWA: this.enableHWA
});
this._thumbnails.push(thumbnail);
}
this._thumbnails[0]?.setPdfPage(firstPdfPage);
const thumbnailView = this._thumbnails[this._currentPageNumber - 1];
thumbnailView.div.classList.add(THUMBNAIL_SELECTED_CLASS);
}).catch(reason => {
ngx_console.NgxConsole.error("Unable to initialize thumbnail viewer", reason);
});
}
#cancelRendering() {
for (const thumbnail of this._thumbnails) {
thumbnail.cancelRendering();
}
}
setPageLabels(labels) {
if (!this.pdfDocument) {
return;
}
if (!labels) {
this._pageLabels = null;
} else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
this._pageLabels = null;
ngx_console.NgxConsole.error("PDFThumbnailViewer_setPageLabels: Invalid page labels.");
} else {
this._pageLabels = labels;
}
for (let i = 0, ii = this._thumbnails.length; i < ii; i++) {
this._thumbnails[i].setPageLabel(this._pageLabels?.[i] ?? null);
}
}
async #ensurePdfPageLoaded(thumbView) {
if (thumbView.pdfPage) {
return thumbView.pdfPage;
}
try {
const pdfPage = await this.pdfDocument.getPage(thumbView.id);
if (!thumbView.pdfPage) {
thumbView.setPdfPage(pdfPage);
}
return pdfPage;
} catch (reason) {
ngx_console.NgxConsole.error("Unable to get page for thumb view", reason);
return null;
}
}
#getScrollAhead(visible) {
if (visible.first?.id === 1) {
return true;
} else if (visible.last?.id === this._thumbnails.length) {
return false;
}
return this.scroll.down;
}
forceRendering() {
const visibleThumbs = this.#getVisibleThumbs();
const scrollAhead = this.#getScrollAhead(visibleThumbs);
const thumbView = this.renderingQueue.getHighestPriority(visibleThumbs, this._thumbnails, scrollAhead);
if (thumbView) {
this.#ensurePdfPageLoaded(thumbView).then(() => {
this.renderingQueue.renderView(thumbView);
});
return true;
}
return false;
}
stopRendering() {
this.#cancelRendering();
}
}
;// ./web/page-flip.module.js
class Page {
constructor(render, density) {
this.state = {
angle: 0,
area: [],
position: {
x: 0,
y: 0
},
hardAngle: 0,
hardDrawingAngle: 0
};
this.createdDensity = density;
this.nowDrawingDensity = this.createdDensity;
this.render = render;
}
setDensity(density) {
this.createdDensity = density;
this.nowDrawingDensity = density;
}
setDrawingDensity(density) {
this.nowDrawingDensity = density;
}
setPosition(pagePos) {
this.state.position = pagePos;
}
setAngle(angle) {
this.state.angle = angle;
}
setArea(area) {
this.state.area = area;
}
setHardDrawingAngle(angle) {
this.state.hardDrawingAngle = angle;
}
setHardAngle(angle) {
this.state.hardAngle = angle;
this.state.hardDrawingAngle = angle;
}
setOrientation(orientation) {
this.orientation = orientation;
}
getDrawingDensity() {
return this.nowDrawingDensity;
}
getDensity() {
return this.createdDensity;
}
getHardAngle() {
return this.state.hardAngle;
}
}
class PageCollection {
constructor(app, render) {
this.pages = [];
this.currentPageIndex = 0;
this.currentSpreadIndex = 0;
this.landscapeSpread = [];
this.portraitSpread = [];
this.render = render;
this.app = app;
this.currentPageIndex = 0;
this.isShowCover = this.app.getSettings().showCover;
}
destroy() {
this.pages = [];
}
createSpread() {
this.landscapeSpread = [];
this.portraitSpread = [];
for (let i = 0; i < this.pages.length; i++) {
this.portraitSpread.push([i]);
}
let start = 0;
if (this.isShowCover) {
this.pages[0].setDensity("hard");
this.landscapeSpread.push([start]);
start++;
}
for (let i = start; i < this.pages.length; i += 2) {
if (i < this.pages.length - 1) this.landscapeSpread.push([i, i + 1]);else {
this.landscapeSpread.push([i]);
this.pages[i].setDensity("hard");
}
}
}
getSpread() {
return this.render.getOrientation() === "landscape" ? this.landscapeSpread : this.portraitSpread;
}
getSpreadIndexByPage(pageNum) {
const spread = this.getSpread();
for (let i = 0; i < spread.length; i++) if (pageNum === spread[i][0] || pageNum === spread[i][1]) return i;
return null;
}
getPageCount() {
return this.pages.length;
}
getPages() {
return this.pages;
}
getPage(pageIndex) {
if (pageIndex >= 0 && pageIndex < this.pages.length) {
return this.pages[pageIndex];
}
throw new Error('Invalid page number');
}
nextBy(current) {
const idx = this.pages.indexOf(current);
if (idx < this.pages.length - 1) return this.pages[idx + 1];
return null;
}
prevBy(current) {
const idx = this.pages.indexOf(current);
if (idx > 0) return this.pages[idx - 1];
return null;
}
getFlippingPage(direction) {
const current = this.currentSpreadIndex;
if (this.render.getOrientation() === "portrait") {
return direction === 0 ? this.pages[current].newTemporaryCopy() : this.pages[current - 1];
} else {
const spread = direction === 0 ? this.getSpread()[current + 1] : this.getSpread()[current - 1];
if (spread.length === 1) return this.pages[spread[0]];
return direction === 0 ? this.pages[spread[0]] : this.pages[spread[1]];
}
}
getBottomPage(direction) {
const current = this.currentSpreadIndex;
if (this.render.getOrientation() === "portrait") {
return direction === 0 ? this.pages[current + 1] : this.pages[current - 1];
} else {
const spread = direction === 0 ? this.getSpread()[current + 1] : this.getSpread()[current - 1];
if (spread.length === 1) return this.pages[spread[0]];
return direction === 0 ? this.pages[spread[1]] : this.pages[spread[0]];
}
}
showNext() {
if (this.currentSpreadIndex < this.getSpread().length) {
this.currentSpreadIndex++;
this.showSpread();
}
}
showPrev() {
if (this.currentSpreadIndex > 0) {
this.currentSpreadIndex--;
this.showSpread();
}
}
getCurrentPageIndex() {
return this.currentPageIndex;
}
show(pageNum = null) {
if (pageNum === null) pageNum = this.currentPageIndex;
if (pageNum < 0 || pageNum >= this.pages.length) return;
const spreadIndex = this.getSpreadIndexByPage(pageNum);
if (spreadIndex !== null) {
this.currentSpreadIndex = spreadIndex;
this.showSpread();
}
}
getCurrentSpreadIndex() {
return this.currentSpreadIndex;
}
setCurrentSpreadIndex(newIndex) {
if (newIndex >= 0 && newIndex < this.getSpread().length) {
this.currentSpreadIndex = newIndex;
} else {
throw new Error('Invalid page');
}
}
showSpread() {
const spread = this.getSpread()[this.currentSpreadIndex];
if (spread.length === 2) {
this.render.setLeftPage(this.pages[spread[0]]);
this.render.setRightPage(this.pages[spread[1]]);
} else {
if (this.render.getOrientation() === "landscape") {
if (spread[0] === this.pages.length - 1) {
this.render.setLeftPage(this.pages[spread[0]]);
this.render.setRightPage(null);
} else {
this.render.setLeftPage(null);
this.render.setRightPage(this.pages[spread[0]]);
}
} else {
this.render.setLeftPage(null);
this.render.setRightPage(this.pages[spread[0]]);
}
}
this.currentPageIndex = spread[0];
this.app.updatePageIndex(this.currentPageIndex);
}
}
class Helper {
static GetDistanceBetweenTwoPoint(point1, point2) {
if (point1 === null || point2 === null) {
return Infinity;
}
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}
static GetSegmentLength(segment) {
return Helper.GetDistanceBetweenTwoPoint(segment[0], segment[1]);
}
static GetAngleBetweenTwoLine(line1, line2) {
const A1 = line1[0].y - line1[1].y;
const A2 = line2[0].y - line2[1].y;
const B1 = line1[1].x - line1[0].x;
const B2 = line2[1].x - line2[0].x;
return Math.acos((A1 * A2 + B1 * B2) / (Math.sqrt(A1 * A1 + B1 * B1) * Math.sqrt(A2 * A2 + B2 * B2)));
}
static PointInRect(rect, pos) {
if (pos === null) {
return null;
}
if (pos.x >= rect.left && pos.x <= rect.width + rect.left && pos.y >= rect.top && pos.y <= rect.top + rect.height) {
return pos;
}
return null;
}
static GetRotatedPoint(transformedPoint, startPoint, angle) {
return {
x: transformedPoint.x * Math.cos(angle) + transformedPoint.y * Math.sin(angle) + startPoint.x,
y: transformedPoint.y * Math.cos(angle) - transformedPoint.x * Math.sin(angle) + startPoint.y
};
}
static LimitPointToCircle(startPoint, radius, limitedPoint) {
if (Helper.GetDistanceBetweenTwoPoint(startPoint, limitedPoint) <= radius) {
return limitedPoint;
}
const a = startPoint.x;
const b = startPoint.y;
const n = limitedPoint.x;
const m = limitedPoint.y;
let x = Math.sqrt(Math.pow(radius, 2) * Math.pow(a - n, 2) / (Math.pow(a - n, 2) + Math.pow(b - m, 2))) + a;
if (limitedPoint.x < 0) {
x *= -1;
}
let y = (x - a) * (b - m) / (a - n) + b;
if (a - n + b === 0) {
y = radius;
}
return {
x,
y
};
}
static GetIntersectBetweenTwoSegment(rectBorder, one, two) {
return Helper.PointInRect(rectBorder, Helper.GetIntersectBeetwenTwoLine(one, two));
}
static GetIntersectBeetwenTwoLine(one, two) {
const A1 = one[0].y - one[1].y;
const A2 = two[0].y - two[1].y;
const B1 = one[1].x - one[0].x;
const B2 = two[1].x - two[0].x;
const C1 = one[0].x * one[1].y - one[1].x * one[0].y;
const C2 = two[0].x * two[1].y - two[1].x * two[0].y;
const det1 = A1 * C2 - A2 * C1;
const det2 = B1 * C2 - B2 * C1;
const x = -((C1 * B2 - C2 * B1) / (A1 * B2 - A2 * B1));
const y = -((A1 * C2 - A2 * C1) / (A1 * B2 - A2 * B1));
if (isFinite(x) && isFinite(y)) {
return {
x,
y
};
} else {
if (Math.abs(det1 - det2) < 0.1) throw new Error('Segment included');
}
return null;
}
static GetCordsFromTwoPoint(pointOne, pointTwo) {
const sizeX = Math.abs(pointOne.x - pointTwo.x);
const sizeY = Math.abs(pointOne.y - pointTwo.y);
const lengthLine = Math.max(sizeX, sizeY);
const result = [pointOne];
function getCord(c1, c2, size, length, index) {
if (c2 > c1) {
return c1 + index * (size / length);
} else if (c2 < c1) {
return c1 - index * (size / length);
}
return c1;
}
for (let i = 1; i <= lengthLine; i += 1) {
result.push({
x: getCord(pointOne.x, pointTwo.x, sizeX, lengthLine, i),
y: getCord(pointOne.y, pointTwo.y, sizeY, lengthLine, i)
});
}
return result;
}
}
class HTMLPage extends Page {
constructor(render, element, density) {
super(render, density);
this.copiedElement = null;
this.temporaryCopy = null;
this.isLoad = false;
this.element = element;
this.element.classList.add('stf__item');
this.element.classList.add('--' + density);
}
newTemporaryCopy() {
if (this.nowDrawingDensity === "hard") {
return this;
}
if (this.temporaryCopy === null) {
this.copiedElement = this.element.cloneNode(true);
this.element.parentElement.appendChild(this.copiedElement);
this.temporaryCopy = new HTMLPage(this.render, this.copiedElement, this.nowDrawingDensity);
}
return this.getTemporaryCopy();
}
getTemporaryCopy() {
return this.temporaryCopy;
}
hideTemporaryCopy() {
if (this.temporaryCopy !== null) {
this.copiedElement.remove();
this.copiedElement = null;
this.temporaryCopy = null;
}
}
draw(tempDensity) {
const density = tempDensity ? tempDensity : this.nowDrawingDensity;
const pagePos = this.render.convertToGlobal(this.state.position);
const pageWidth = this.render.getRect().pageWidth;
const pageHeight = this.render.getRect().height;
this.element.classList.remove('--simple');
const commonStyle = `
position: absolute;
display: block;
z-index: ${this.element.style.zIndex};
left: 0;
top: 0;
width: ${pageWidth}px;
height: ${pageHeight}px;
`;
density === "hard" ? this.drawHard(commonStyle) : this.drawSoft(pagePos, commonStyle);
}
drawHard(commonStyle = '') {
const pos = this.render.getRect().left + this.render.getRect().width / 2;
const angle = this.state.hardDrawingAngle;
const newStyle = commonStyle + `
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
clip-path: none;
-webkit-clip-path: none;
` + (this.orientation === 0 ? `transform-origin: ${this.render.getRect().pageWidth}px 0;
transform: translate3d(0, 0, 0) rotateY(${angle}deg);` : `transform-origin: 0 0;
transform: translate3d(${pos}px, 0, 0) rotateY(${angle}deg);`);
this.element.style.cssText = newStyle;
}
drawSoft(position, commonStyle = '') {
let polygon = 'polygon( ';
for (const p of this.state.area) {
if (p !== null) {
let g = this.render.getDirection() === 1 ? {
x: -p.x + this.state.position.x,
y: p.y - this.state.position.y
} : {
x: p.x - this.state.position.x,
y: p.y - this.state.position.y
};
g = Helper.GetRotatedPoint(g, {
x: 0,
y: 0
}, this.state.angle);
polygon += g.x + 'px ' + g.y + 'px, ';
}
}
polygon = polygon.slice(0, -2);
polygon += ')';
const newStyle = commonStyle + `transform-origin: 0 0; clip-path: ${polygon}; -webkit-clip-path: ${polygon};` + (this.render.isSafari() && this.state.angle === 0 ? `transform: translate(${position.x}px, ${position.y}px);` : `transform: translate3d(${position.x}px, ${position.y}px, 0) rotate(${this.state.angle}rad);`);
this.element.style.cssText = newStyle;
}
simpleDraw(orient) {
const rect = this.render.getRect();
const pageWidth = rect.pageWidth;
const pageHeight = rect.height;
const x = orient === 1 ? rect.left + rect.pageWidth : rect.left;
const y = rect.top;
this.element.classList.add('--simple');
this.element.style.cssText = `
position: absolute;
display: block;
height: ${pageHeight}px;
left: ${x}px;
top: ${y}px;
width: ${pageWidth}px;
z-index: ${this.render.getSettings().startZIndex + 1};`;
}
getElement() {
return this.element;
}
load() {
this.isLoad = true;
}
setOrientation(orientation) {
super.setOrientation(orientation);
this.element.classList.remove('--left', '--right');
this.element.classList.add(orientation === 1 ? '--right' : '--left');
}
setDrawingDensity(density) {
this.element.classList.remove('--soft', '--hard');
this.element.classList.add('--' + density);
super.setDrawingDensity(density);
}
}
class HTMLPageCollection extends PageCollection {
constructor(app, render, element, items) {
super(app, render);
this.element = element;
this.pagesElement = items;
}
load() {
for (const pageElement of this.pagesElement) {
const page = new HTMLPage(this.render, pageElement, pageElement.dataset['density'] === 'hard' ? "hard" : "soft");
page.load();
this.pages.push(page);
}
this.createSpread();
}
}
class FlipCalculation {
constructor(direction, corner, pageWidth, pageHeight) {
this.direction = direction;
this.corner = corner;
this.topIntersectPoint = null;
this.sideIntersectPoint = null;
this.bottomIntersectPoint = null;
this.pageWidth = parseInt(pageWidth, 10);
this.pageHeight = parseInt(pageHeight, 10);
}
calc(localPos) {
try {
this.position = this.calcAngleAndPosition(localPos);
this.calculateIntersectPoint(this.position);
return true;
} catch (e) {
return false;
}
}
getFlippingClipArea() {
const result = [];
let clipBottom = false;
result.push(this.rect.topLeft);
result.push(this.topIntersectPoint);
if (this.sideIntersectPoint === null) {
clipBottom = true;
} else {
result.push(this.sideIntersectPoint);
if (this.bottomIntersectPoint === null) clipBottom = false;
}
result.push(this.bottomIntersectPoint);
if (clipBottom || this.corner === "bottom") {
result.push(this.rect.bottomLeft);
}
return result;
}
getBottomClipArea() {
const result = [];
result.push(this.topIntersectPoint);
if (this.corner === "top") {
result.push({
x: this.pageWidth,
y: 0
});
} else {
if (this.topIntersectPoint !== null) {
result.push({
x: this.pageWidth,
y: 0
});
}
result.push({
x: this.pageWidth,
y: this.pageHeight
});
}
if (this.sideIntersectPoint !== null) {
if (Helper.GetDistanceBetweenTwoPoint(this.sideIntersectPoint, this.topIntersectPoint) >= 10) result.push(this.sideIntersectPoint);
} else {
if (this.corner === "top") {
result.push({
x: this.pageWidth,
y: this.pageHeight
});
}
}
result.push(this.bottomIntersectPoint);
result.push(this.topIntersectPoint);
return result;
}
getAngle() {
if (this.direction === 0) {
return -this.angle;
}
return this.angle;
}
getRect() {
return this.rect;
}
getPosition() {
return this.position;
}
getActiveCorner() {
if (this.direction === 0) {
return this.rect.topLeft;
}
return this.rect.topRight;
}
getDirection() {
return this.direction;
}
getFlippingProgress() {
return Math.abs((this.position.x - this.pageWidth) / (2 * this.pageWidth) * 100);
}
getCorner() {
return this.corner;
}
getBottomPagePosition() {
if (this.direction === 1) {
return {
x: this.pageWidth,
y: 0
};
}
return {
x: 0,
y: 0
};
}
getShadowStartPoint() {
if (this.corner === "top") {
return this.topIntersectPoint;
} else {
if (this.sideIntersectPoint !== null) return this.sideIntersectPoint;
return this.topIntersectPoint;
}
}
getShadowAngle() {
const angle = Helper.GetAngleBetweenTwoLine(this.getSegmentToShadowLine(), [{
x: 0,
y: 0
}, {
x: this.pageWidth,
y: 0
}]);
if (this.direction === 0) {
return angle;
}
return Math.PI - angle;
}
calcAngleAndPosition(pos) {
let result = pos;
this.updateAngleAndGeometry(result);
if (this.corner === "top") {
result = this.checkPositionAtCenterLine(result, {
x: 0,
y: 0
}, {
x: 0,
y: this.pageHeight
});
} else {
result = this.checkPositionAtCenterLine(result, {
x: 0,
y: this.pageHeight
}, {
x: 0,
y: 0
});
}
if (Math.abs(result.x - this.pageWidth) < 1 && Math.abs(result.y) < 1) {
throw new Error('Point is too small');
}
return result;
}
updateAngleAndGeometry(pos) {
this.angle = this.calculateAngle(pos);
this.rect = this.getPageRect(pos);
}
calculateAngle(pos) {
const left = this.pageWidth - pos.x + 1;
const top = this.corner === "bottom" ? this.pageHeight - pos.y : pos.y;
let angle = 2 * Math.acos(left / Math.sqrt(top * top + left * left));
if (top < 0) angle = -angle;
const da = Math.PI - angle;
if (!isFinite(angle) || da >= 0 && da < 0.003) throw new Error('The G point is too small');
if (this.corner === "bottom") angle = -angle;
return angle;
}
getPageRect(localPos) {
if (this.corner === "top") {
return this.getRectFromBasePoint([{
x: 0,
y: 0
}, {
x: this.pageWidth,
y: 0
}, {
x: 0,
y: this.pageHeight
}, {
x: this.pageWidth,
y: this.pageHeight
}], localPos);
}
return this.getRectFromBasePoint([{
x: 0,
y: -this.pageHeight
}, {
x: this.pageWidth,
y: -this.pageHeight
}, {
x: 0,
y: 0
}, {
x: this.pageWidth,
y: 0
}], localPos);
}
getRectFromBasePoint(points, localPos) {
return {
topLeft: this.getRotatedPoint(points[0], localPos),
topRight: this.getRotatedPoint(points[1], localPos),
bottomLeft: this.getRotatedPoint(points[2], localPos),
bottomRight: this.getRotatedPoint(points[3], localPos)
};
}
getRotatedPoint(transformedPoint, startPoint) {
return {
x: transformedPoint.x * Math.cos(this.angle) + transformedPoint.y * Math.sin(this.angle) + startPoint.x,
y: transformedPoint.y * Math.cos(this.angle) - transformedPoint.x * Math.sin(this.angle) + startPoint.y
};
}
calculateIntersectPoint(pos) {
const boundRect = {
left: -1,
top: -1,
width: this.pageWidth + 2,
height: this.pageHeight + 2
};
if (this.corner === "top") {
this.topIntersectPoint = Helper.GetIntersectBetweenTwoSegment(boundRect, [pos, this.rect.topRight], [{
x: 0,
y: 0
}, {
x: this.pageWidth,
y: 0
}]);
this.sideIntersectPoint = Helper.GetIntersectBetweenTwoSegment(boundRect, [pos, this.rect.bottomLeft], [{
x: this.pageWidth,
y: 0
}, {
x: this.pageWidth,
y: this.pageHeight
}]);
this.bottomIntersectPoint = Helper.GetIntersectBetweenTwoSegment(boundRect, [this.rect.bottomLeft, this.rect.bottomRight], [{
x: 0,
y: this.pageHeight
}, {
x: this.pageWidth,
y: this.pageHeight
}]);
} else {
this.topIntersectPoint = Helper.GetIntersectBetweenTwoSegment(boundRect, [this.rect.topLeft, this.rect.topRight], [{
x: 0,
y: 0
}, {
x: this.pageWidth,
y: 0
}]);
this.sideIntersectPoint = Helper.GetIntersectBetweenTwoSegment(boundRect, [pos, this.rect.topLeft], [{
x: this.pageWidth,
y: 0
}, {
x: this.pageWidth,
y: this.pageHeight
}]);
this.bottomIntersectPoint = Helper.GetIntersectBetweenTwoSegment(boundRect, [this.rect.bottomLeft, this.rect.bottomRight], [{
x: 0,
y: this.pageHeight
}, {
x: this.pageWidth,
y: this.pageHeight
}]);
}
}
checkPositionAtCenterLine(checkedPos, centerOne, centerTwo) {
let result = checkedPos;
const tmp = Helper.LimitPointToCircle(centerOne, this.pageWidth, result);
if (result !== tmp) {
result = tmp;
this.updateAngleAndGeometry(result);
}
const rad = Math.sqrt(Math.pow(this.pageWidth, 2) + Math.pow(this.pageHeight, 2));
let checkPointOne = this.rect.bottomRight;
let checkPointTwo = this.rect.topLeft;
if (this.corner === "bottom") {
checkPointOne = this.rect.topRight;
checkPointTwo = this.rect.bottomLeft;
}
if (checkPointOne.x <= 0) {
const bottomPoint = Helper.LimitPointToCircle(centerTwo, rad, checkPointTwo);
if (bottomPoint !== result) {
result = bottomPoint;
this.updateAngleAndGeometry(result);
}
}
return result;
}
getSegmentToShadowLine() {
const first = this.getShadowStartPoint();
const second = first !== this.sideIntersectPoint && this.sideIntersectPoint !== null ? this.sideIntersectPoint : this.bottomIntersectPoint;
return [first, second];
}
}
class Flip {
constructor(render, app) {
this.flippingPage = null;
this.bottomPage = null;
this.calc = null;
this.state = "read";
this.render = render;
this.app = app;
}
fold(globalPos) {
this.setState("user_fold");
if (this.calc === null) this.start(globalPos);
this.do(this.render.convertToPage(globalPos));
}
flip(globalPos) {
if (this.app.getSettings().disableFlipByClick && !this.isPointOnCorners(globalPos)) return;
if (this.calc !== null) this.render.finishAnimation();
if (!this.start(globalPos)) return;
const rect = this.getBoundsRect();
this.setState("flipping");
const topMargins = rect.height / 10;
const yStart = this.calc.getCorner() === "bottom" ? rect.height - topMargins : topMargins;
const yDest = this.calc.getCorner() === "bottom" ? rect.height : 0;
this.calc.calc({
x: rect.pageWidth - topMargins,
y: yStart
});
this.animateFlippingTo({
x: rect.pageWidth - topMargins,
y: yStart
}, {
x: -rect.pageWidth,
y: yDest
}, true);
}
start(globalPos) {
this.reset();
const bookPos = this.render.convertToBook(globalPos);
const rect = this.getBoundsRect();
const direction = this.getDirectionByPoint(bookPos);
const flipCorner = bookPos.y >= rect.height / 2 ? "bottom" : "top";
if (!this.checkDirection(direction)) return false;
try {
this.flippingPage = this.app.getPageCollection().getFlippingPage(direction);
this.bottomPage = this.app.getPageCollection().getBottomPage(direction);
if (this.render.getOrientation() === "landscape") {
if (direction === 1) {
const nextPage = this.app.getPageCollection().nextBy(this.flippingPage);
if (nextPage !== null) {
if (this.flippingPage.getDensity() !== nextPage.getDensity()) {
this.flippingPage.setDrawingDensity("hard");
nextPage.setDrawingDensity("hard");
}
}
} else {
const prevPage = this.app.getPageCollection().prevBy(this.flippingPage);
if (prevPage !== null) {
if (this.flippingPage.getDensity() !== prevPage.getDensity()) {
this.flippingPage.setDrawingDensity("hard");
prevPage.setDrawingDensity("hard");
}
}
}
}
this.render.setDirection(direction);
this.calc = new FlipCalculation(direction, flipCorner, rect.pageWidth.toString(10), rect.height.toString(10));
return true;
} catch (e) {
return false;
}
}
do(pagePos) {
if (this.calc === null) return;
if (this.calc.calc(pagePos)) {
const progress = this.calc.getFlippingProgress();
this.bottomPage.setArea(this.calc.getBottomClipArea());
this.bottomPage.setPosition(this.calc.getBottomPagePosition());
this.bottomPage.setAngle(0);
this.bottomPage.setHardAngle(0);
this.flippingPage.setArea(this.calc.getFlippingClipArea());
this.flippingPage.setPosition(this.calc.getActiveCorner());
this.flippingPage.setAngle(this.calc.getAngle());
if (this.calc.getDirection() === 0) {
this.flippingPage.setHardAngle(90 * (200 - progress * 2) / 100);
} else {
this.flippingPage.setHardAngle(-90 * (200 - progress * 2) / 100);
}
this.render.setPageRect(this.calc.getRect());
this.render.setBottomPage(this.bottomPage);
this.render.setFlippingPage(this.flippingPage);
this.render.setShadowData(this.calc.getShadowStartPoint(), this.calc.getShadowAngle(), progress, this.calc.getDirection());
}
}
flipToPage(page, corner) {
const current = this.app.getPageCollection().getCurrentSpreadIndex();
const next = this.app.getPageCollection().getSpreadIndexByPage(page);
try {
if (next > current) {
this.app.getPageCollection().setCurrentSpreadIndex(next - 1);
this.flipNext(corner);
}
if (next < current) {
this.app.getPageCollection().setCurrentSpreadIndex(next + 1);
this.flipPrev(corner);
}
} catch (e) {}
}
flipNext(corner) {
this.flip({
x: this.render.getRect().left + this.render.getRect().pageWidth * 2 - 10,
y: corner === "top" ? 1 : this.render.getRect().height - 2
});
}
flipPrev(corner) {
this.flip({
x: 10,
y: corner === "top" ? 1 : this.render.getRect().height - 2
});
}
stopMove() {
if (this.calc === null) return;
const pos = this.calc.getPosition();
const rect = this.getBoundsRect();
const y = this.calc.getCorner() === "bottom" ? rect.height : 0;
if (pos.x <= 0) this.animateFlippingTo(pos, {
x: -rect.pageWidth,
y
}, true);else this.animateFlippingTo(pos, {
x: rect.pageWidth,
y
}, false);
}
showCorner(globalPos) {
if (!this.checkState("read", "fold_corner")) return;
const rect = this.getBoundsRect();
const pageWidth = rect.pageWidth;
if (this.isPointOnCorners(globalPos)) {
if (this.calc === null) {
if (!this.start(globalPos)) return;
this.setState("fold_corner");
this.calc.calc({
x: pageWidth - 1,
y: 1
});
const fixedCornerSize = 50;
const yStart = this.calc.getCorner() === "bottom" ? rect.height - 1 : 1;
const yDest = this.calc.getCorner() === "bottom" ? rect.height - fixedCornerSize : fixedCornerSize;
this.animateFlippingTo({
x: pageWidth - 1,
y: yStart
}, {
x: pageWidth - fixedCornerSize,
y: yDest
}, false, false);
} else {
this.do(this.render.convertToPage(globalPos));
}
} else {
this.setState("read");
this.render.finishAnimation();
this.stopMove();
}
}
animateFlippingTo(start, dest, isTurned, needReset = true) {
const points = Helper.GetCordsFromTwoPoint(start, dest);
const frames = [];
for (const p of points) frames.push(() => this.do(p));
const duration = this.getAnimationDuration(points.length);
this.render.startAnimation(frames, duration, () => {
if (!this.calc) return;
if (isTurned) {
if (this.calc.getDirection() === 1) this.app.turnToPrevPage();else this.app.turnToNextPage();
}
if (needReset) {
this.render.setBottomPage(null);
this.render.setFlippingPage(null);
this.render.clearShadow();
this.setState("read");
this.reset();
}
});
}
getCalculation() {
return this.calc;
}
getState() {
return this.state;
}
setState(newState) {
if (this.state !== newState) {
this.app.updateState(newState);
this.state = newState;
}
}
getDirectionByPoint(touchPos) {
const rect = this.getBoundsRect();
if (this.render.getOrientation() === "portrait") {
if (touchPos.x - rect.pageWidth <= rect.width / 5) {
return 1;
}
} else if (touchPos.x < rect.width / 2) {
return 1;
}
return 0;
}
getAnimationDuration(size) {
const defaultTime = this.app.getSettings().flippingTime;
if (size >= 1000) return defaultTime;
return size / 1000 * defaultTime;
}
checkDirection(direction) {
if (direction === 0) return this.app.getCurrentPageIndex() < this.app.getPageCount() - 1;
return this.app.getCurrentPageIndex() >= 1;
}
reset() {
this.calc = null;
this.flippingPage = null;
this.bottomPage = null;
}
getBoundsRect() {
return this.render.getRect();
}
checkState(...states) {
for (const state of states) {
if (this.state === state) return true;
}
return false;
}
isPointOnCorners(globalPos) {
const rect = this.getBoundsRect();
const pageWidth = rect.pageWidth;
const operatingDistance = Math.sqrt(Math.pow(pageWidth, 2) + Math.pow(rect.height, 2)) / 5;
const bookPos = this.render.convertToBook(globalPos);
return bookPos.x > 0 && bookPos.y > 0 && bookPos.x < rect.width && bookPos.y < rect.height && (bookPos.x < operatingDistance || bookPos.x > rect.width - operatingDistance) && (bookPos.y < operatingDistance || bookPos.y > rect.height - operatingDistance);
}
}
class Render {
constructor(app, setting) {
this.leftPage = null;
this.rightPage = null;
this.flippingPage = null;
this.bottomPage = null;
this.direction = null;
this.orientation = null;
this.shadow = null;
this.animation = null;
this.pageRect = null;
this.boundsRect = null;
this.timer = 0;
this.safari = false;
this.setting = setting;
this.app = app;
const regex = new RegExp('Version\\/[\\d\\.]+.*Safari/');
this.safari = regex.exec(window.navigator.userAgent) !== null;
}
render(timer) {
if (this.animation !== null) {
const frameIndex = Math.round((timer - this.animation.startedAt) / this.animation.durationFrame);
if (frameIndex < this.animation.frames.length) {
this.animation.frames[frameIndex]();
} else {
this.animation.onAnimateEnd();
this.animation = null;
}
}
this.timer = timer;
this.drawFrame();
}
start() {
this.update();
const loop = timer => {
this.render(timer);
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
}
startAnimation(frames, duration, onAnimateEnd) {
this.finishAnimation();
this.animation = {
frames,
duration,
durationFrame: duration / frames.length,
onAnimateEnd,
startedAt: this.timer
};
}
finishAnimation() {
if (this.animation !== null) {
this.animation.frames[this.animation.frames.length - 1]();
if (this.animation.onAnimateEnd !== null) {
this.animation.onAnimateEnd();
}
}
this.animation = null;
}
update() {
this.boundsRect = null;
const orientation = this.calculateBoundsRect();
if (this.orientation !== orientation) {
this.orientation = orientation;
this.app.updateOrientation(orientation);
}
}
calculateBoundsRect() {
let orientation = "landscape";
const blockWidth = this.getBlockWidth();
const middlePoint = {
x: blockWidth / 2,
y: this.getBlockHeight() / 2
};
const ratio = this.setting.width / this.setting.height;
let pageWidth = this.setting.width;
let pageHeight = this.setting.height;
let left = middlePoint.x - pageWidth;
if (this.setting.size === "stretch") {
if (blockWidth < this.setting.minWidth * 2 && this.app.getSettings().usePortrait) orientation = "portrait";
pageWidth = orientation === "portrait" ? this.getBlockWidth() : this.getBlockWidth() / 2;
if (pageWidth > this.setting.maxWidth) pageWidth = this.setting.maxWidth;
pageHeight = pageWidth / ratio;
if (pageHeight > this.getBlockHeight()) {
pageHeight = this.getBlockHeight();
pageWidth = pageHeight * ratio;
}
left = orientation === "portrait" ? middlePoint.x - pageWidth / 2 - pageWidth : middlePoint.x - pageWidth;
} else {
if (blockWidth < pageWidth * 2) {
if (this.app.getSettings().usePortrait) {
orientation = "portrait";
left = middlePoint.x - pageWidth / 2 - pageWidth;
}
}
}
this.boundsRect = {
left,
top: middlePoint.y - pageHeight / 2,
width: pageWidth * 2,
height: pageHeight,
pageWidth: pageWidth
};
return orientation;
}
setShadowData(pos, angle, progress, direction) {
if (!this.app.getSettings().drawShadow) return;
const maxShadowOpacity = 100 * this.getSettings().maxShadowOpacity;
this.shadow = {
pos,
angle,
width: this.getRect().pageWidth * 3 / 4 * progress / 100,
opacity: (100 - progress) * maxShadowOpacity / 100 / 100,
direction,
progress: progress * 2
};
}
clearShadow() {
this.shadow = null;
}
getBlockWidth() {
return this.app.getUI().getDistElement().offsetWidth;
}
getBlockHeight() {
return this.app.getUI().getDistElement().offsetHeight;
}
getDirection() {
return this.direction;
}
getRect() {
if (this.boundsRect === null) this.calculateBoundsRect();
return this.boundsRect;
}
getSettings() {
return this.app.getSettings();
}
getOrientation() {
return this.orientation;
}
setPageRect(pageRect) {
this.pageRect = pageRect;
}
setDirection(direction) {
this.direction = direction;
}
setRightPage(page) {
if (page !== null) page.setOrientation(1);
this.rightPage = page;
}
setLeftPage(page) {
if (page !== null) page.setOrientation(0);
this.leftPage = page;
}
setBottomPage(page) {
if (page !== null) page.setOrientation(this.direction === 1 ? 0 : 1);
this.bottomPage = page;
}
setFlippingPage(page) {
if (page !== null) page.setOrientation(this.direction === 0 && this.orientation !== "portrait" ? 0 : 1);
this.flippingPage = page;
}
convertToBook(pos) {
const rect = this.getRect();
return {
x: pos.x - rect.left,
y: pos.y - rect.top
};
}
isSafari() {
return this.safari;
}
convertToPage(pos, direction) {
if (!direction) direction = this.direction;
const rect = this.getRect();
const x = direction === 0 ? pos.x - rect.left - rect.width / 2 : rect.width / 2 - pos.x + rect.left;
return {
x,
y: pos.y - rect.top
};
}
convertToGlobal(pos, direction) {
if (!direction) direction = this.direction;
if (pos == null) return null;
const rect = this.getRect();
const x = direction === 0 ? pos.x + rect.left + rect.width / 2 : rect.width / 2 - pos.x + rect.left;
return {
x,
y: pos.y + rect.top
};
}
convertRectToGlobal(rect, direction) {
if (!direction) direction = this.direction;
return {
topLeft: this.convertToGlobal(rect.topLeft, direction),
topRight: this.convertToGlobal(rect.topRight, direction),
bottomLeft: this.convertToGlobal(rect.bottomLeft, direction),
bottomRight: this.convertToGlobal(rect.bottomRight, direction)
};
}
}
class CanvasRender extends Render {
constructor(app, setting, inCanvas) {
super(app, setting);
this.canvas = inCanvas;
const ctx = canvas.getContext("2d");
}
getContext() {
return this.ctx;
}
reload() {}
drawFrame() {
this.clear();
if (this.orientation !== "portrait") if (this.leftPage != null) this.leftPage.simpleDraw(0);
if (this.rightPage != null) this.rightPage.simpleDraw(1);
if (this.bottomPage != null) this.bottomPage.draw();
this.drawBookShadow();
if (this.flippingPage != null) this.flippingPage.draw();
if (this.shadow != null) {
this.drawOuterShadow();
this.drawInnerShadow();
}
const rect = this.getRect();
if (this.orientation === "portrait") {
this.ctx.beginPath();
this.ctx.rect(rect.left + rect.pageWidth, rect.top, rect.width, rect.height);
this.ctx.clip();
}
}
drawBookShadow() {
const rect = this.getRect();
this.ctx.save();
this.ctx.beginPath();
const shadowSize = rect.width / 20;
this.ctx.rect(rect.left, rect.top, rect.width, rect.height);
const shadowPos = {
x: rect.left + rect.width / 2 - shadowSize / 2,
y: 0
};
this.ctx.translate(shadowPos.x, shadowPos.y);
const outerGradient = this.ctx.createLinearGradient(0, 0, shadowSize, 0);
outerGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
outerGradient.addColorStop(0.4, 'rgba(0, 0, 0, 0.2)');
outerGradient.addColorStop(0.49, 'rgba(0, 0, 0, 0.1)');
outerGradient.addColorStop(0.5, 'rgba(0, 0, 0, 0.5)');
outerGradient.addColorStop(0.51, 'rgba(0, 0, 0, 0.4)');
outerGradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
this.ctx.clip();
this.ctx.fillStyle = outerGradient;
this.ctx.fillRect(0, 0, shadowSize, rect.height * 2);
this.ctx.restore();
}
drawOuterShadow() {
const rect = this.getRect();
this.ctx.save();
this.ctx.beginPath();
this.ctx.rect(rect.left, rect.top, rect.width, rect.height);
const shadowPos = this.convertToGlobal({
x: this.shadow.pos.x,
y: this.shadow.pos.y
});
this.ctx.translate(shadowPos.x, shadowPos.y);
this.ctx.rotate(Math.PI + this.shadow.angle + Math.PI / 2);
const outerGradient = this.ctx.createLinearGradient(0, 0, this.shadow.width, 0);
if (this.shadow.direction === 0) {
this.ctx.translate(0, -100);
outerGradient.addColorStop(0, 'rgba(0, 0, 0, ' + this.shadow.opacity + ')');
outerGradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
} else {
this.ctx.translate(-this.shadow.width, -100);
outerGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
outerGradient.addColorStop(1, 'rgba(0, 0, 0, ' + this.shadow.opacity + ')');
}
this.ctx.clip();
this.ctx.fillStyle = outerGradient;
this.ctx.fillRect(0, 0, this.shadow.width, rect.height * 2);
this.ctx.restore();
}
drawInnerShadow() {
const rect = this.getRect();
this.ctx.save();
this.ctx.beginPath();
const shadowPos = this.convertToGlobal({
x: this.shadow.pos.x,
y: this.shadow.pos.y
});
const pageRect = this.convertRectToGlobal(this.pageRect);
this.ctx.moveTo(pageRect.topLeft.x, pageRect.topLeft.y);
this.ctx.lineTo(pageRect.topRight.x, pageRect.topRight.y);
this.ctx.lineTo(pageRect.bottomRight.x, pageRect.bottomRight.y);
this.ctx.lineTo(pageRect.bottomLeft.x, pageRect.bottomLeft.y);
this.ctx.translate(shadowPos.x, shadowPos.y);
this.ctx.rotate(Math.PI + this.shadow.angle + Math.PI / 2);
const isw = this.shadow.width * 3 / 4;
const innerGradient = this.ctx.createLinearGradient(0, 0, isw, 0);
if (this.shadow.direction === 0) {
this.ctx.translate(-isw, -100);
innerGradient.addColorStop(1, 'rgba(0, 0, 0, ' + this.shadow.opacity + ')');
innerGradient.addColorStop(0.9, 'rgba(0, 0, 0, 0.05)');
innerGradient.addColorStop(0.7, 'rgba(0, 0, 0, ' + this.shadow.opacity + ')');
innerGradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
} else {
this.ctx.translate(0, -100);
innerGradient.addColorStop(0, 'rgba(0, 0, 0, ' + this.shadow.opacity + ')');
innerGradient.addColorStop(0.1, 'rgba(0, 0, 0, 0.05)');
innerGradient.addColorStop(0.3, 'rgba(0, 0, 0, ' + this.shadow.opacity + ')');
innerGradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
}
this.ctx.clip();
this.ctx.fillStyle = innerGradient;
this.ctx.fillRect(0, 0, isw, rect.height * 2);
this.ctx.restore();
}
clear() {
this.ctx.fillStyle = 'white';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
}
class UI {
constructor(inBlock, app, setting) {
this.cspPolicyService = app.cspPolicyService;
this.touchPoint = null;
this.swipeTimeout = 250;
this.onResize = () => {
this.update();
};
this.onMouseDown = e => {
if (this.checkTarget(e.target)) {
const pos = this.getMousePos(e.clientX, e.clientY);
this.app.startUserTouch(pos);
e.preventDefault();
}
};
this.onTouchStart = e => {
if (this.checkTarget(e.target)) {
if (e.changedTouches.length > 0) {
const t = e.changedTouches[0];
const pos = this.getMousePos(t.clientX, t.clientY);
this.touchPoint = {
point: pos,
time: Date.now()
};
setTimeout(() => {
if (this.touchPoint !== null) {
this.app.startUserTouch(pos);
}
}, this.swipeTimeout);
if (!this.app.getSettings().mobileScrollSupport) e.preventDefault();
}
}
};
this.onMouseUp = e => {
const pos = this.getMousePos(e.clientX, e.clientY);
this.app.userStop(pos);
};
this.onMouseMove = e => {
const pos = this.getMousePos(e.clientX, e.clientY);
this.app.userMove(pos, false);
};
this.onTouchMove = e => {
if (e.changedTouches.length > 0) {
const t = e.changedTouches[0];
const pos = this.getMousePos(t.clientX, t.clientY);
if (this.app.getSettings().mobileScrollSupport) {
if (this.touchPoint !== null) {
if (Math.abs(this.touchPoint.point.x - pos.x) > 10 || this.app.getState() !== "read") {
if (e.cancelable) this.app.userMove(pos, true);
}
}
if (this.app.getState() !== "read") {
e.preventDefault();
}
} else {
this.app.userMove(pos, true);
}
}
};
this.onTouchEnd = e => {
if (e.changedTouches.length > 0) {
const t = e.changedTouches[0];
const pos = this.getMousePos(t.clientX, t.clientY);
let isSwipe = false;
if (this.touchPoint !== null) {
const dx = pos.x - this.touchPoint.point.x;
const distY = Math.abs(pos.y - this.touchPoint.point.y);
if (Math.abs(dx) > this.swipeDistance && distY < this.swipeDistance * 2 && Date.now() - this.touchPoint.time < this.swipeTimeout) {
if (dx > 0) {
this.app.flipPrev(this.touchPoint.point.y < this.app.getRender().getRect().height / 2 ? "top" : "bottom");
} else {
this.app.flipNext(this.touchPoint.point.y < this.app.getRender().getRect().height / 2 ? "top" : "bottom");
}
isSwipe = true;
}
this.touchPoint = null;
}
this.app.userStop(pos, isSwipe);
}
};
this.parentElement = inBlock;
inBlock.classList.add('stf__parent');
const wrapperHtml = this.cspPolicyService.createTrustedHTML('<div class="stf__wrapper"></div>');
inBlock.insertAdjacentHTML('afterbegin', wrapperHtml);
this.wrapper = inBlock.querySelector('.stf__wrapper');
this.app = app;
const k = this.app.getSettings().usePortrait ? 1 : 2;
inBlock.style.minWidth = setting.minWidth * k + 'px';
inBlock.style.minHeight = setting.minHeight + 'px';
if (setting.size === "fixed") {
inBlock.style.minWidth = setting.width * k + 'px';
inBlock.style.minHeight = setting.height + 'px';
}
if (setting.autoSize) {
inBlock.style.width = '100%';
inBlock.style.maxWidth = setting.maxWidth * 2 + 'px';
}
inBlock.style.display = 'block';
window.addEventListener('resize', this.onResize, false);
this.swipeDistance = setting.swipeDistance;
}
destroy() {
if (this.app.getSettings().useMouseEvents) this.removeHandlers();
this.distElement.remove();
this.wrapper.remove();
}
getDistElement() {
return this.distElement;
}
getWrapper() {
return this.wrapper;
}
setOrientationStyle(orientation) {
this.wrapper.classList.remove('--portrait', '--landscape');
if (orientation === "portrait") {
if (this.app.getSettings().autoSize) this.wrapper.style.paddingBottom = this.app.getSettings().height / this.app.getSettings().width * 100 + '%';
this.wrapper.classList.add('--portrait');
} else {
if (this.app.getSettings().autoSize) this.wrapper.style.paddingBottom = this.app.getSettings().height / (this.app.getSettings().width * 2) * 100 + '%';
this.wrapper.classList.add('--landscape');
}
this.update();
}
removeHandlers() {
window.removeEventListener('resize', this.onResize);
this.distElement.removeEventListener('mousedown', this.onMouseDown);
this.distElement.removeEventListener('touchstart', this.onTouchStart);
window.removeEventListener('mousemove', this.onMouseMove);
window.removeEventListener('touchmove', this.onTouchMove);
window.removeEventListener('mouseup', this.onMouseUp);
window.removeEventListener('touchend', this.onTouchEnd);
}
setHandlers() {
window.addEventListener('resize', this.onResize, false);
if (!this.app.getSettings().useMouseEvents) return;
this.distElement.addEventListener('mousedown', this.onMouseDown);
this.distElement.addEventListener('touchstart', this.onTouchStart);
window.addEventListener('mousemove', this.onMouseMove);
window.addEventListener('touchmove', this.onTouchMove, {
passive: !this.app.getSettings().mobileScrollSupport
});
window.addEventListener('mouseup', this.onMouseUp);
window.addEventListener('touchend', this.onTouchEnd);
}
getMousePos(x, y) {
const rect = this.distElement.getBoundingClientRect();
return {
x: x - rect.left,
y: y - rect.top
};
}
checkTarget(targer) {
if (!this.app.getSettings().clickEventForward) return true;
if (['a', 'button'].includes(targer.tagName.toLowerCase())) {
return false;
}
return true;
}
}
class HTMLUI extends UI {
constructor(inBlock, app, setting, items, cspPolicyService) {
super(inBlock, app, setting);
this.cspPolicyService = cspPolicyService;
const wrapperHtml = this.cspPolicyService.createTrustedHTML('<div class="stf__block"></div>');
this.wrapper.insertAdjacentHTML('afterbegin', wrapperHtml);
this.distElement = inBlock.querySelector('.stf__block');
this.items = items;
for (const item of items) {
this.distElement.appendChild(item);
}
this.setHandlers();
}
clear() {
for (const item of this.items) {
this.parentElement.appendChild(item);
}
}
updateItems(items) {
this.removeHandlers();
this.distElement.innerHTML = '';
for (const item of items) {
this.distElement.appendChild(item);
}
this.items = items;
this.setHandlers();
}
update() {
this.app.getRender().update();
}
}
class CanvasUI extends UI {
constructor(inBlock, app, setting) {
super(inBlock, app, setting);
this.wrapper.innerHTML = '<canvas class="stf__canvas"></canvas>';
this.canvas = inBlock.querySelectorAll('canvas')[0];
this.distElement = this.canvas;
this.resizeCanvas();
this.setHandlers();
}
resizeCanvas() {
const cs = getComputedStyle(this.canvas);
const width = parseInt(cs.getPropertyValue('width'), 10);
const height = parseInt(cs.getPropertyValue('height'), 10);
this.canvas.width = width;
this.canvas.height = height;
}
getCanvas() {
return this.canvas;
}
update() {
this.resizeCanvas();
this.app.getRender().update();
}
}
class EventObject {
constructor() {
this.events = new Map();
}
on(eventName, callback) {
if (!this.events.has(eventName)) {
this.events.set(eventName, [callback]);
} else {
this.events.get(eventName).push(callback);
}
return this;
}
off(event) {
this.events.delete(event);
}
trigger(eventName, app, data = null) {
if (!this.events.has(eventName)) return;
for (const callback of this.events.get(eventName)) {
callback({
data,
object: app
});
}
}
}
class HTMLRender extends Render {
constructor(app, setting, element) {
super(app, setting);
this.outerShadow = null;
this.innerShadow = null;
this.hardShadow = null;
this.hardInnerShadow = null;
this.element = element;
this.createShadows();
}
createShadows() {
const shadowHtml = this.app.cspPolicyService.createTrustedHTML(`<div class="stf__outerShadow"></div>
<div class="stf__innerShadow"></div>
<div class="stf__hardShadow"></div>
<div class="stf__hardInnerShadow"></div>`);
this.element.insertAdjacentHTML('beforeend', shadowHtml);
this.outerShadow = this.element.querySelector('.stf__outerShadow');
this.innerShadow = this.element.querySelector('.stf__innerShadow');
this.hardShadow = this.element.querySelector('.stf__hardShadow');
this.hardInnerShadow = this.element.querySelector('.stf__hardInnerShadow');
}
clearShadow() {
super.clearShadow();
this.outerShadow.style.cssText = 'display: none';
this.innerShadow.style.cssText = 'display: none';
this.hardShadow.style.cssText = 'display: none';
this.hardInnerShadow.style.cssText = 'display: none';
}
reload() {
const testShadow = this.element.querySelector('.stf__outerShadow');
if (!testShadow) {
this.createShadows();
}
}
drawHardInnerShadow() {
const rect = this.getRect();
const progress = this.shadow.progress > 100 ? 200 - this.shadow.progress : this.shadow.progress;
let innerShadowSize = (100 - progress) * (2.5 * rect.pageWidth) / 100 + 20;
if (innerShadowSize > rect.pageWidth) innerShadowSize = rect.pageWidth;
let newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 5).toString(10)};
width: ${innerShadowSize}px;
height: ${rect.height}px;
background: linear-gradient(to right,
rgba(0, 0, 0, ${this.shadow.opacity * progress / 100}) 5%,
rgba(0, 0, 0, 0) 100%);
left: ${rect.left + rect.width / 2}px;
transform-origin: 0 0;
`;
newStyle += this.getDirection() === 0 && this.shadow.progress > 100 || this.getDirection() === 1 && this.shadow.progress <= 100 ? `transform: translate3d(0, 0, 0);` : `transform: translate3d(0, 0, 0) rotateY(180deg);`;
this.hardInnerShadow.style.cssText = newStyle;
}
drawHardOuterShadow() {
const rect = this.getRect();
const progress = this.shadow.progress > 100 ? 200 - this.shadow.progress : this.shadow.progress;
let shadowSize = (100 - progress) * (2.5 * rect.pageWidth) / 100 + 20;
if (shadowSize > rect.pageWidth) shadowSize = rect.pageWidth;
let newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 4).toString(10)};
width: ${shadowSize}px;
height: ${rect.height}px;
background: linear-gradient(to left, rgba(0, 0, 0, ${this.shadow.opacity}) 5%, rgba(0, 0, 0, 0) 100%);
left: ${rect.left + rect.width / 2}px;
transform-origin: 0 0;
`;
newStyle += this.getDirection() === 0 && this.shadow.progress > 100 || this.getDirection() === 1 && this.shadow.progress <= 100 ? `transform: translate3d(0, 0, 0) rotateY(180deg);` : `transform: translate3d(0, 0, 0);`;
this.hardShadow.style.cssText = newStyle;
}
drawInnerShadow() {
const rect = this.getRect();
const innerShadowSize = this.shadow.width * 3 / 4;
const shadowTranslate = this.getDirection() === 0 ? innerShadowSize : 0;
const shadowDirection = this.getDirection() === 0 ? 'to left' : 'to right';
const shadowPos = this.convertToGlobal(this.shadow.pos);
const angle = this.shadow.angle + 3 * Math.PI / 2;
const clip = [this.pageRect.topLeft, this.pageRect.topRight, this.pageRect.bottomRight, this.pageRect.bottomLeft];
let polygon = 'polygon( ';
for (const p of clip) {
let g = this.getDirection() === 1 ? {
x: -p.x + this.shadow.pos.x,
y: p.y - this.shadow.pos.y
} : {
x: p.x - this.shadow.pos.x,
y: p.y - this.shadow.pos.y
};
g = Helper.GetRotatedPoint(g, {
x: shadowTranslate,
y: 100
}, angle);
polygon += g.x + 'px ' + g.y + 'px, ';
}
polygon = polygon.slice(0, -2);
polygon += ')';
const newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 10).toString(10)};
width: ${innerShadowSize}px;
height: ${rect.height * 2}px;
background: linear-gradient(${shadowDirection},
rgba(0, 0, 0, ${this.shadow.opacity}) 5%,
rgba(0, 0, 0, 0.05) 15%,
rgba(0, 0, 0, ${this.shadow.opacity}) 35%,
rgba(0, 0, 0, 0) 100%);
transform-origin: ${shadowTranslate}px 100px;
transform: translate3d(${shadowPos.x - shadowTranslate}px, ${shadowPos.y - 100}px, 0) rotate(${angle}rad);
clip-path: ${polygon};
-webkit-clip-path: ${polygon};
`;
this.innerShadow.style.cssText = newStyle;
}
drawOuterShadow() {
const rect = this.getRect();
const shadowPos = this.convertToGlobal({
x: this.shadow.pos.x,
y: this.shadow.pos.y
});
const angle = this.shadow.angle + 3 * Math.PI / 2;
const shadowTranslate = this.getDirection() === 1 ? this.shadow.width : 0;
const shadowDirection = this.getDirection() === 0 ? 'to right' : 'to left';
const clip = [{
x: 0,
y: 0
}, {
x: rect.pageWidth,
y: 0
}, {
x: rect.pageWidth,
y: rect.height
}, {
x: 0,
y: rect.height
}];
let polygon = 'polygon( ';
for (const p of clip) {
if (p !== null) {
let g = this.getDirection() === 1 ? {
x: -p.x + this.shadow.pos.x,
y: p.y - this.shadow.pos.y
} : {
x: p.x - this.shadow.pos.x,
y: p.y - this.shadow.pos.y
};
g = Helper.GetRotatedPoint(g, {
x: shadowTranslate,
y: 100
}, angle);
polygon += g.x + 'px ' + g.y + 'px, ';
}
}
polygon = polygon.slice(0, -2);
polygon += ')';
const newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 10).toString(10)};
width: ${this.shadow.width}px;
height: ${rect.height * 2}px;
background: linear-gradient(${shadowDirection}, rgba(0, 0, 0, ${this.shadow.opacity}), rgba(0, 0, 0, 0));
transform-origin: ${shadowTranslate}px 100px;
transform: translate3d(${shadowPos.x - shadowTranslate}px, ${shadowPos.y - 100}px, 0) rotate(${angle}rad);
clip-path: ${polygon};
-webkit-clip-path: ${polygon};
`;
this.outerShadow.style.cssText = newStyle;
}
drawLeftPage() {
if (this.orientation === "portrait" || this.leftPage === null) return;
if (this.direction === 1 && this.flippingPage !== null && this.flippingPage.getDrawingDensity() === "hard") {
const angle = this.flippingPage.getHardAngle();
if (angle < -90) {
this.leftPage.getElement().style.zIndex = (this.getSettings().startZIndex + 5).toString(10);
this.leftPage.setHardDrawingAngle(180 + this.flippingPage.getHardAngle());
this.leftPage.draw(this.flippingPage.getDrawingDensity());
} else {
this.leftPage.getElement().style.display = "none";
}
} else {
this.leftPage.simpleDraw(0);
}
}
drawRightPage() {
if (this.rightPage === null) return;
if (this.direction === 0 && this.flippingPage !== null && this.flippingPage.getDrawingDensity() === "hard") {
const angle = this.flippingPage.getHardAngle();
if (angle > 90) {
this.rightPage.getElement().style.zIndex = (this.getSettings().startZIndex + 5).toString(10);
this.rightPage.setHardDrawingAngle(180 + this.flippingPage.getHardAngle());
this.rightPage.draw(this.flippingPage.getDrawingDensity());
} else {
this.rightPage.getElement().style.display = "none";
}
} else {
this.rightPage.simpleDraw(1);
}
}
drawBottomPage() {
if (this.bottomPage === null) return;
const tempDensity = this.flippingPage != null ? this.flippingPage.getDrawingDensity() : null;
if (!(this.orientation === "portrait" && this.direction === 1)) {
this.bottomPage.getElement().style.zIndex = (this.getSettings().startZIndex + 3).toString(10);
this.bottomPage.draw(tempDensity);
}
}
drawFrame() {
if (this.flippingPage !== null) {
if (this.flippingPage.getHardAngle() === this.lastAngle) {
return;
}
this.lastAngle = this.flippingPage.getHardAngle();
} else {
this.lastAngle = -1234;
}
this.clear();
this.drawLeftPage();
this.drawRightPage();
this.drawBottomPage();
if (this.flippingPage != null) {
const angle = this.flippingPage.state.hardDrawingAngle;
if (angle <= 90) {
this.flippingPage.getElement().style.zIndex = (this.getSettings().startZIndex + 5).toString(10);
this.flippingPage.draw();
} else {
this.flippingPage.getElement().style.display = "none";
}
}
if (this.shadow != null && this.flippingPage !== null) {
if (this.flippingPage.getDrawingDensity() === "soft") {
this.drawOuterShadow();
this.drawInnerShadow();
} else {
this.drawHardOuterShadow();
this.drawHardInnerShadow();
}
}
}
clear() {
for (const page of this.app.getPageCollection().getPages()) {
if (page !== this.leftPage && page !== this.rightPage && page !== this.flippingPage && page !== this.bottomPage) {
const style = page.getElement().style;
if (style.display !== 'none') {
style.cssText = 'display: none';
}
}
if (page.getTemporaryCopy() !== this.flippingPage) {
page.hideTemporaryCopy();
}
}
}
update() {
super.update();
if (this.rightPage !== null) {
this.rightPage.setOrientation(1);
}
if (this.leftPage !== null) {
this.leftPage.setOrientation(0);
}
}
}
class Settings {
constructor() {
this._default = {
startPage: 0,
size: "fixed",
width: 0,
height: 0,
minWidth: 0,
maxWidth: 0,
minHeight: 0,
maxHeight: 0,
drawShadow: true,
flippingTime: 1000,
usePortrait: true,
startZIndex: 0,
autoSize: true,
maxShadowOpacity: 1,
showCover: false,
mobileScrollSupport: true,
swipeDistance: 30,
clickEventForward: true,
useMouseEvents: true,
showPageCorners: true,
disableFlipByClick: false
};
}
getSettings(userSetting) {
const result = this._default;
Object.assign(result, userSetting);
if (result.size !== "stretch" && result.size !== "fixed") throw new Error('Invalid size type. Available only "fixed" and "stretch" value');
if (result.width <= 0 || result.height <= 0) throw new Error('Invalid width or height');
if (result.flippingTime <= 0) throw new Error('Invalid flipping time');
if (result.size === "stretch") {
if (result.minWidth <= 0) result.minWidth = 100;
if (result.maxWidth < result.minWidth) result.maxWidth = 2000;
if (result.minHeight <= 0) result.minHeight = 100;
if (result.maxHeight < result.minHeight) result.maxHeight = 2000;
} else {
result.minWidth = result.width;
result.maxWidth = result.width;
result.minHeight = result.height;
result.maxHeight = result.height;
}
return result;
}
}
class PageFlip extends EventObject {
constructor(inBlock, setting, cspPolicyService) {
super();
this.isUserTouch = false;
this.isUserMove = false;
this.setting = null;
this.pages = null;
this.setting = new Settings().getSettings(setting);
this.block = inBlock;
this.cspPolicyService = cspPolicyService;
}
destroy() {
this.ui.destroy();
this.render = undefined;
}
update() {
this.render.update();
this.pages.show();
}
loadFromHTML(items) {
this.ui = new HTMLUI(this.block, this, this.setting, items, this.cspPolicyService);
this.render = new HTMLRender(this, this.setting, this.ui.getDistElement());
this.flipController = new Flip(this.render, this);
this.pages = new HTMLPageCollection(this, this.render, this.ui.getDistElement(), items);
this.pages.load();
this.render.start();
this.pages.show(this.setting.startPage);
setTimeout(() => {
this.ui.update();
this.trigger('init', this, {
page: this.setting.startPage,
mode: this.render.getOrientation()
});
}, 1);
}
updateFromHtml(items) {
const current = this.pages.getCurrentPageIndex();
this.pages.destroy();
this.pages = new HTMLPageCollection(this, this.render, this.ui.getDistElement(), items);
this.pages.load();
this.ui.updateItems(items);
this.render.reload();
this.pages.show(current);
this.trigger('update', this, {
page: current,
mode: this.render.getOrientation()
});
}
clear() {
this.pages.destroy();
this.ui.clear();
}
turnToPrevPage() {
this.pages.showPrev();
}
turnToNextPage() {
this.pages.showNext();
}
turnToPage(page) {
this.pages.show(page);
}
flipNext(corner = "top") {
this.flipController.flipNext(corner);
}
flipPrev(corner = "top") {
this.flipController.flipPrev(corner);
}
flip(page, corner = "top") {
this.flipController.flipToPage(page, corner);
}
updateState(newState) {
this.trigger('changeState', this, newState);
}
updatePageIndex(newPage) {
this.trigger('flip', this, newPage);
}
updateOrientation(newOrientation) {
this.ui.setOrientationStyle(newOrientation);
this.update();
this.trigger('changeOrientation', this, newOrientation);
}
getPageCount() {
return this.pages.getPageCount();
}
getCurrentPageIndex() {
return this.pages.getCurrentPageIndex();
}
getPage(pageIndex) {
return this.pages.getPage(pageIndex);
}
getRender() {
return this.render;
}
getFlipController() {
return this.flipController;
}
getOrientation() {
return this.render.getOrientation();
}
getBoundsRect() {
return this.render.getRect();
}
getSettings() {
return this.setting;
}
getUI() {
return this.ui;
}
getState() {
return this.flipController.getState();
}
getPageCollection() {
return this.pages;
}
startUserTouch(pos) {
this.mousePosition = pos;
this.isUserTouch = true;
this.isUserMove = false;
}
userMove(pos, isTouch) {
if (!this.isUserTouch && !isTouch && this.setting.showPageCorners) {
this.flipController.showCorner(pos);
} else if (this.isUserTouch) {
if (Helper.GetDistanceBetweenTwoPoint(this.mousePosition, pos) > 5) {
this.isUserMove = true;
this.flipController.fold(pos);
}
}
}
userStop(pos, isSwipe = false) {
if (this.isUserTouch) {
this.isUserTouch = false;
if (!isSwipe) {
if (!this.isUserMove) this.flipController.flip(pos);else this.flipController.stopMove();
}
}
}
}
;// ./web/annotation_editor_layer_builder.js
class AnnotationEditorLayerBuilder {
#annotationLayer = null;
#drawLayer = null;
#onAppend = null;
#structTreeLayer = null;
#textLayer = null;
#uiManager;
constructor(options) {
this.pdfPage = options.pdfPage;
this.accessibilityManager = options.accessibilityManager;
this.l10n = options.l10n;
this.l10n ||= new genericl10n_GenericL10n();
this.annotationEditorLayer = null;
this.div = null;
this._cancelled = false;
this.#uiManager = options.uiManager;
this.#annotationLayer = options.annotationLayer || null;
this.#textLayer = options.textLayer || null;
this.#drawLayer = options.drawLayer || null;
this.#onAppend = options.onAppend || null;
this.eventBus = options.eventBus;
this.#structTreeLayer = options.structTreeLayer || null;
}
async render(viewport, intent = "display") {
if (intent !== "display") {
return;
}
if (this._cancelled) {
return;
}
const clonedViewport = viewport.clone({
dontFlip: true
});
if (this.div) {
this.annotationEditorLayer.update({
viewport: clonedViewport
});
this.show();
return;
}
const div = this.div = document.createElement("div");
div.className = "annotationEditorLayer";
div.hidden = true;
div.dir = this.#uiManager.direction;
this.#onAppend?.(div);
this.annotationEditorLayer = new pdf.AnnotationEditorLayer({
uiManager: this.#uiManager,
div,
structTreeLayer: this.#structTreeLayer,
accessibilityManager: this.accessibilityManager,
pageIndex: this.pdfPage.pageNumber - 1,
l10n: this.l10n,
viewport: clonedViewport,
annotationLayer: this.#annotationLayer,
textLayer: this.#textLayer,
drawLayer: this.#drawLayer,
eventBus: this.eventBus
});
const parameters = {
viewport: clonedViewport,
div,
annotations: null,
intent
};
this.annotationEditorLayer.render(parameters);
this.show();
}
cancel() {
this._cancelled = true;
if (!this.div) {
return;
}
this.annotationEditorLayer.destroy();
}
hide() {
if (!this.div) {
return;
}
this.annotationEditorLayer.pause(true);
this.div.hidden = true;
}
show() {
if (!this.div || this.annotationEditorLayer.isInvisible) {
return;
}
this.div.hidden = false;
this.annotationEditorLayer.pause(false);
}
}
;// ./web/annotation_layer_builder.js
class AnnotationLayerBuilder {
#onAppend = null;
#eventAbortController = null;
constructor({
pdfPage,
linkService,
downloadManager,
annotationStorage = null,
imageResourcesPath = "",
renderForms = true,
enableScripting = false,
hasJSActionsPromise = null,
fieldObjectsPromise = null,
annotationCanvasMap = null,
accessibilityManager = null,
annotationEditorUIManager = null,
onAppend = null
}) {
this.pdfPage = pdfPage;
this.linkService = linkService;
this.downloadManager = downloadManager;
this.imageResourcesPath = imageResourcesPath;
this.renderForms = renderForms;
this.annotationStorage = annotationStorage;
this.enableScripting = enableScripting;
this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false);
this._fieldObjectsPromise = fieldObjectsPromise || Promise.resolve(null);
this._annotationCanvasMap = annotationCanvasMap;
this._accessibilityManager = accessibilityManager;
this._annotationEditorUIManager = annotationEditorUIManager;
this.#onAppend = onAppend;
this.annotationLayer = null;
this.div = null;
this._cancelled = false;
this._eventBus = linkService.eventBus;
}
async render(viewport, options, intent = "display") {
if (this.div) {
if (this._cancelled || !this.annotationLayer) {
return;
}
this.annotationLayer.update({
viewport: viewport.clone({
dontFlip: true
})
});
return;
}
const [annotations, hasJSActions, fieldObjects] = await Promise.all([this.pdfPage.getAnnotations({
intent
}), this._hasJSActionsPromise, this._fieldObjectsPromise]);
if (this._cancelled) {
return;
}
const div = this.div = document.createElement("div");
div.className = "annotationLayer";
this.#onAppend?.(div);
if (annotations.length === 0) {
this.hide();
return;
}
this.annotationLayer = new pdf.AnnotationLayer({
div,
accessibilityManager: this._accessibilityManager,
annotationCanvasMap: this._annotationCanvasMap,
annotationEditorUIManager: this._annotationEditorUIManager,
page: this.pdfPage,
viewport: viewport.clone({
dontFlip: true
}),
structTreeLayer: options?.structTreeLayer || null
});
await this.annotationLayer.render({
annotations,
imageResourcesPath: this.imageResourcesPath,
renderForms: this.renderForms,
linkService: this.linkService,
downloadManager: this.downloadManager,
annotationStorage: this.annotationStorage,
enableScripting: this.enableScripting,
hasJSActions,
fieldObjects
});
if (this.linkService.isInPresentationMode) {
this.#updatePresentationModeState(PresentationModeState.FULLSCREEN);
}
if (!this.#eventAbortController) {
this.#eventAbortController = new AbortController();
this._eventBus?._on("presentationmodechanged", evt => {
this.#updatePresentationModeState(evt.state);
}, {
signal: this.#eventAbortController.signal
});
}
}
cancel() {
this._cancelled = true;
this.#eventAbortController?.abort();
this.#eventAbortController = null;
}
hide() {
if (!this.div) {
return;
}
if (!document.querySelector("[data-pdfjsprinting=true]")) {
this.div.hidden = true;
}
}
hasEditableAnnotations() {
return !!this.annotationLayer?.hasEditableAnnotations();
}
#updatePresentationModeState(state) {
if (!this.div) {
return;
}
let disableFormElements = false;
switch (state) {
case PresentationModeState.FULLSCREEN:
disableFormElements = true;
break;
case PresentationModeState.NORMAL:
break;
default:
return;
}
for (const section of this.div.childNodes) {
if (section.hasAttribute("data-internal-link")) {
continue;
}
section.inert = disableFormElements;
}
}
}
;// ./web/draw_layer_builder.js
class DrawLayerBuilder {
#drawLayer = null;
constructor(options) {
this.pageIndex = options.pageIndex;
}
async render(intent = "display") {
if (intent !== "display" || this.#drawLayer || this._cancelled) {
return;
}
this.#drawLayer = new pdf.DrawLayer({
pageIndex: this.pageIndex
});
}
cancel() {
this._cancelled = true;
if (!this.#drawLayer) {
return;
}
this.#drawLayer.destroy();
this.#drawLayer = null;
}
setParent(parent) {
this.#drawLayer?.setParent(parent);
}
getDrawLayer() {
return this.#drawLayer;
}
}
;// ./web/struct_tree_layer_builder.js
const PDF_ROLE_TO_HTML_ROLE = {
Document: null,
DocumentFragment: null,
Part: "group",
Sect: "group",
Div: "group",
Aside: "note",
NonStruct: "none",
P: null,
H: "heading",
Title: null,
FENote: "note",
Sub: "group",
Lbl: null,
Span: null,
Em: null,
Strong: null,
Link: "link",
Annot: "note",
Form: "form",
Ruby: null,
RB: null,
RT: null,
RP: null,
Warichu: null,
WT: null,
WP: null,
L: "list",
LI: "listitem",
LBody: null,
Table: "table",
TR: "row",
TH: "columnheader",
TD: "cell",
THead: "columnheader",
TBody: null,
TFoot: null,
Caption: null,
Figure: "figure",
Formula: null,
Artifact: null
};
const HEADING_PATTERN = /^H(\d+)$/;
class StructTreeLayerBuilder {
#promise;
#treeDom = null;
#treePromise;
#elementAttributes = new Map();
#rawDims;
#elementsToAddToTextLayer = null;
constructor(pdfPage, rawDims) {
this.#promise = pdfPage.getStructTree();
this.#rawDims = rawDims;
}
async render() {
if (this.#treePromise) {
return this.#treePromise;
}
const {
promise,
resolve,
reject
} = Promise.withResolvers();
this.#treePromise = promise;
try {
this.#treeDom = this.#walk(await this.#promise);
} catch (ex) {
reject(ex);
}
this.#promise = null;
this.#treeDom?.classList.add("structTree");
resolve(this.#treeDom);
return promise;
}
async getAriaAttributes(annotationId) {
try {
await this.render();
return this.#elementAttributes.get(annotationId);
} catch {}
return null;
}
hide() {
if (this.#treeDom && !this.#treeDom.hidden) {
this.#treeDom.hidden = true;
}
}
show() {
if (this.#treeDom?.hidden) {
this.#treeDom.hidden = false;
}
}
#setAttributes(structElement, htmlElement) {
const {
alt,
id,
lang
} = structElement;
if (alt !== undefined) {
let added = false;
const label = removeNullCharacters(alt);
for (const child of structElement.children) {
if (child.type === "annotation") {
let attrs = this.#elementAttributes.get(child.id);
if (!attrs) {
attrs = new Map();
this.#elementAttributes.set(child.id, attrs);
}
attrs.set("aria-label", label);
added = true;
}
}
if (!added) {
htmlElement.setAttribute("aria-label", label);
}
}
if (id !== undefined) {
htmlElement.setAttribute("aria-owns", id);
}
if (lang !== undefined) {
htmlElement.setAttribute("lang", removeNullCharacters(lang, true));
}
}
#addImageInTextLayer(node, element) {
const {
alt,
bbox,
children
} = node;
const child = children?.[0];
if (!this.#rawDims || !alt || !bbox || child?.type !== "content") {
return false;
}
const {
id
} = child;
if (!id) {
return false;
}
element.setAttribute("aria-owns", id);
const img = document.createElement("span");
(this.#elementsToAddToTextLayer ||= new Map()).set(id, img);
img.setAttribute("role", "img");
img.setAttribute("aria-label", removeNullCharacters(alt));
const {
pageHeight,
pageX,
pageY
} = this.#rawDims;
const calc = "calc(var(--scale-factor)*";
const {
style
} = img;
style.width = `${calc}${bbox[2] - bbox[0]}px)`;
style.height = `${calc}${bbox[3] - bbox[1]}px)`;
style.left = `${calc}${bbox[0] - pageX}px)`;
style.top = `${calc}${pageHeight - bbox[3] + pageY}px)`;
return true;
}
addElementsToTextLayer() {
if (!this.#elementsToAddToTextLayer) {
return;
}
for (const [id, img] of this.#elementsToAddToTextLayer) {
document.getElementById(id)?.append(img);
}
this.#elementsToAddToTextLayer.clear();
this.#elementsToAddToTextLayer = null;
}
#walk(node) {
if (!node) {
return null;
}
const element = document.createElement("span");
if ("role" in node) {
const {
role
} = node;
const match = role.match(HEADING_PATTERN);
if (match) {
element.setAttribute("role", "heading");
element.setAttribute("aria-level", match[1]);
} else if (PDF_ROLE_TO_HTML_ROLE[role]) {
element.setAttribute("role", PDF_ROLE_TO_HTML_ROLE[role]);
}
if (role === "Figure" && this.#addImageInTextLayer(node, element)) {
return element;
}
}
this.#setAttributes(node, element);
if (node.children) {
if (node.children.length === 1 && "id" in node.children[0]) {
this.#setAttributes(node.children[0], element);
} else {
for (const kid of node.children) {
element.append(this.#walk(kid));
}
}
}
return element;
}
}
;// ./web/text_accessibility.js
class TextAccessibilityManager {
#enabled = false;
#textChildren = null;
#textNodes = new Map();
#waitingElements = new Map();
setTextMapping(textDivs) {
this.#textChildren = textDivs;
}
static #compareElementPositions(e1, e2) {
const rect1 = e1.getBoundingClientRect();
const rect2 = e2.getBoundingClientRect();
if (rect1.width === 0 && rect1.height === 0) {
return +1;
}
if (rect2.width === 0 && rect2.height === 0) {
return -1;
}
const top1 = rect1.y;
const bot1 = rect1.y + rect1.height;
const mid1 = rect1.y + rect1.height / 2;
const top2 = rect2.y;
const bot2 = rect2.y + rect2.height;
const mid2 = rect2.y + rect2.height / 2;
if (mid1 <= top2 && mid2 >= bot1) {
return -1;
}
if (mid2 <= top1 && mid1 >= bot2) {
return +1;
}
const centerX1 = rect1.x + rect1.width / 2;
const centerX2 = rect2.x + rect2.width / 2;
return centerX1 - centerX2;
}
enable() {
if (this.#enabled) {
throw new Error("TextAccessibilityManager is already enabled.");
}
if (!this.#textChildren) {
throw new Error("Text divs and strings have not been set.");
}
this.#enabled = true;
this.#textChildren = this.#textChildren.slice();
this.#textChildren.sort(TextAccessibilityManager.#compareElementPositions);
if (this.#textNodes.size > 0) {
const textChildren = this.#textChildren;
for (const [id, nodeIndex] of this.#textNodes) {
const element = document.getElementById(id);
if (!element) {
this.#textNodes.delete(id);
continue;
}
this.#addIdToAriaOwns(id, textChildren[nodeIndex]);
}
}
for (const [element, isRemovable] of this.#waitingElements) {
this.addPointerInTextLayer(element, isRemovable);
}
this.#waitingElements.clear();
}
disable() {
if (!this.#enabled) {
return;
}
this.#waitingElements.clear();
this.#textChildren = null;
this.#enabled = false;
}
removePointerInTextLayer(element) {
if (!this.#enabled) {
this.#waitingElements.delete(element);
return;
}
const children = this.#textChildren;
if (!children || children.length === 0) {
return;
}
const {
id
} = element;
const nodeIndex = this.#textNodes.get(id);
if (nodeIndex === undefined) {
return;
}
const node = children[nodeIndex];
this.#textNodes.delete(id);
let owns = node.getAttribute("aria-owns");
if (owns?.includes(id)) {
owns = owns.split(" ").filter(x => x !== id).join(" ");
if (owns) {
node.setAttribute("aria-owns", owns);
} else {
node.removeAttribute("aria-owns");
node.setAttribute("role", "presentation");
}
}
}
#addIdToAriaOwns(id, node) {
const owns = node.getAttribute("aria-owns");
if (!owns?.includes(id)) {
node.setAttribute("aria-owns", owns ? `${owns} ${id}` : id);
}
node.removeAttribute("role");
}
addPointerInTextLayer(element, isRemovable) {
const {
id
} = element;
if (!id) {
return null;
}
if (!this.#enabled) {
this.#waitingElements.set(element, isRemovable);
return null;
}
if (isRemovable) {
this.removePointerInTextLayer(element);
}
const children = this.#textChildren;
if (!children || children.length === 0) {
return null;
}
const index = binarySearchFirstItem(children, node => TextAccessibilityManager.#compareElementPositions(element, node) < 0);
const nodeIndex = Math.max(0, index - 1);
const child = children[nodeIndex];
this.#addIdToAriaOwns(id, child);
this.#textNodes.set(id, nodeIndex);
const parent = child.parentNode;
return parent?.classList.contains("markedContent") ? parent.id : null;
}
moveElementInDOM(container, element, contentElement, isRemovable) {
const id = this.addPointerInTextLayer(contentElement, isRemovable);
if (!container.hasChildNodes()) {
container.append(element);
return id;
}
const children = Array.from(container.childNodes).filter(node => node !== element);
if (children.length === 0) {
return id;
}
const elementToCompare = contentElement || element;
const index = binarySearchFirstItem(children, node => TextAccessibilityManager.#compareElementPositions(elementToCompare, node) < 0);
if (index === 0) {
children[0].before(element);
} else {
children[index - 1].after(element);
}
return id;
}
}
;// ./web/text_highlighter.js
class TextHighlighter {
#eventAbortController = null;
constructor({
findController,
customFindController,
eventBus,
pageIndex
}) {
this.findController = findController;
this.customFindController = customFindController;
this.matches = [];
this.eventBus = eventBus;
this.pageIdx = pageIndex;
this.textDivs = null;
this.textContentItemsStr = null;
this.enabled = false;
}
setTextMapping(divs, texts) {
this.textDivs = divs;
this.textContentItemsStr = texts;
}
enable() {
if (!this.textDivs || !this.textContentItemsStr) {
throw new Error("Text divs and strings have not been set.");
}
if (this.enabled) {
return;
}
this.enabled = true;
if (!this.#eventAbortController) {
this.#eventAbortController = new AbortController();
this.eventBus._on("updatetextlayermatches", evt => {
if (evt.pageIndex === this.pageIdx || evt.pageIndex === -1) {
this._updateMatches();
}
}, {
signal: this.#eventAbortController.signal
});
}
this._updateMatches();
}
disable() {
if (!this.enabled) {
return;
}
this.enabled = false;
this.#eventAbortController?.abort();
this.#eventAbortController = null;
this._updateMatches(true);
}
_convertMatches(matches, matchesLength, cssClass = "highlight", highlightAll = false, isPageWithSelection = false, selectedMatchIdx = -1) {
if (!matches) {
return [];
}
const {
textContentItemsStr
} = this;
let i = 0,
iIndex = 0;
const end = textContentItemsStr.length - 1;
const result = [];
for (let m = 0, mm = matches.length; m < mm; m++) {
let matchIdx = matches[m];
while (i !== end && matchIdx >= iIndex + textContentItemsStr[i].length) {
iIndex += textContentItemsStr[i].length;
i++;
}
if (i === textContentItemsStr.length) {
ngx_console.NgxConsole.error("Could not find a matching mapping");
}
const match = {
begin: {
divIdx: i,
offset: matchIdx - iIndex
},
cssClass,
highlightAll,
selected: isPageWithSelection && m === selectedMatchIdx
};
matchIdx += matchesLength[m];
while (i !== end && matchIdx > iIndex + textContentItemsStr[i].length) {
iIndex += textContentItemsStr[i].length;
i++;
}
match.end = {
divIdx: i,
offset: matchIdx - iIndex
};
result.push(match);
}
return result;
}
_renderMatches(matches, findController) {
if (matches.length === 0) {
return;
}
const {
pageIdx
} = this;
const {
textContentItemsStr,
textDivs
} = this;
let prevEnd = null;
const infinity = {
divIdx: -1,
offset: undefined
};
function beginText(begin, className) {
const divIdx = begin.divIdx;
textDivs[divIdx].textContent = "";
return appendTextToDiv(divIdx, 0, begin.offset, className);
}
function appendTextToDiv(divIdx, fromOffset, toOffset, className) {
let div = textDivs[divIdx];
if (div.nodeType === Node.TEXT_NODE) {
const span = document.createElement("span");
div.before(span);
span.append(div);
textDivs[divIdx] = span;
div = span;
}
const content = textContentItemsStr[divIdx].substring(fromOffset, toOffset);
const node = document.createTextNode(content);
if (className) {
const span = document.createElement("span");
span.className = `${className} appended`;
span.append(node);
div.append(span);
if (className.includes("selected")) {
const {
left
} = span.getClientRects()[0];
const parentLeft = div.getBoundingClientRect().left;
return left - parentLeft;
}
return 0;
}
div.append(node);
return 0;
}
let lastDivIdx = -1;
let lastOffset = -1;
for (let i = 0; i < matches.length; i++) {
const match = matches[i];
if (!match.selected && !match.highlightAll) {
continue;
}
const begin = match.begin;
if (begin.divIdx === lastDivIdx && begin.offset === lastOffset) {
continue;
}
lastDivIdx = begin.divIdx;
lastOffset = begin.offset;
const end = match.end;
const highlightSuffix = match.selected ? " selected" : "";
let selectedLeft = 0;
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
if (prevEnd !== null) {
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
}
beginText(begin);
} else {
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, begin.offset);
}
if (begin.divIdx === end.divIdx) {
selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, end.offset, match.cssClass + highlightSuffix);
} else {
selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, match.cssClass + " begin" + highlightSuffix);
for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) {
textDivs[n0].className = match.cssClass + " middle" + highlightSuffix;
}
beginText(end, match.cssClass + " end" + highlightSuffix);
}
prevEnd = end;
if (match.selected) {
findController.scrollMatchIntoView({
element: textDivs[begin.divIdx],
selectedLeft,
pageIndex: pageIdx,
matchIndex: i
});
}
if (this.textDivs.length > 0) {
const textLayer = this.textDivs[0].closest(".textLayer");
const highlights = textLayer.querySelectorAll(`.${match.cssClass}`);
this.eventBus.dispatch("renderedtextlayerhighlights", {
pageIndex: pageIdx,
highlights
});
}
}
if (prevEnd) {
appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset);
}
}
_updateMatches(reset = false) {
if (!this.enabled && !reset) {
return;
}
const {
findController,
customFindController,
matches,
pageIdx
} = this;
const {
textContentItemsStr,
textDivs
} = this;
let clearedUntilDivIdx = -1;
for (const match of matches) {
const begin = Math.max(clearedUntilDivIdx, match.begin.divIdx);
for (let n = begin, end = match.end.divIdx; n <= end; n++) {
const div = textDivs[n];
div.textContent = textContentItemsStr[n];
div.className = "";
}
clearedUntilDivIdx = match.end.divIdx + 1;
}
if (reset) {
return;
}
const customPageMatches = customFindController.pageMatches[pageIdx] || null;
const customPageMatchesLength = customFindController.pageMatchesLength[pageIdx] || null;
const pageMatches = findController.pageMatches[pageIdx] || null;
const customMatches = this._convertMatches(customPageMatches, customPageMatchesLength, "customHighlight", customFindController.state?.highlightAll, pageIdx === customFindController.selected.pageIdx, customFindController.selected.matchIdx);
this.matches = [...customMatches];
if (!findController?.highlightMatches || reset) {
if (!reset) {
this._renderMatches(this.matches, this.customFindController);
}
}
const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null;
const convertedMatches = this._convertMatches(pageMatches, pageMatchesLength, "highlight", findController.state?.highlightAll, pageIdx === findController.selected.pageIdx, findController.selected.matchIdx);
this.matches.push(...convertedMatches);
this.matches.sort((a, b) => {
const cmp = a.begin.divIdx - b.begin.divIdx;
return cmp === 0 ? a.begin.offset - b.begin.offset : cmp;
});
this._renderMatches(this.matches, this.findController);
}
}
;// ./web/text_layer_builder.js
class TextLayerBuilder {
#enablePermissions = false;
#onAppend = null;
#renderingDone = false;
#textLayer = null;
static #textLayers = new Map();
static #selectionChangeAbortController = null;
constructor({
pdfPage,
highlighter = null,
accessibilityManager = null,
enablePermissions = false,
onAppend = null
}) {
this.pdfPage = pdfPage;
this.highlighter = highlighter;
this.accessibilityManager = accessibilityManager;
this.#enablePermissions = enablePermissions === true;
this.#onAppend = onAppend;
this.div = document.createElement("div");
this.div.tabIndex = 0;
this.div.className = "textLayer";
}
async render(viewport, textContentParams = null) {
if (this.#renderingDone && this.#textLayer) {
this.#textLayer.update({
viewport,
onBefore: this.hide.bind(this)
});
this.show();
return;
}
this.cancel();
this.#textLayer = new pdf.TextLayer({
textContentSource: this.pdfPage.streamTextContent(textContentParams || {
includeMarkedContent: true,
disableNormalization: true
}),
container: this.div,
viewport
});
const {
textDivs,
textContentItemsStr
} = this.#textLayer;
this.highlighter?.setTextMapping(textDivs, textContentItemsStr);
this.accessibilityManager?.setTextMapping(textDivs);
await this.#textLayer.render();
this.#renderingDone = true;
const endOfContent = document.createElement("div");
endOfContent.className = "endOfContent";
this.div.append(endOfContent);
this.#bindMouse(endOfContent);
this.#onAppend?.(this.div);
this.highlighter?.enable();
this.accessibilityManager?.enable();
}
hide() {
if (!this.div.hidden && this.#renderingDone) {
this.highlighter?.disable();
this.div.hidden = true;
}
}
show() {
if (this.div.hidden && this.#renderingDone) {
this.div.hidden = false;
this.highlighter?.enable();
}
}
cancel() {
this.#textLayer?.cancel();
this.#textLayer = null;
this.highlighter?.disable();
this.accessibilityManager?.disable();
TextLayerBuilder.#removeGlobalSelectionListener(this.div);
}
#bindMouse(end) {
const {
div
} = this;
div.addEventListener("mousedown", () => {
div.classList.add("selecting");
});
div.addEventListener("copy", event => {
if (!this.#enablePermissions) {
const selection = document.getSelection();
event.clipboardData.setData("text/plain", removeNullCharacters((0,util.normalizeUnicode)(selection.toString())));
}
(0,display_utils.stopEvent)(event);
});
TextLayerBuilder.#textLayers.set(div, end);
TextLayerBuilder.#enableGlobalSelectionListener();
}
static #removeGlobalSelectionListener(textLayerDiv) {
this.#textLayers.delete(textLayerDiv);
if (this.#textLayers.size === 0) {
this.#selectionChangeAbortController?.abort();
this.#selectionChangeAbortController = null;
}
}
static #enableGlobalSelectionListener() {
if (this.#selectionChangeAbortController) {
return;
}
this.#selectionChangeAbortController = new AbortController();
const {
signal
} = this.#selectionChangeAbortController;
const reset = (end, textLayer) => {
textLayer.append(end);
end.style.width = "";
end.style.height = "";
textLayer.classList.remove("selecting");
};
let isPointerDown = false;
document.addEventListener("pointerdown", () => {
isPointerDown = true;
}, {
signal
});
document.addEventListener("pointerup", () => {
isPointerDown = false;
this.#textLayers.forEach(reset);
}, {
signal
});
window.addEventListener("blur", () => {
isPointerDown = false;
this.#textLayers.forEach(reset);
}, {
signal
});
document.addEventListener("keyup", () => {
if (!isPointerDown) {
this.#textLayers.forEach(reset);
}
}, {
signal
});
var isFirefox, prevRange;
document.addEventListener("selectionchange", () => {
const selection = document.getSelection();
if (selection.rangeCount === 0) {
this.#textLayers.forEach(reset);
return;
}
if (this.#textLayers.size === 0) {
return;
}
const activeTextLayers = new Set();
for (let i = 0; i < selection.rangeCount; i++) {
const range = selection.getRangeAt(i);
for (const textLayerDiv of this.#textLayers.keys()) {
if (!activeTextLayers.has(textLayerDiv) && range.intersectsNode(textLayerDiv)) {
activeTextLayers.add(textLayerDiv);
}
}
}
for (const [textLayerDiv, endDiv] of this.#textLayers) {
if (activeTextLayers.has(textLayerDiv)) {
textLayerDiv.classList.add("selecting");
} else {
reset(endDiv, textLayerDiv);
}
}
isFirefox ??= getComputedStyle(this.#textLayers.values().next().value).getPropertyValue("-moz-user-select") === "none";
if (isFirefox) {
return;
}
const range = selection.getRangeAt(0);
const modifyStart = prevRange && (range.compareBoundaryPoints(Range.END_TO_END, prevRange) === 0 || range.compareBoundaryPoints(Range.START_TO_END, prevRange) === 0);
let anchor = modifyStart ? range.startContainer : range.endContainer;
if (anchor.nodeType === Node.TEXT_NODE) {
anchor = anchor.parentNode;
}
const parentTextLayer = anchor.parentElement?.closest(".textLayer");
const endDiv = this.#textLayers.get(parentTextLayer);
if (endDiv) {
endDiv.style.width = parentTextLayer.style.width;
endDiv.style.height = parentTextLayer.style.height;
anchor.parentElement.insertBefore(endDiv, modifyStart ? anchor : anchor.nextSibling);
}
prevRange = range.cloneRange();
}, {
signal
});
}
}
;// ./web/pdf_page_view.js
const DEFAULT_LAYER_PROPERTIES = null;
const LAYERS_ORDER = new Map([["canvasWrapper", 0], ["textLayer", 1], ["annotationLayer", 2], ["annotationEditorLayer", 3], ["xfaLayer", 3]]);
class PDFPageView {
#annotationMode = util.AnnotationMode.ENABLE_FORMS;
#canvasWrapper = null;
#enableHWA = false;
#hasRestrictedScaling = false;
#isEditing = false;
#layerProperties = null;
#loadingId = null;
#originalViewport = null;
#previousRotation = null;
#scaleRoundX = 1;
#scaleRoundY = 1;
#renderError = null;
#renderingState = RenderingStates.INITIAL;
#textLayerMode = TextLayerMode.ENABLE;
#useThumbnailCanvas = {
directDrawing: true,
initialOptionalContent: true,
regularAnnotations: true
};
#layers = [null, null, null, null];
constructor(options) {
const container = options.container;
const defaultViewport = options.defaultViewport;
this.id = options.id;
this.renderingId = "page" + this.id;
this.#layerProperties = options.layerProperties || DEFAULT_LAYER_PROPERTIES;
this.pdfPage = null;
this.pageLabel = null;
this.rotation = 0;
this.scale = options.scale || DEFAULT_SCALE;
this.viewport = defaultViewport;
this.pdfPageRotate = defaultViewport.rotation;
this._optionalContentConfigPromise = options.optionalContentConfigPromise || null;
this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this.#annotationMode = options.annotationMode ?? util.AnnotationMode.ENABLE_FORMS;
this.imageResourcesPath = options.imageResourcesPath || "";
this.maxCanvasPixels = options.maxCanvasPixels ?? AppOptions.get("maxCanvasPixels");
this.pageColors = options.pageColors || null;
this.#enableHWA = options.enableHWA || false;
this.eventBus = options.eventBus;
this.renderingQueue = options.renderingQueue;
this.l10n = options.l10n;
this.l10n ||= new genericl10n_GenericL10n();
this.renderTask = null;
this.resume = null;
this._isStandalone = !this.renderingQueue?.hasViewer();
this._container = container;
this._annotationCanvasMap = null;
this.annotationLayer = null;
this.annotationEditorLayer = null;
this.textLayer = null;
this.xfaLayer = null;
this.structTreeLayer = null;
this.drawLayer = null;
const div = document.createElement("div");
div.className = "page";
div.setAttribute("data-page-number", this.id);
div.setAttribute("role", "region");
div.setAttribute("data-l10n-id", "pdfjs-page-landmark");
div.setAttribute("data-l10n-args", JSON.stringify({
page: this.id
}));
this.div = div;
this.#setDimensions();
container?.append(div);
if (this._isStandalone) {
container?.style.setProperty("--scale-factor", this.scale * pdf.PixelsPerInch.PDF_TO_CSS_UNITS);
if (this.pageColors?.background) {
container?.style.setProperty("--page-bg-color", this.pageColors.background);
}
const {
optionalContentConfigPromise
} = options;
if (optionalContentConfigPromise) {
optionalContentConfigPromise.then(optionalContentConfig => {
if (optionalContentConfigPromise !== this._optionalContentConfigPromise) {
return;
}
this.#useThumbnailCanvas.initialOptionalContent = optionalContentConfig.hasInitialVisibility;
});
}
if (!options.l10n) {
this.l10n.translate(this.div);
}
}
}
#addLayer(div, name) {
const pos = LAYERS_ORDER.get(name);
const oldDiv = this.#layers[pos];
this.#layers[pos] = div;
if (oldDiv) {
oldDiv.replaceWith(div);
return;
}
for (let i = pos - 1; i >= 0; i--) {
const layer = this.#layers[i];
if (layer) {
layer.after(div);
return;
}
}
this.div.prepend(div);
}
get renderingState() {
return this.#renderingState;
}
set renderingState(state) {
if (state === this.#renderingState) {
return;
}
this.#renderingState = state;
if (this.#loadingId) {
clearTimeout(this.#loadingId);
this.#loadingId = null;
}
switch (state) {
case RenderingStates.PAUSED:
this.div.classList.remove("loading");
break;
case RenderingStates.RUNNING:
this.div.classList.add("loadingIcon");
this.#loadingId = setTimeout(() => {
this.div.classList.add("loading");
this.#loadingId = null;
}, 0);
break;
case RenderingStates.INITIAL:
case RenderingStates.FINISHED:
this.div.classList.remove("loadingIcon", "loading");
break;
}
}
#setDimensions() {
const {
viewport
} = this;
if (this.pdfPage) {
if (this.#previousRotation === viewport.rotation) {
return;
}
this.#previousRotation = viewport.rotation;
}
(0,pdf.setLayerDimensions)(this.div, viewport, true, false);
}
setPdfPage(pdfPage) {
if (this._isStandalone && (this.pageColors?.foreground === "CanvasText" || this.pageColors?.background === "Canvas")) {
this._container?.style.setProperty("--hcm-highlight-filter", pdfPage.filterFactory.addHighlightHCMFilter("highlight", "CanvasText", "Canvas", "HighlightText", "Highlight"));
this._container?.style.setProperty("--hcm-highlight-selected-filter", pdfPage.filterFactory.addHighlightHCMFilter("highlight_selected", "CanvasText", "Canvas", "HighlightText", "Highlight"));
}
this.pdfPage = pdfPage;
this.pdfPageRotate = pdfPage.rotate;
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = pdfPage.getViewport({
scale: this.scale * pdf.PixelsPerInch.PDF_TO_CSS_UNITS,
rotation: totalRotation
});
this.#setDimensions();
this.reset();
}
destroy() {
this.reset();
this.pdfPage?.cleanup();
}
hasEditableAnnotations() {
return !!this.annotationLayer?.hasEditableAnnotations();
}
get _textHighlighter() {
return (0,util.shadow)(this, "_textHighlighter", new TextHighlighter({
pageIndex: this.id - 1,
eventBus: this.eventBus,
findController: this.#layerProperties.findController,
customFindController: this.#layerProperties.customFindController
}));
}
#dispatchLayerRendered(name, error, layer) {
this.eventBus.dispatch(name, {
source: this,
pageNumber: this.id,
error,
layer
});
}
async #renderAnnotationLayer() {
let error = null;
try {
await this.annotationLayer.render(this.viewport, {
structTreeLayer: this.structTreeLayer
}, "display");
} catch (ex) {
console.error("#renderAnnotationLayer:", ex);
error = ex;
} finally {
this.#dispatchLayerRendered("annotationlayerrendered", error, this.annotationLayer);
}
}
async #renderAnnotationEditorLayer() {
let error = null;
try {
await this.annotationEditorLayer.render(this.viewport, "display");
} catch (ex) {
console.error("#renderAnnotationEditorLayer:", ex);
error = ex;
} finally {
this.#dispatchLayerRendered("annotationeditorlayerrendered", error, this.annotationEditorLayer);
}
}
async #renderDrawLayer() {
try {
await this.drawLayer.render("display");
} catch (ex) {
console.error("#renderDrawLayer:", ex);
}
}
async #renderXfaLayer() {
let error = null;
try {
const result = await this.xfaLayer.render(this.viewport, "display");
if (result?.textDivs && this._textHighlighter) {
this.#buildXfaTextContentItems(result.textDivs);
}
} catch (ex) {
console.error("#renderXfaLayer:", ex);
error = ex;
} finally {
if (this.xfaLayer?.div) {
this.l10n.pause();
this.#addLayer(this.xfaLayer.div, "xfaLayer");
this.l10n.resume();
}
this.#dispatchLayerRendered("xfalayerrendered", error, this.xfaLayer);
}
}
async #renderTextLayer() {
if (!this.textLayer) {
return;
}
let error = null;
try {
await this.textLayer.render(this.viewport);
} catch (ex) {
if (ex instanceof util.AbortException) {
return;
}
console.error("#renderTextLayer:", ex);
error = ex;
}
this.#dispatchLayerRendered("textlayerrendered", error, this.textLayer);
this.#renderStructTreeLayer();
}
async #renderStructTreeLayer() {
if (!this.textLayer) {
return;
}
const treeDom = await this.structTreeLayer?.render();
if (treeDom) {
this.l10n.pause();
this.structTreeLayer?.addElementsToTextLayer();
if (this.canvas && treeDom.parentNode !== this.canvas) {
this.canvas.append(treeDom);
}
this.l10n.resume();
}
this.structTreeLayer?.show();
}
async #buildXfaTextContentItems(textDivs) {
const text = await this.pdfPage.getTextContent();
const items = [];
for (const item of text.items) {
items.push(item.str);
}
this._textHighlighter.setTextMapping(textDivs, items);
this._textHighlighter.enable();
}
#resetCanvas() {
const {
canvas
} = this;
if (!canvas) {
return;
}
canvas.remove();
canvas.width = canvas.height = 0;
this.canvas = null;
this.#originalViewport = null;
}
reset({
keepAnnotationLayer = false,
keepAnnotationEditorLayer = false,
keepXfaLayer = false,
keepTextLayer = false,
keepCanvasWrapper = false
} = {}) {
this.cancelRendering({
keepAnnotationLayer,
keepAnnotationEditorLayer,
keepXfaLayer,
keepTextLayer
});
this.renderingState = RenderingStates.INITIAL;
const div = this.div;
const childNodes = div.childNodes,
annotationLayerNode = keepAnnotationLayer && this.annotationLayer?.div || null,
annotationEditorLayerNode = keepAnnotationEditorLayer && this.annotationEditorLayer?.div || null,
xfaLayerNode = keepXfaLayer && this.xfaLayer?.div || null,
textLayerNode = keepTextLayer && this.textLayer?.div || null,
canvasWrapperNode = keepCanvasWrapper && this.#canvasWrapper || null;
for (let i = childNodes.length - 1; i >= 0; i--) {
const node = childNodes[i];
switch (node) {
case annotationLayerNode:
case annotationEditorLayerNode:
case xfaLayerNode:
case textLayerNode:
case canvasWrapperNode:
continue;
}
node.remove();
const layerIndex = this.#layers.indexOf(node);
if (layerIndex >= 0) {
this.#layers[layerIndex] = null;
}
}
div.removeAttribute("data-loaded");
if (annotationLayerNode) {
this.annotationLayer.hide();
}
if (annotationEditorLayerNode) {
this.annotationEditorLayer.hide();
}
if (xfaLayerNode) {
this.xfaLayer.hide();
}
if (textLayerNode) {
this.textLayer.hide();
}
this.structTreeLayer?.hide();
if (!keepCanvasWrapper && this.#canvasWrapper) {
this.#canvasWrapper = null;
this.#resetCanvas();
}
}
toggleEditingMode(isEditing) {
if (!this.hasEditableAnnotations()) {
return;
}
this.#isEditing = isEditing;
this.reset({
keepAnnotationLayer: true,
keepAnnotationEditorLayer: true,
keepXfaLayer: true,
keepTextLayer: true,
keepCanvasWrapper: true
});
}
update({
scale = 0,
rotation = null,
optionalContentConfigPromise = null,
drawingDelay = -1
}) {
this.scale = scale || this.scale;
if (typeof rotation === "number") {
this.rotation = rotation;
}
if (optionalContentConfigPromise instanceof Promise) {
this._optionalContentConfigPromise = optionalContentConfigPromise;
optionalContentConfigPromise.then(optionalContentConfig => {
if (optionalContentConfigPromise !== this._optionalContentConfigPromise) {
return;
}
this.#useThumbnailCanvas.initialOptionalContent = optionalContentConfig.hasInitialVisibility;
});
}
this.#useThumbnailCanvas.directDrawing = true;
const totalRotation = (this.rotation + this.pdfPageRotate) % 360;
this.viewport = this.viewport.clone({
scale: this.scale * pdf.PixelsPerInch.PDF_TO_CSS_UNITS,
rotation: totalRotation
});
this.#setDimensions();
if (this._isStandalone) {
this._container?.style.setProperty("--scale-factor", this.viewport.scale);
}
if (this.canvas) {
let onlyCssZoom = false;
if (this.#hasRestrictedScaling) {
if (this.maxCanvasPixels === 0) {
onlyCssZoom = true;
} else if (this.maxCanvasPixels > 0) {
const {
width,
height
} = this.viewport;
const {
sx,
sy
} = this.outputScale;
onlyCssZoom = (Math.floor(width) * sx | 0) * (Math.floor(height) * sy | 0) > this.maxCanvasPixels;
}
}
const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;
if (postponeDrawing || onlyCssZoom) {
if (postponeDrawing && !onlyCssZoom && this.renderingState !== RenderingStates.FINISHED) {
this.cancelRendering({
keepAnnotationLayer: true,
keepAnnotationEditorLayer: true,
keepXfaLayer: true,
keepTextLayer: true,
cancelExtraDelay: drawingDelay
});
this.renderingState = RenderingStates.FINISHED;
this.#useThumbnailCanvas.directDrawing = false;
}
this.cssTransform({
redrawAnnotationLayer: true,
redrawAnnotationEditorLayer: true,
redrawXfaLayer: true,
redrawTextLayer: !postponeDrawing,
hideTextLayer: postponeDrawing
});
if (postponeDrawing) {
return;
}
this.eventBus.dispatch("pagerendered", {
source: this,
pageNumber: this.id,
cssTransform: true,
timestamp: performance.now(),
error: this.#renderError
});
return;
}
}
this.cssTransform({});
this.reset({
keepAnnotationLayer: true,
keepAnnotationEditorLayer: true,
keepXfaLayer: true,
keepTextLayer: true,
keepCanvasWrapper: true
});
}
cancelRendering({
keepAnnotationLayer = false,
keepAnnotationEditorLayer = false,
keepXfaLayer = false,
keepTextLayer = false,
cancelExtraDelay = 0
} = {}) {
if (this.renderTask) {
this.renderTask.cancel(cancelExtraDelay);
this.renderTask = null;
}
this.resume = null;
if (this.textLayer && (!keepTextLayer || !this.textLayer.div)) {
this.textLayer.cancel();
this.textLayer = null;
}
if (this.annotationLayer && (!keepAnnotationLayer || !this.annotationLayer.div)) {
this.annotationLayer.cancel();
this.annotationLayer = null;
this._annotationCanvasMap = null;
}
if (this.structTreeLayer && !this.textLayer) {
this.structTreeLayer = null;
}
if (this.annotationEditorLayer && (!keepAnnotationEditorLayer || !this.annotationEditorLayer.div)) {
if (this.drawLayer) {
this.drawLayer.cancel();
this.drawLayer = null;
}
this.annotationEditorLayer.cancel();
this.annotationEditorLayer = null;
}
if (this.xfaLayer && (!keepXfaLayer || !this.xfaLayer.div)) {
this.xfaLayer.cancel();
this.xfaLayer = null;
this._textHighlighter?.disable();
}
}
cssTransform({
redrawAnnotationLayer = false,
redrawAnnotationEditorLayer = false,
redrawXfaLayer = false,
redrawTextLayer = false,
hideTextLayer = false
}) {
const {
canvas
} = this;
if (!canvas) {
return;
}
const originalViewport = this.#originalViewport;
if (this.viewport !== originalViewport) {
const relativeRotation = (360 + this.viewport.rotation - originalViewport.rotation) % 360;
if (relativeRotation === 90 || relativeRotation === 270) {
const {
width,
height
} = this.viewport;
const scaleX = height / width;
const scaleY = width / height;
canvas.style.transform = `rotate(${relativeRotation}deg) scale(${scaleX},${scaleY})`;
} else {
canvas.style.transform = relativeRotation === 0 ? "" : `rotate(${relativeRotation}deg)`;
}
}
if (redrawAnnotationLayer && this.annotationLayer) {
this.#renderAnnotationLayer();
}
if (redrawAnnotationEditorLayer && this.annotationEditorLayer) {
if (this.drawLayer) {
this.#renderDrawLayer();
}
this.#renderAnnotationEditorLayer();
}
if (redrawXfaLayer && this.xfaLayer) {
this.#renderXfaLayer();
}
if (this.textLayer) {
if (hideTextLayer) {
this.textLayer.hide();
this.structTreeLayer?.hide();
} else if (redrawTextLayer) {
this.#renderTextLayer();
}
}
}
get width() {
return this.viewport.width;
}
get height() {
return this.viewport.height;
}
getPagePoint(x, y) {
return this.viewport.convertToPdfPoint(x, y);
}
async #finishRenderTask(renderTask, error = null) {
if (renderTask === this.renderTask) {
this.renderTask = null;
}
if (error instanceof pdf.RenderingCancelledException) {
this.#renderError = null;
return;
}
this.#renderError = error;
this.renderingState = RenderingStates.FINISHED;
this.#useThumbnailCanvas.regularAnnotations = !renderTask.separateAnnots;
this.eventBus.dispatch("pagerendered", {
source: this,
pageNumber: this.id,
cssTransform: false,
timestamp: performance.now(),
error: this.#renderError
});
if (error) {
throw error;
}
}
async draw() {
if (this.renderingState !== RenderingStates.INITIAL) {
ngx_console.NgxConsole.error("Must be in new state before drawing");
this.reset();
}
const {
div,
l10n,
pageColors,
pdfPage,
viewport
} = this;
if (!pdfPage) {
this.renderingState = RenderingStates.FINISHED;
throw new Error("pdfPage is not loaded");
}
this.renderingState = RenderingStates.RUNNING;
let canvasWrapper = this.#canvasWrapper;
if (!canvasWrapper) {
canvasWrapper = this.#canvasWrapper = document.createElement("div");
canvasWrapper.classList.add("canvasWrapper");
this.#addLayer(canvasWrapper, "canvasWrapper");
}
if (!this.textLayer && this.#textLayerMode !== TextLayerMode.DISABLE && !pdfPage.isPureXfa) {
this._accessibilityManager ||= new TextAccessibilityManager();
this.textLayer = new TextLayerBuilder({
pdfPage,
highlighter: this._textHighlighter,
accessibilityManager: this._accessibilityManager,
enablePermissions: this.#textLayerMode === TextLayerMode.ENABLE_PERMISSIONS,
onAppend: textLayerDiv => {
this.l10n.pause();
this.#addLayer(textLayerDiv, "textLayer");
this.l10n.resume();
}
});
}
if (!this.annotationLayer && this.#annotationMode !== util.AnnotationMode.DISABLE) {
const {
annotationStorage,
annotationEditorUIManager,
downloadManager,
enableScripting,
fieldObjectsPromise,
hasJSActionsPromise,
linkService
} = this.#layerProperties;
this._annotationCanvasMap ||= new Map();
this.annotationLayer = new AnnotationLayerBuilder({
pdfPage,
annotationStorage,
imageResourcesPath: this.imageResourcesPath,
renderForms: this.#annotationMode === util.AnnotationMode.ENABLE_FORMS,
linkService,
downloadManager,
enableScripting,
hasJSActionsPromise,
fieldObjectsPromise,
annotationCanvasMap: this._annotationCanvasMap,
accessibilityManager: this._accessibilityManager,
annotationEditorUIManager,
onAppend: annotationLayerDiv => {
this.#addLayer(annotationLayerDiv, "annotationLayer");
}
});
}
const renderContinueCallback = cont => {
showCanvas?.(false);
if (this.renderingQueue && !this.renderingQueue.isHighestPriority(this)) {
this.renderingState = RenderingStates.PAUSED;
this.resume = () => {
this.renderingState = RenderingStates.RUNNING;
cont();
};
return;
}
cont();
};
let {
width,
height
} = viewport;
const canvas = document.createElement("canvas");
canvas.setAttribute("role", "presentation");
const hasHCM = !!(pageColors?.background && pageColors?.foreground);
const prevCanvas = this.canvas;
const updateOnFirstShow = !prevCanvas && !hasHCM;
this.canvas = canvas;
this.#originalViewport = viewport;
let showCanvas = isLastShow => {
if (updateOnFirstShow) {
canvasWrapper.prepend(canvas);
showCanvas = null;
return;
}
if (!isLastShow) {
return;
}
if (prevCanvas) {
prevCanvas.replaceWith(canvas);
prevCanvas.width = prevCanvas.height = 0;
} else {
canvasWrapper.prepend(canvas);
}
showCanvas = null;
};
const ctx = canvas.getContext("2d", {
alpha: false,
willReadFrequently: !this.#enableHWA
});
const outputScale = this.outputScale = new pdf.OutputScale();
if (this.maxCanvasPixels === 0) {
const invScale = 1 / this.scale;
outputScale.sx *= invScale;
outputScale.sy *= invScale;
this.#hasRestrictedScaling = true;
} else if (this.maxCanvasPixels > 0) {
const pixelsInViewport = width * height;
const maxScale = Math.sqrt(this.maxCanvasPixels / pixelsInViewport);
if (outputScale.sx > maxScale || outputScale.sy > maxScale) {
outputScale.sx = maxScale;
outputScale.sy = maxScale;
this.#hasRestrictedScaling = true;
} else {
this.#hasRestrictedScaling = false;
}
}
const sfx = approximateFraction(outputScale.sx);
const sfy = approximateFraction(outputScale.sy);
let canvasWidth = floorToDivide(width * outputScale.sx, sfx[0]);
let canvasHeight = floorToDivide(height * outputScale.sy, sfy[0]);
const divisor = await MaxCanvasSize.reduceToMaxCanvasSize(canvasWidth, canvasHeight);
if (divisor > 1) {
canvasWidth /= divisor;
canvasWidth *= 0.95;
canvasHeight /= divisor;
canvasHeight *= 0.95;
const reduction = Math.round((divisor / 0.95 - 1) * 100);
(0,util.warn)(`Page ${this.id}: Reduced the maximum resolution by ${reduction}% because the browser can't render larger canvases.`);
}
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const pageWidth = floorToDivide(calcRound(width), sfx[1]);
const pageHeight = floorToDivide(calcRound(height), sfy[1]);
outputScale.sx = canvasWidth / pageWidth;
outputScale.sy = canvasHeight / pageHeight;
if (this.#scaleRoundX !== sfx[1]) {
div.style.setProperty("--scale-round-x", `${sfx[1]}px`);
this.#scaleRoundX = sfx[1];
}
if (this.#scaleRoundY !== sfy[1]) {
div.style.setProperty("--scale-round-y", `${sfy[1]}px`);
this.#scaleRoundY = sfy[1];
}
const transform = outputScale.scaled ? [outputScale.sx, 0, 0, outputScale.sy, 0, 0] : null;
const renderContext = {
canvasContext: ctx,
transform,
viewport,
annotationMode: this.#annotationMode,
optionalContentConfigPromise: this._optionalContentConfigPromise,
annotationCanvasMap: this._annotationCanvasMap,
pageColors,
isEditing: this.#isEditing
};
const renderTask = this.renderTask = pdfPage.render(renderContext);
renderTask.onContinue = renderContinueCallback;
const resultPromise = renderTask.promise.then(async () => {
showCanvas?.(true);
await this.#finishRenderTask(renderTask);
this.structTreeLayer ||= new StructTreeLayerBuilder(pdfPage, viewport.rawDims);
this.#renderTextLayer();
if (this.annotationLayer) {
await this.#renderAnnotationLayer();
}
const {
annotationEditorUIManager
} = this.#layerProperties;
if (!annotationEditorUIManager) {
return;
}
this.drawLayer ||= new DrawLayerBuilder({
pageIndex: this.id
});
await this.#renderDrawLayer();
this.drawLayer.setParent(canvasWrapper);
this.annotationEditorLayer ||= new AnnotationEditorLayerBuilder({
uiManager: annotationEditorUIManager,
pdfPage,
l10n,
structTreeLayer: this.structTreeLayer,
accessibilityManager: this._accessibilityManager,
annotationLayer: this.annotationLayer?.annotationLayer,
textLayer: this.textLayer,
drawLayer: this.drawLayer.getDrawLayer(),
onAppend: annotationEditorLayerDiv => {
this.#addLayer(annotationEditorLayerDiv, "annotationEditorLayer");
},
eventBus: this.eventBus
});
this.#renderAnnotationEditorLayer();
}, error => {
if (!(error instanceof pdf.RenderingCancelledException)) {
showCanvas?.(true);
} else {
prevCanvas?.remove();
this.#resetCanvas();
}
return this.#finishRenderTask(renderTask, error);
});
if (pdfPage.isPureXfa) {
if (!this.xfaLayer) {
const {
annotationStorage,
linkService
} = this.#layerProperties;
this.xfaLayer = new XfaLayerBuilder({
pdfPage,
annotationStorage,
linkService
});
}
this.#renderXfaLayer();
}
div.setAttribute("data-loaded", true);
this.eventBus.dispatch("pagerender", {
source: this,
pageNumber: this.id
});
return resultPromise;
}
setPageLabel(label) {
this.pageLabel = typeof label === "string" ? label : null;
this.div.setAttribute("data-l10n-args", JSON.stringify({
page: this.pageLabel ?? this.id
}));
if (this.pageLabel !== null) {
this.div.setAttribute("data-page-label", this.pageLabel);
} else {
this.div.removeAttribute("data-page-label");
}
}
get thumbnailCanvas() {
const {
directDrawing,
initialOptionalContent,
regularAnnotations
} = this.#useThumbnailCanvas;
return directDrawing && initialOptionalContent && regularAnnotations ? this.canvas : null;
}
}
;// ./web/pdf_viewer.js
const DEFAULT_CACHE_SIZE = 10;
const PagesCountLimit = {
FORCE_SCROLL_MODE_PAGE: 10000,
FORCE_LAZY_PAGE_INIT: 5000,
PAUSE_EAGER_PAGE_INIT: 250
};
function isValidAnnotationEditorMode(mode) {
return Object.values(util.AnnotationEditorType).includes(mode) && mode !== util.AnnotationEditorType.DISABLE;
}
class PDFPageViewBuffer {
#buf = new Set();
#size = 0;
constructor(size) {
this.#size = size;
}
push(view) {
const buf = this.#buf;
if (buf.has(view)) {
buf.delete(view);
}
buf.add(view);
if (buf.size > this.#size) {
this.#destroyFirstView();
}
}
resize(newSize, idsToKeep = null) {
this.#size = newSize;
const buf = this.#buf;
if (idsToKeep) {
const ii = buf.size;
let i = 1;
for (const view of buf) {
if (idsToKeep.has(view.id)) {
buf.delete(view);
buf.add(view);
}
if (++i > ii) {
break;
}
}
}
while (buf.size > this.#size) {
this.#destroyFirstView();
}
}
has(view) {
return this.#buf.has(view);
}
[Symbol.iterator]() {
return this.#buf.keys();
}
#destroyFirstView() {
const firstView = this.#buf.keys().next().value;
firstView?.destroy();
this.#buf.delete(firstView);
}
}
class PDFViewer {
#buffer = null;
#altTextManager = null;
#annotationEditorHighlightColors = null;
#annotationEditorMode = util.AnnotationEditorType.NONE;
#annotationEditorUIManager = null;
#annotationMode = util.AnnotationMode.ENABLE_FORMS;
#containerTopLeft = null;
#editorUndoBar = null;
#enableHWA = false;
#enableHighlightFloatingButton = false;
#enablePermissions = false;
#enableUpdatedAddImage = false;
#enableNewAltTextWhenAddingImage = false;
#eventAbortController = null;
#mlManager = null;
#switchAnnotationEditorModeAC = null;
#switchAnnotationEditorModeTimeoutId = null;
#getAllTextInProgress = false;
#hiddenCopyElement = null;
#interruptCopyCondition = false;
#previousContainerHeight = 0;
#resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this));
#scrollModePageState = null;
#scaleTimeoutId = null;
#supportsPinchToZoom = true;
#textLayerMode = TextLayerMode.ENABLE;
#outerScrollContainer = undefined;
#pageViewMode = "multiple";
#maxZoom = MAX_SCALE;
#minZoom = MIN_SCALE;
constructor(options) {
const viewerVersion = "4.10.708";
if (pdf.version !== viewerVersion) {
throw new Error(`The API version "${pdf.version}" does not match the Viewer version "${viewerVersion}".`);
}
this.container = options.container;
this.viewer = options.viewer || options.container.firstElementChild;
this.eventBus = options.eventBus;
this.pageViewMode = options.pageViewMode || "multiple";
this.defaultCacheSize = options.defaultCacheSize;
this.#maxZoom = options.maxZoom;
this.#minZoom = options.minZoom;
if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") {
throw new Error("Invalid `container` and/or `viewer` option.");
}
if (this.container.offsetParent && getComputedStyle(this.container).position !== "absolute") {
throw new Error("The `container` must be absolutely positioned.");
}
this.#resizeObserver.observe(this.container);
this.linkService = options.linkService || new SimpleLinkService();
this.downloadManager = options.downloadManager || null;
this.findController = options.findController || null;
this.customFindController = options.customFindController || null;
this.#altTextManager = options.altTextManager || null;
this.#editorUndoBar = options.editorUndoBar || null;
if (this.findController) {
this.findController.onIsPageVisible = pageNumber => this._getVisiblePages().ids.has(pageNumber);
}
this._scriptingManager = options.scriptingManager || null;
this.#textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
this.#annotationMode = options.annotationMode ?? util.AnnotationMode.ENABLE_FORMS;
this.#annotationEditorMode = options.annotationEditorMode ?? util.AnnotationEditorType.NONE;
this.#annotationEditorHighlightColors = options.annotationEditorHighlightColors || null;
this.#enableHighlightFloatingButton = options.enableHighlightFloatingButton === true;
this.#enableUpdatedAddImage = options.enableUpdatedAddImage === true;
this.#enableNewAltTextWhenAddingImage = options.enableNewAltTextWhenAddingImage === true;
this.imageResourcesPath = options.imageResourcesPath || "";
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
this.removePageBorders = options.removePageBorders || false;
this.maxCanvasPixels = options.maxCanvasPixels;
this.l10n = options.l10n;
this.l10n ||= new genericl10n_GenericL10n();
this.#enablePermissions = options.enablePermissions || false;
this.pageColors = options.pageColors || null;
this.#mlManager = options.mlManager || null;
this.#enableHWA = options.enableHWA || false;
this.#supportsPinchToZoom = options.supportsPinchToZoom !== false;
this.defaultRenderingQueue = !options.renderingQueue;
if (this.defaultRenderingQueue) {
this.renderingQueue = new PDFRenderingQueue();
this.renderingQueue.setViewer(this);
} else {
this.renderingQueue = options.renderingQueue;
}
const {
abortSignal
} = options;
abortSignal?.addEventListener("abort", () => {
this.#resizeObserver.disconnect();
this.#resizeObserver = null;
}, {
once: true
});
this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this), abortSignal);
this.presentationModeState = PresentationModeState.UNKNOWN;
this._resetView();
if (this.removePageBorders) {
this.viewer.classList.add("removePageBorders");
}
this.#updateContainerHeightCss();
this.eventBus._on("thumbnailrendered", ({
pageNumber,
pdfPage
}) => {
const pageView = this._pages[pageNumber - 1];
if (!this.#buffer.has(pageView)) {
pdfPage?.cleanup();
}
});
if (!options.l10n) {
this.l10n.translate(this.container);
}
this.cspPolicyService = options.cspPolicyService;
}
get maxZoom() {
return this.#maxZoom;
}
set maxZoom(value) {
this.#maxZoom = value;
}
get minZoom() {
return this.#minZoom;
}
set minZoom(value) {
this.#minZoom = value;
}
setTextLayerMode(mode) {
this.#textLayerMode = mode;
}
get pageViewMode() {
return this.#pageViewMode;
}
set pageViewMode(viewMode) {
if (this.#pageViewMode !== viewMode) {
this.#pageViewMode = viewMode;
if (!this.#outerScrollContainer && viewMode === "infinite-scroll") {
this.#outerScrollContainer = this.#findParentWithScrollbar(this.container.offsetParent);
if (this.#outerScrollContainer) {
watchScroll(this.#outerScrollContainer, this._scrollUpdate.bind(this));
}
}
this.eventBus.dispatch("pageviewmodechanged", {
source: this,
pageViewMode: viewMode
});
}
}
#findParentWithScrollbar(element) {
while (element) {
if (element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth) {
return element;
}
element = element.parentElement;
}
return null;
}
get pagesCount() {
return this._pages.length;
}
getPageView(index) {
return this._pages[index];
}
getCachedPageViews() {
return new Set(this.#buffer);
}
get pageViewsReady() {
return this._pages.every(pageView => pageView?.pdfPage);
}
get renderForms() {
return this.#annotationMode === util.AnnotationMode.ENABLE_FORMS;
}
get enableScripting() {
return !!this._scriptingManager;
}
get currentPageNumber() {
return this._currentPageNumber;
}
set currentPageNumber(val) {
if (!Number.isInteger(val)) {
throw new Error("Invalid page number.");
}
if (!this.pdfDocument) {
return;
}
const flip = Math.abs(this._currentPageNumber - val) <= 2;
if (!this._setCurrentPageNumber(val, true)) {
ngx_console.NgxConsole.error(`currentPageNumber: "${val}" is not a valid page.`);
}
if (this.pageFlip) {
if (flip) {
this.pageFlip.flip(val - 1);
} else {
this.pageFlip.turnToPage(val - 1);
}
this.ensureAdjacentPagesAreLoaded();
}
}
hidePagesDependingOnpageViewMode() {
if (this.pageViewMode === "book") {
if (!this.pageFlip) {
setTimeout(() => {
if (!this.pageFlip) {
const page1 = this._pages[0].div;
const htmlParentElement = page1.parentElement;
const viewer = htmlParentElement.parentElement;
viewer.style.width = 2 * page1.clientWidth + "px";
viewer.style.overflow = "hidden";
viewer.style.marginLeft = "auto";
viewer.style.marginRight = "auto";
this.pageFlip = new PageFlip(htmlParentElement, {
width: page1.clientWidth,
height: page1.clientHeight,
showCover: true,
size: "fixed"
}, this.cspPolicyService);
this.pageFlip.loadFromHTML(this.container.querySelectorAll(".page"));
this.pageFlip.on("flip", e => {
if (this._currentPageNumber !== e.data + 1) {
this._setCurrentPageNumber(e.data + 1, false);
}
});
}
}, 100);
}
}
}
async _setCurrentPageNumber(val, resetCurrentPageView = false) {
if (this._currentPageNumber === val) {
if (resetCurrentPageView) {
this.#resetCurrentPageView();
}
return true;
}
if (!(0 < val && val <= this.pagesCount)) {
return false;
}
const previous = this._currentPageNumber;
this._currentPageNumber = val;
this.hidePagesDependingOnpageViewMode();
if (this.pageViewMode === "book" || this.pageViewMode === "infinite-scroll") {
const pageView = this._pages[this.currentPageNumber - 1];
if (pageView.div.parentElement.classList.contains("spread")) {
pageView.div.parentElement.childNodes.forEach(async div => {
const pageNumber = Number(div.getAttribute("data-page-number"));
const pv = this._pages[pageNumber - 1];
await this.#ensurePdfPageLoaded(pv);
this.renderingQueue.renderView(pv);
div.style.display = "inline-block";
});
} else {
await this.#ensurePdfPageLoaded(pageView);
this.renderingQueue.renderView(pageView);
if (this.#pageViewMode === "book") {
this.ensureAdjacentPagesAreLoaded();
}
}
}
this.eventBus.dispatch("pagechanging", {
source: this,
pageNumber: val,
pageLabel: this._pageLabels?.[val - 1] ?? null,
previous
});
if (resetCurrentPageView) {
this.#resetCurrentPageView();
}
return true;
}
addPageToRenderQueue(pageIndex = 0) {
if (pageIndex >= 0 && pageIndex <= this._pages.length - 1) {
const pageView = this._pages[pageIndex];
const isLoading = pageView.renderingState === RenderingStates.INITIAL;
if (isLoading) {
this.#ensurePdfPageLoaded(pageView).then(() => {
this.renderingQueue.renderView(pageView);
});
return true;
}
}
return false;
}
async ensureAdjacentPagesAreLoaded() {
const advances = [0, 1, -1, 2, -2, -3];
let offset = 0;
if (this.currentPageNumber % 2 === 1) {
offset = -1;
}
let renderAsynchronously = false;
for (const advance of advances) {
const pageIndex = this.currentPageNumber + advance + offset;
if (pageIndex >= 0 && pageIndex < this._pages.length) {
try {
const pageView = this._pages[pageIndex];
await this.#ensurePdfPageLoaded(pageView);
const isAlreadyRendering = this._pages.some(pv => pv.renderingState === RenderingStates.RUNNING || pv.renderingState === RenderingStates.PAUSED);
if (isAlreadyRendering || renderAsynchronously) {
const loader = () => this.adjacentPagesRenderer(loader, pageIndex);
this.eventBus._on("pagerendered", loader);
this.eventBus._on("thumbnailRendered", loader);
} else {
renderAsynchronously = this.adjacentPagesRenderer(null, pageIndex);
}
} catch (exception) {
ngx_console.NgxConsole.log("Exception during pre-rendering page %s", pageIndex, exception);
}
}
}
}
adjacentPagesRenderer(self, pageIndex) {
const isAlreadyRendering = this._pages.find(pageView => pageView.renderingState === RenderingStates.RUNNING);
if (isAlreadyRendering) {
return true;
}
const pausedRendering = this._pages.find(pageView => pageView.renderingState === RenderingStates.PAUSED);
if (pausedRendering) {
this.renderingQueue.renderView(pausedRendering);
return true;
}
if (self) {
this.eventBus._off("pagerendered", self);
this.eventBus._off("thumbnailRendered", self);
}
if (pageIndex >= 0 && pageIndex < this._pages.length) {
const pageView = this._pages[pageIndex];
const needsToBeRendered = pageView.renderingState === RenderingStates.INITIAL;
if (needsToBeRendered) {
this.renderingQueue.renderView(pageView);
return true;
}
}
return false;
}
get currentPageLabel() {
return this._pageLabels?.[this._currentPageNumber - 1] ?? null;
}
set currentPageLabel(val) {
if (!this.pdfDocument) {
return;
}
let page = val | 0;
if (this._pageLabels) {
const i = this._pageLabels.indexOf(val);
if (i >= 0) {
page = i + 1;
}
}
if (!this._setCurrentPageNumber(page, true)) {
ngx_console.NgxConsole.error(`currentPageLabel: "${val}" is not a valid page.`);
}
}
get currentScale() {
return this._currentScale !== UNKNOWN_SCALE ? this._currentScale : DEFAULT_SCALE;
}
set currentScale(val) {
if (isNaN(val)) {
throw new Error("Invalid numeric scale.");
}
if (!this.pdfDocument) {
return;
}
this.#setScale(val, {
noScroll: false
});
}
get currentScaleValue() {
return this._currentScaleValue;
}
set currentScaleValue(val) {
if (!this.pdfDocument) {
return;
}
this.#setScale(val, {
noScroll: false
});
}
get pagesRotation() {
return this._pagesRotation;
}
set pagesRotation(rotation) {
if (!isValidRotation(rotation)) {
throw new Error("Invalid pages rotation angle.");
}
if (!this.pdfDocument) {
return;
}
rotation %= 360;
if (rotation < 0) {
rotation += 360;
}
if (this._pagesRotation === rotation) {
return;
}
this._pagesRotation = rotation;
const pageNumber = this._currentPageNumber;
this.refresh(true, {
rotation
});
if (this._currentScaleValue) {
this.#setScale(this._currentScaleValue, {
noScroll: true
});
}
this.eventBus.dispatch("rotationchanging", {
source: this,
pagesRotation: rotation,
pageNumber
});
if (this.defaultRenderingQueue) {
this.update();
}
}
get firstPagePromise() {
return this.pdfDocument ? this._firstPageCapability.promise : null;
}
get onePageRendered() {
return this.pdfDocument ? this._onePageRenderedCapability.promise : null;
}
get pagesPromise() {
return this.pdfDocument ? this._pagesCapability.promise : null;
}
get _layerProperties() {
const self = this;
return (0,util.shadow)(this, "_layerProperties", {
get annotationEditorUIManager() {
return self.#annotationEditorUIManager;
},
get annotationStorage() {
return self.pdfDocument?.annotationStorage;
},
get downloadManager() {
return self.downloadManager;
},
get enableScripting() {
return !!self._scriptingManager;
},
get fieldObjectsPromise() {
return self.pdfDocument?.getFieldObjects();
},
get findController() {
return self.findController;
},
get customFindController() {
return self.customFindController;
},
get hasJSActionsPromise() {
return self.pdfDocument?.hasJSActions();
},
get linkService() {
return self.linkService;
}
});
}
#initializePermissions(permissions) {
const params = {
annotationEditorMode: this.#annotationEditorMode,
annotationMode: this.#annotationMode,
textLayerMode: this.#textLayerMode
};
if (!permissions) {
return params;
}
if (!permissions.includes(util.PermissionFlag.COPY) && this.#textLayerMode === TextLayerMode.ENABLE) {
params.textLayerMode = TextLayerMode.ENABLE_PERMISSIONS;
}
if (!permissions.includes(util.PermissionFlag.MODIFY_CONTENTS)) {
params.annotationEditorMode = util.AnnotationEditorType.DISABLE;
}
if (!permissions.includes(util.PermissionFlag.MODIFY_ANNOTATIONS) && !permissions.includes(util.PermissionFlag.FILL_INTERACTIVE_FORMS) && this.#annotationMode === util.AnnotationMode.ENABLE_FORMS) {
params.annotationMode = util.AnnotationMode.ENABLE;
}
return params;
}
async #onePageRenderedOrForceFetch(signal) {
if (document.visibilityState === "hidden" || !this.container.offsetParent || this._getVisiblePages().views.length === 0) {
return;
}
const hiddenCapability = Promise.withResolvers(),
ac = new AbortController();
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
hiddenCapability.resolve();
}
}, {
signal: typeof AbortSignal.any === "function" ? AbortSignal.any([signal, ac.signal]) : signal
});
await Promise.race([this._onePageRenderedCapability.promise, hiddenCapability.promise]);
ac.abort();
}
async getAllText() {
const texts = [];
const buffer = [];
for (let pageNum = 1, pagesCount = this.pdfDocument.numPages; pageNum <= pagesCount; ++pageNum) {
if (this.#interruptCopyCondition) {
return null;
}
buffer.length = 0;
const page = await this.pdfDocument.getPage(pageNum);
const {
items
} = await page.getTextContent();
for (const item of items) {
if (item.str) {
buffer.push(item.str);
}
if (item.hasEOL) {
buffer.push("\n");
}
}
texts.push(removeNullCharacters(buffer.join("")));
}
return texts.join("\n");
}
#copyCallback(textLayerMode, event) {
const selection = document.getSelection();
const {
focusNode,
anchorNode
} = selection;
if (anchorNode && focusNode && selection.containsNode(this.#hiddenCopyElement)) {
if (this.#getAllTextInProgress || textLayerMode === TextLayerMode.ENABLE_PERMISSIONS) {
(0,display_utils.stopEvent)(event);
return;
}
this.#getAllTextInProgress = true;
const {
classList
} = this.viewer;
classList.add("copyAll");
const ac = new AbortController();
window.addEventListener("keydown", ev => this.#interruptCopyCondition = ev.key === "Escape", {
signal: ac.signal
});
this.getAllText().then(async text => {
if (text !== null) {
await navigator.clipboard.writeText(text);
}
}).catch(reason => {
ngx_console.NgxConsole.warn(`Something goes wrong when extracting the text: ${reason.message}`);
}).finally(() => {
this.#getAllTextInProgress = false;
this.#interruptCopyCondition = false;
ac.abort();
classList.remove("copyAll");
});
(0,display_utils.stopEvent)(event);
}
}
setDocument(pdfDocument) {
if (this.pdfDocument) {
this.eventBus.dispatch("pagesdestroy", {
source: this
});
this._cancelRendering();
this._resetView();
this.findController?.setDocument(null);
this.customFindController?.setDocument(null);
this._scriptingManager?.setDocument(null);
this.#annotationEditorUIManager?.destroy();
this.#annotationEditorUIManager = null;
}
this.pdfDocument = pdfDocument;
if (!pdfDocument) {
return;
}
const pagesCount = pdfDocument.numPages;
const firstPagePromise = pdfDocument.getPage(1);
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
intent: "display"
});
const permissionsPromise = this.#enablePermissions ? pdfDocument.getPermissions() : Promise.resolve();
const {
eventBus,
pageColors,
viewer
} = this;
this.#eventAbortController = new AbortController();
const {
signal
} = this.#eventAbortController;
if (pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
ngx_console.NgxConsole.warn("Forcing PAGE-scrolling for performance reasons, given the length of the document.");
const mode = this._scrollMode = ScrollMode.PAGE;
eventBus.dispatch("scrollmodechanged", {
source: this,
mode
});
}
this._pagesCapability.promise.then(() => {
eventBus.dispatch("pagesloaded", {
source: this,
pagesCount
});
}, () => {});
const onBeforeDraw = evt => {
const pageView = this._pages[evt.pageNumber - 1];
if (!pageView) {
return;
}
this.#buffer.push(pageView);
};
eventBus._on("pagerender", onBeforeDraw, {
signal
});
const onAfterDraw = evt => {
if (evt.cssTransform) {
return;
}
this._onePageRenderedCapability.resolve({
timestamp: evt.timestamp
});
eventBus._off("pagerendered", onAfterDraw);
};
eventBus._on("pagerendered", onAfterDraw, {
signal
});
Promise.all([firstPagePromise, permissionsPromise]).then(([firstPdfPage, permissions]) => {
if (pdfDocument !== this.pdfDocument) {
return;
}
this._firstPageCapability.resolve(firstPdfPage);
this._optionalContentConfigPromise = optionalContentConfigPromise;
const {
annotationEditorMode,
annotationMode,
textLayerMode
} = this.#initializePermissions(permissions);
if (textLayerMode !== TextLayerMode.DISABLE) {
const element = this.#hiddenCopyElement = document.createElement("div");
element.id = "hiddenCopyElement";
viewer.before(element);
}
if (typeof AbortSignal.any === "function" && annotationEditorMode !== util.AnnotationEditorType.DISABLE) {
const mode = annotationEditorMode;
if (pdfDocument.isPureXfa) {
ngx_console.NgxConsole.warn("Warning: XFA-editing is not implemented.");
} else if (isValidAnnotationEditorMode(mode)) {
this.#annotationEditorUIManager = new pdf.AnnotationEditorUIManager(this.container, viewer, this.#altTextManager, eventBus, pdfDocument, pageColors, this.#annotationEditorHighlightColors, this.#enableHighlightFloatingButton, this.#enableUpdatedAddImage, this.#enableNewAltTextWhenAddingImage, this.#mlManager, this.#editorUndoBar, this.#supportsPinchToZoom);
eventBus.dispatch("annotationeditoruimanager", {
source: this,
uiManager: this.#annotationEditorUIManager
});
if (mode !== util.AnnotationEditorType.NONE) {
if (mode === util.AnnotationEditorType.STAMP) {
this.#mlManager?.loadModel("altText");
}
this.#annotationEditorUIManager.updateMode(mode);
}
} else {
ngx_console.NgxConsole.error(`Invalid AnnotationEditor mode: ${mode}`);
}
}
const viewerElement = this._scrollMode === ScrollMode.PAGE ? null : viewer;
const scale = this.currentScale;
const viewport = firstPdfPage.getViewport({
scale: scale * pdf.PixelsPerInch.PDF_TO_CSS_UNITS
});
viewer.style.setProperty("--scale-factor", viewport.scale);
if (pageColors?.background) {
viewer.style.setProperty("--page-bg-color", pageColors.background);
}
if (pageColors?.foreground === "CanvasText" || pageColors?.background === "Canvas") {
viewer.style.setProperty("--hcm-highlight-filter", pdfDocument.filterFactory.addHighlightHCMFilter("highlight", "CanvasText", "Canvas", "HighlightText", "Highlight"));
viewer.style.setProperty("--hcm-highlight-selected-filter", pdfDocument.filterFactory.addHighlightHCMFilter("highlight_selected", "CanvasText", "Canvas", "HighlightText", "ButtonText"));
}
for (let pageNum = 1; pageNum <= pagesCount; ++pageNum) {
const pageView = new PDFPageView({
container: viewerElement,
eventBus,
id: pageNum,
scale,
defaultViewport: viewport.clone(),
optionalContentConfigPromise,
renderingQueue: this.renderingQueue,
textLayerMode,
annotationMode,
imageResourcesPath: this.imageResourcesPath,
maxCanvasPixels: this.maxCanvasPixels,
pageColors,
l10n: this.l10n,
layerProperties: this._layerProperties,
enableHWA: this.#enableHWA
});
this._pages.push(pageView);
}
this._pages[0]?.setPdfPage(firstPdfPage);
if (this._scrollMode === ScrollMode.PAGE) {
this.#ensurePageViewVisible();
} else if (this._spreadMode !== SpreadMode.NONE) {
this._updateSpreadMode();
}
this.#onePageRenderedOrForceFetch(signal).then(async () => {
if (pdfDocument !== this.pdfDocument) {
return;
}
this.findController?.setDocument(pdfDocument);
this.customFindController?.setDocument(pdfDocument);
this._scriptingManager?.setDocument(pdfDocument);
if (this.#hiddenCopyElement) {
document.addEventListener("copy", this.#copyCallback.bind(this, textLayerMode), {
signal
});
}
if (this.#annotationEditorUIManager) {
eventBus.dispatch("annotationeditormodechanged", {
source: this,
mode: this.#annotationEditorMode
});
}
if (pdfDocument.loadingParams.disableAutoFetch || pagesCount > PagesCountLimit.FORCE_LAZY_PAGE_INIT) {
this._pagesCapability.resolve();
return;
}
let getPagesLeft = pagesCount - 1;
if (getPagesLeft <= 0) {
this._pagesCapability.resolve();
return;
}
if (this.#pageViewMode === "book") {
await this.ensureAdjacentPagesAreLoaded();
}
for (let pageNum = 2; pageNum <= pagesCount; ++pageNum) {
const promise = pdfDocument.getPage(pageNum).then(pdfPage => {
const pageView = this._pages[pageNum - 1];
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
if (--getPagesLeft === 0) {
this._pagesCapability.resolve();
}
}, reason => {
ngx_console.NgxConsole.error(`Unable to get page ${pageNum} to initialize viewer`, reason);
if (--getPagesLeft === 0) {
this._pagesCapability.resolve();
}
});
if (pageNum % PagesCountLimit.PAUSE_EAGER_PAGE_INIT === 0) {
await promise;
}
}
});
this.hidePagesDependingOnpageViewMode();
eventBus.dispatch("pagesinit", {
source: this
});
pdfDocument.getMetadata().then(({
info
}) => {
if (pdfDocument !== this.pdfDocument) {
return;
}
if (info.Language) {
viewer.lang = info.Language;
}
});
if (this.defaultRenderingQueue) {
this.update();
}
}).catch(reason => {
ngx_console.NgxConsole.error("Unable to initialize viewer", reason);
this._pagesCapability.reject(reason);
});
}
setPageLabels(labels) {
if (!this.pdfDocument) {
return;
}
if (!labels) {
this._pageLabels = null;
} else if (!(Array.isArray(labels) && this.pdfDocument.numPages === labels.length)) {
this._pageLabels = null;
ngx_console.NgxConsole.error(`setPageLabels: Invalid page labels.`);
} else {
this._pageLabels = labels;
}
for (let i = 0, ii = this._pages.length; i < ii; i++) {
this._pages[i].setPageLabel(this._pageLabels?.[i] ?? null);
}
}
_resetView() {
this._pages = [];
this._currentPageNumber = 1;
this._currentScale = UNKNOWN_SCALE;
this._currentScaleValue = null;
this._pageLabels = null;
const bufferSize = this.defaultCacheSize || DEFAULT_CACHE_SIZE;
this.#buffer = new PDFPageViewBuffer(bufferSize);
this._location = null;
this._pagesRotation = 0;
this._optionalContentConfigPromise = null;
this._firstPageCapability = Promise.withResolvers();
this._onePageRenderedCapability = Promise.withResolvers();
this._pagesCapability = Promise.withResolvers();
this._scrollMode = ScrollMode.VERTICAL;
this._previousScrollMode = ScrollMode.UNKNOWN;
this._spreadMode = SpreadMode.NONE;
this.#scrollModePageState = {
previousPageNumber: 1,
scrollDown: true,
pages: []
};
this.#eventAbortController?.abort();
this.#eventAbortController = null;
this.viewer.textContent = "";
this._updateScrollMode();
this.viewer.removeAttribute("lang");
this.#hiddenCopyElement?.remove();
this.#hiddenCopyElement = null;
this.#cleanupSwitchAnnotationEditorMode();
}
#ensurePageViewVisible() {
if (this._scrollMode !== ScrollMode.PAGE) {
throw new Error("#ensurePageViewVisible: Invalid scrollMode value.");
}
const pageNumber = this._currentPageNumber,
state = this.#scrollModePageState,
viewer = this.viewer;
viewer.textContent = "";
state.pages.length = 0;
if (this._spreadMode === SpreadMode.NONE && !this.isInPresentationMode) {
const pageView = this._pages[pageNumber - 1];
viewer.append(pageView.div);
state.pages.push(pageView);
} else {
const pageIndexSet = new Set(),
parity = this._spreadMode - 1;
if (parity === -1) {
pageIndexSet.add(pageNumber - 1);
} else if (pageNumber % 2 !== parity) {
pageIndexSet.add(pageNumber - 1);
pageIndexSet.add(pageNumber);
} else {
pageIndexSet.add(pageNumber - 2);
pageIndexSet.add(pageNumber - 1);
}
const spread = document.createElement("div");
spread.className = "spread";
if (this.isInPresentationMode) {
const dummyPage = document.createElement("div");
dummyPage.className = "dummyPage";
spread.append(dummyPage);
}
for (const i of pageIndexSet) {
const pageView = this._pages[i];
if (!pageView) {
continue;
}
spread.append(pageView.div);
state.pages.push(pageView);
}
viewer.append(spread);
}
state.scrollDown = pageNumber >= state.previousPageNumber;
state.previousPageNumber = pageNumber;
}
_scrollUpdate() {
if (this.pagesCount === 0) {
return;
}
this.update();
}
scrollPagePosIntoView(pageNumber, pageSpot) {
const pageDiv = this._pages[pageNumber - 1].div;
if (pageSpot) {
const targetPageSpot = {
...pageSpot
};
if (typeof targetPageSpot.top === "string") {
if (targetPageSpot.top.endsWith("%")) {
const percent = Number(targetPageSpot.top.replace("%", ""));
const viewerHeight = this.viewer.querySelector(".page")?.clientHeight;
let height = pageDiv.clientHeight ?? viewerHeight;
const visibleWindowHeight = this.viewer.parentElement.clientHeight;
height = Math.max(0, height - visibleWindowHeight);
targetPageSpot.top = percent * height / 100;
}
}
if (typeof targetPageSpot.left === "string") {
if (targetPageSpot.left.endsWith("%")) {
const percent = Number(targetPageSpot.left.replace("%", ""));
const viewerWidth = this.viewer.querySelector(".page")?.clientWidth;
const width = pageDiv.clientWidth ?? viewerWidth;
targetPageSpot.left = percent * width / 100;
}
}
this.#scrollIntoView({
div: pageDiv,
id: pageNumber
}, targetPageSpot);
} else {
this.#scrollIntoView({
div: pageDiv,
id: pageNumber
});
}
}
#scrollIntoView(pageView, pageSpot = null) {
if (!pageView) {
return;
}
const {
div,
id
} = pageView;
if (this._currentPageNumber !== id) {
this._setCurrentPageNumber(id);
}
if (this._scrollMode === ScrollMode.PAGE) {
this.#ensurePageViewVisible();
this.update();
}
if (!pageSpot && !this.isInPresentationMode) {
const left = div.offsetLeft + div.clientLeft,
right = left + div.clientWidth;
const {
scrollLeft,
clientWidth
} = this.container;
if (this._scrollMode === ScrollMode.HORIZONTAL || left < scrollLeft || right > scrollLeft + clientWidth) {
pageSpot = {
left: 0,
top: 0
};
}
}
scrollIntoView(div, pageSpot, false, this.pageViewMode === "infinite-scroll");
if (!this._currentScaleValue && this._location) {
this._location = null;
}
}
#isSameScale(newScale) {
return newScale === this._currentScale || Math.abs(newScale - this._currentScale) < 1e-15;
}
#setScaleUpdatePages(newScale, newValue, {
noScroll = false,
preset = false,
drawingDelay = -1,
origin = null
}) {
const previousScale = isNaN(Number(this.currentScale)) ? undefined : Number(this.currentScale);
const previousScaleValue = this.currentScaleValue;
this._currentScaleValue = newValue.toString();
if (this.#isSameScale(newScale)) {
if (preset) {
this.eventBus.dispatch("scalechanging", {
source: this,
scale: newScale,
presetValue: newValue,
previousScale,
previousPresetValue: previousScaleValue,
noScroll
});
}
return;
}
this.viewer.style.setProperty("--scale-factor", newScale * pdf.PixelsPerInch.PDF_TO_CSS_UNITS);
const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;
this.refresh(true, {
scale: newScale,
drawingDelay: postponeDrawing ? drawingDelay : -1
});
if (postponeDrawing) {
this.#scaleTimeoutId = setTimeout(() => {
this.#scaleTimeoutId = null;
this.refresh();
}, drawingDelay);
}
this._currentScale = newScale;
if (!noScroll) {
let page = this._currentPageNumber,
dest;
if (this._location && !(this.isInPresentationMode || this.isChangingPresentationMode)) {
page = this._location.pageNumber;
dest = [null, {
name: "XYZ"
}, this._location.left, this._location.top, null];
}
this.scrollPageIntoView({
pageNumber: page,
destArray: dest,
allowNegativeOffset: true
});
if (Array.isArray(origin)) {
const scaleDiff = newScale / previousScale - 1;
const [top, left] = this.containerTopLeft;
this.container.scrollLeft += (origin[0] - left) * scaleDiff;
this.container.scrollTop += (origin[1] - top) * scaleDiff;
}
}
this.eventBus.dispatch("scalechanging", {
source: this,
scale: newScale,
presetValue: preset ? newValue : undefined,
previousScale,
previousPresetValue: previousScaleValue,
noScroll
});
if (this.defaultRenderingQueue) {
this.update();
}
}
get #pageWidthScaleFactor() {
if (this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL) {
return 2;
}
return 1;
}
#setScale(value, options) {
if (!value) {
value = "auto";
}
if (this.maxZoom && this.maxZoom === this.minZoom) {
value = this.maxZoom;
}
let scale = parseFloat(value);
if (this._currentScale === scale) {
return;
}
if (scale > 0) {
options.preset = false;
this.#setScaleUpdatePages(scale, value, options);
} else {
const currentPage = this._pages[this._currentPageNumber - 1];
if (!currentPage) {
return;
}
let hPadding = SCROLLBAR_PADDING,
vPadding = VERTICAL_PADDING;
if (this.isInPresentationMode) {
hPadding = vPadding = 4;
if (this._spreadMode !== SpreadMode.NONE) {
hPadding *= 2;
}
} else if (this.removePageBorders) {
hPadding = vPadding = 0;
} else if (this._scrollMode === ScrollMode.HORIZONTAL) {
[hPadding, vPadding] = [vPadding, hPadding];
}
const pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale / this.#pageWidthScaleFactor;
const pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale;
switch (value) {
case "page-actual":
scale = 1;
break;
case "page-width":
scale = pageWidthScale;
break;
case "page-height":
scale = pageHeightScale;
break;
case "page-fit":
scale = Math.min(pageWidthScale, pageHeightScale);
break;
case "auto":
const horizontalScale = isPortraitOrientation(currentPage) ? pageWidthScale : Math.min(pageHeightScale, pageWidthScale);
scale = Math.min(MAX_AUTO_SCALE, horizontalScale);
break;
default:
ngx_console.NgxConsole.error(`#setScale: "${value}" is an unknown zoom value.`);
return;
}
options.preset = true;
this.#setScaleUpdatePages(scale, value, options);
}
}
#resetCurrentPageView() {
const pageView = this._pages[this._currentPageNumber - 1];
if (this.isInPresentationMode) {
this.#setScale(this._currentScaleValue, {
noScroll: true
});
}
this.#scrollIntoView(pageView);
}
pageLabelToPageNumber(label) {
if (!this._pageLabels) {
return null;
}
const i = this._pageLabels.indexOf(label);
if (i < 0) {
return null;
}
return i + 1;
}
scrollPageIntoView({
pageNumber,
destArray = null,
allowNegativeOffset = false,
ignoreDestinationZoom = false
}) {
if (!this.pdfDocument) {
return;
}
const pageView = Number.isInteger(pageNumber) && this._pages[pageNumber - 1];
if (!pageView) {
ngx_console.NgxConsole.error(`scrollPageIntoView: "${pageNumber}" is not a valid pageNumber parameter.`);
return;
}
if (this.isInPresentationMode || !destArray) {
this._setCurrentPageNumber(pageNumber, true);
return;
}
let x = 0,
y = 0;
let width = 0,
height = 0,
widthScale,
heightScale;
const changeOrientation = pageView.rotation % 180 !== 0;
const pageWidth = (changeOrientation ? pageView.height : pageView.width) / pageView.scale / pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
const pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
let scale = 0;
switch (destArray[1].name) {
case "XYZ":
x = destArray[2];
y = destArray[3];
scale = destArray[4];
x = x !== null ? x : 0;
y = y !== null ? y : pageHeight;
break;
case "Fit":
case "FitB":
scale = "page-fit";
break;
case "FitH":
case "FitBH":
y = destArray[2];
scale = "page-width";
if (y === null && this._location) {
x = this._location.left;
y = this._location.top;
} else if (typeof y !== "number" || y < 0) {
y = pageHeight;
}
break;
case "FitV":
case "FitBV":
x = destArray[2];
width = pageWidth;
height = pageHeight;
scale = "page-height";
break;
case "FitR":
x = destArray[2];
y = destArray[3];
width = destArray[4] - x;
height = destArray[5] - y;
let hPadding = SCROLLBAR_PADDING,
vPadding = VERTICAL_PADDING;
if (this.removePageBorders) {
hPadding = vPadding = 0;
}
widthScale = (this.container.clientWidth - hPadding) / width / pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
heightScale = (this.container.clientHeight - vPadding) / height / pdf.PixelsPerInch.PDF_TO_CSS_UNITS;
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
break;
default:
ngx_console.NgxConsole.error(`scrollPageIntoView: "${destArray[1].name}" is not a valid destination type.`);
return;
}
if (!ignoreDestinationZoom) {
if (scale && scale !== this._currentScale) {
this.currentScaleValue = scale;
} else if (this._currentScale === UNKNOWN_SCALE) {
this.currentScaleValue = DEFAULT_SCALE_VALUE;
}
}
this.#ensurePdfPageLoaded(pageView).then(() => {
this.renderingQueue.renderView(pageView);
if (this.pageViewMode === "single") {
if (this.currentPageNumber !== pageNumber) {
this.currentPageNumber = pageNumber;
}
}
});
if (scale === "page-fit" && !destArray[4]) {
this.#scrollIntoView(pageView);
return;
}
const boundingRect = [pageView.viewport.convertToViewportPoint(x, y), pageView.viewport.convertToViewportPoint(x + width, y + height)];
let left = Math.min(boundingRect[0][0], boundingRect[1][0]);
let top = Math.min(boundingRect[0][1], boundingRect[1][1]);
if (!allowNegativeOffset) {
left = Math.max(left, 0);
top = Math.max(top, 0);
}
this.#scrollIntoView(pageView, {
left,
top
});
}
_updateLocation(firstPage) {
const currentScale = this._currentScale;
const currentScaleValue = this._currentScaleValue;
const normalizedScaleValue = parseFloat(currentScaleValue) === currentScale ? Math.round(currentScale * 10000) / 100 : currentScaleValue;
const pageNumber = firstPage.id;
const currentPageView = this._pages[pageNumber - 1];
const container = this.container;
const topLeft = currentPageView.getPagePoint(container.scrollLeft - firstPage.x, container.scrollTop - firstPage.y);
const intLeft = Math.round(topLeft[0]);
const intTop = Math.round(topLeft[1]);
let pdfOpenParams = `#page=${pageNumber}`;
if (!this.isInPresentationMode) {
pdfOpenParams += `&zoom=${normalizedScaleValue},${intLeft},${intTop}`;
}
this._location = {
pageNumber,
scale: normalizedScaleValue,
top: intTop,
left: intLeft,
rotation: this._pagesRotation,
pdfOpenParams
};
}
update(noScroll = false) {
if (this.scrollMode === ScrollMode.PAGE) {
this.viewer.classList.add("singlePageView");
} else {
this.viewer.classList.remove("singlePageView");
}
const visible = this._getVisiblePages();
const visiblePages = visible.views,
numVisiblePages = visiblePages.length;
if (numVisiblePages === 0) {
return;
}
const bufferSize = this.defaultCacheSize || DEFAULT_CACHE_SIZE;
const newCacheSize = Math.max(bufferSize, 2 * numVisiblePages + 1);
this.#buffer.resize(newCacheSize, visible.ids);
this.renderingQueue.renderHighestPriority(visible);
const isSimpleLayout = this._spreadMode === SpreadMode.NONE && (this._scrollMode === ScrollMode.PAGE || this._scrollMode === ScrollMode.VERTICAL);
const currentId = this._currentPageNumber;
let stillFullyVisible = false;
for (const page of visiblePages) {
if (page.percent < 100) {
break;
}
if (page.id === currentId && isSimpleLayout) {
stillFullyVisible = true;
break;
}
}
if (this.scrollMode !== ScrollMode.PAGE && !noScroll) {
this._setCurrentPageNumber(stillFullyVisible ? currentId : visiblePages[0].id);
}
this._updateLocation(visible.first);
this.eventBus.dispatch("updateviewarea", {
source: this,
location: this._location
});
this.hidePagesDependingOnpageViewMode();
}
#switchToEditAnnotationMode() {
const visible = this._getVisiblePages();
const pagesToRefresh = [];
const {
ids,
views
} = visible;
for (const page of views) {
const {
view
} = page;
if (!view.hasEditableAnnotations()) {
ids.delete(view.id);
continue;
}
pagesToRefresh.push(page);
}
if (pagesToRefresh.length === 0) {
return null;
}
this.renderingQueue.renderHighestPriority({
first: pagesToRefresh[0],
last: pagesToRefresh.at(-1),
views: pagesToRefresh,
ids
});
return ids;
}
containsElement(element) {
return this.container.contains(element);
}
focus() {
this.container.focus();
}
get _isContainerRtl() {
return getComputedStyle(this.container).direction === "rtl";
}
get isInPresentationMode() {
return this.presentationModeState === PresentationModeState.FULLSCREEN;
}
get isChangingPresentationMode() {
return this.presentationModeState === PresentationModeState.CHANGING;
}
get isHorizontalScrollbarEnabled() {
return this.isInPresentationMode ? false : this.container.scrollWidth > this.container.clientWidth;
}
get isVerticalScrollbarEnabled() {
return this.isInPresentationMode ? false : this.container.scrollHeight > this.container.clientHeight;
}
_getVisiblePages() {
const views = this._scrollMode === ScrollMode.PAGE ? this.#scrollModePageState.pages : this._pages,
horizontal = this._scrollMode === ScrollMode.HORIZONTAL,
rtl = horizontal && this._isContainerRtl;
return getVisibleElements({
scrollEl: this.container,
views,
sortByVisibility: true,
horizontal,
rtl
});
}
cleanup() {
for (const pageView of this._pages) {
if (pageView.renderingState !== RenderingStates.FINISHED) {
pageView.reset();
}
}
}
_cancelRendering() {
for (const pageView of this._pages) {
pageView.cancelRendering();
}
}
async #ensurePdfPageLoaded(pageView) {
if (pageView.pdfPage) {
return pageView.pdfPage;
}
try {
const pdfPage = await this.pdfDocument.getPage(pageView.id);
if (!pageView.pdfPage) {
pageView.setPdfPage(pdfPage);
}
return pdfPage;
} catch (reason) {
ngx_console.NgxConsole.error("Unable to get page for page view", reason);
return null;
}
}
#getScrollAhead(visible) {
if (visible.first?.id === 1) {
return true;
} else if (visible.last?.id === this.pagesCount) {
return false;
}
switch (this._scrollMode) {
case ScrollMode.PAGE:
return this.#scrollModePageState.scrollDown;
case ScrollMode.HORIZONTAL:
return this.scroll.right;
}
return this.scroll.down;
}
forceRendering(currentlyVisiblePages) {
const visiblePages = currentlyVisiblePages || this._getVisiblePages();
const scrollAhead = this.#getScrollAhead(visiblePages);
const preRenderExtra = this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL;
const pageView = this.renderingQueue.getHighestPriority(visiblePages, this._pages, scrollAhead, preRenderExtra);
if (pageView) {
this.#ensurePdfPageLoaded(pageView).then(() => {
this.renderingQueue.renderView(pageView);
});
return true;
}
return false;
}
get hasEqualPageSizes() {
const firstPageView = this._pages[0];
for (let i = 1, ii = this._pages.length; i < ii; ++i) {
const pageView = this._pages[i];
if (pageView.width !== firstPageView.width || pageView.height !== firstPageView.height) {
return false;
}
}
return true;
}
getPagesOverview() {
let initialOrientation;
return this._pages.map(pageView => {
const viewport = pageView.pdfPage.getViewport({
scale: 1
});
const orientation = isPortraitOrientation(viewport);
if (initialOrientation === undefined) {
initialOrientation = orientation;
} else if (this.enablePrintAutoRotate && orientation !== initialOrientation) {
return {
width: viewport.height,
height: viewport.width,
rotation: (viewport.rotation - 90) % 360
};
}
return {
width: viewport.width,
height: viewport.height,
rotation: viewport.rotation
};
});
}
get optionalContentConfigPromise() {
if (!this.pdfDocument) {
return Promise.resolve(null);
}
if (!this._optionalContentConfigPromise) {
ngx_console.NgxConsole.error("optionalContentConfigPromise: Not initialized yet.");
return this.pdfDocument.getOptionalContentConfig({
intent: "display"
});
}
return this._optionalContentConfigPromise;
}
set optionalContentConfigPromise(promise) {
if (!(promise instanceof Promise)) {
throw new Error(`Invalid optionalContentConfigPromise: ${promise}`);
}
if (!this.pdfDocument) {
return;
}
if (!this._optionalContentConfigPromise) {
return;
}
this._optionalContentConfigPromise = promise;
this.refresh(false, {
optionalContentConfigPromise: promise
});
this.eventBus.dispatch("optionalcontentconfigchanged", {
source: this,
promise
});
}
get scrollMode() {
return this._scrollMode;
}
set scrollMode(mode) {
if (this._scrollMode === mode) {
return;
}
if (!isValidScrollMode(mode)) {
throw new Error(`Invalid scroll mode: ${mode}`);
}
if (this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE) {
return;
}
this._previousScrollMode = this._scrollMode;
this._scrollMode = mode;
this.eventBus.dispatch("scrollmodechanged", {
source: this,
mode
});
this._updateScrollMode(this._currentPageNumber);
}
_updateScrollMode(pageNumber = null) {
const scrollMode = this._scrollMode,
viewer = this.viewer;
viewer.classList.toggle("scrollHorizontal", scrollMode === ScrollMode.HORIZONTAL);
viewer.classList.toggle("scrollWrapped", scrollMode === ScrollMode.WRAPPED);
if (!this.pdfDocument || !pageNumber) {
return;
}
if (scrollMode === ScrollMode.PAGE) {
this.#ensurePageViewVisible();
} else if (this._previousScrollMode === ScrollMode.PAGE) {
this._updateSpreadMode();
}
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
this.#setScale(this._currentScaleValue, {
noScroll: true
});
}
this._setCurrentPageNumber(pageNumber, true);
this.update();
}
get spreadMode() {
return this._spreadMode;
}
set spreadMode(mode) {
if (this._spreadMode === mode) {
return;
}
if (!isValidSpreadMode(mode)) {
throw new Error(`Invalid spread mode: ${mode}`);
}
this._spreadMode = mode;
this.eventBus.dispatch("spreadmodechanged", {
source: this,
mode
});
this._updateSpreadMode(this._currentPageNumber);
}
_updateSpreadMode(pageNumber = null) {
if (!this.pdfDocument) {
return;
}
const viewer = this.viewer,
pages = this._pages;
if (this._scrollMode === ScrollMode.PAGE) {
this.#ensurePageViewVisible();
} else {
viewer.textContent = "";
if (this._spreadMode === SpreadMode.NONE) {
for (const pageView of this._pages) {
viewer.append(pageView.div);
}
} else {
const parity = this._spreadMode - 1;
let spread = null;
for (let i = 0, ii = pages.length; i < ii; ++i) {
if (spread === null) {
spread = document.createElement("div");
spread.className = "spread";
viewer.append(spread);
} else if (i % 2 === parity) {
spread = spread.cloneNode(false);
viewer.append(spread);
}
spread.append(pages[i].div);
}
}
}
this.hidePagesDependingOnpageViewMode();
if (!pageNumber) {
return;
}
if (this._currentScaleValue && isNaN(this._currentScaleValue)) {
this.#setScale(this._currentScaleValue, {
noScroll: true
});
}
this._setCurrentPageNumber(pageNumber, true);
this.update();
}
_getPageAdvance(currentPageNumber, previous = false) {
if (this.pageViewMode === "book") {
return 2;
}
switch (this._scrollMode) {
case ScrollMode.WRAPPED:
{
const {
views
} = this._getVisiblePages(),
pageLayout = new Map();
for (const {
id,
y,
percent,
widthPercent
} of views) {
if (percent === 0 || widthPercent < 100) {
continue;
}
let yArray = pageLayout.get(y);
if (!yArray) {
pageLayout.set(y, yArray ||= []);
}
yArray.push(id);
}
for (const yArray of pageLayout.values()) {
const currentIndex = yArray.indexOf(currentPageNumber);
if (currentIndex === -1) {
continue;
}
const numPages = yArray.length;
if (numPages === 1) {
break;
}
if (previous) {
for (let i = currentIndex - 1, ii = 0; i >= ii; i--) {
const currentId = yArray[i],
expectedId = yArray[i + 1] - 1;
if (currentId < expectedId) {
return currentPageNumber - expectedId;
}
}
} else {
for (let i = currentIndex + 1, ii = numPages; i < ii; i++) {
const currentId = yArray[i],
expectedId = yArray[i - 1] + 1;
if (currentId > expectedId) {
return expectedId - currentPageNumber;
}
}
}
if (previous) {
const firstId = yArray[0];
if (firstId < currentPageNumber) {
return currentPageNumber - firstId + 1;
}
} else {
const lastId = yArray[numPages - 1];
if (lastId > currentPageNumber) {
return lastId - currentPageNumber + 1;
}
}
break;
}
break;
}
case ScrollMode.HORIZONTAL:
{
break;
}
case ScrollMode.PAGE:
case ScrollMode.VERTICAL:
{
if (this._spreadMode === SpreadMode.NONE) {
break;
}
const parity = this._spreadMode - 1;
if (previous && currentPageNumber % 2 !== parity) {
break;
} else if (!previous && currentPageNumber % 2 === parity) {
break;
}
const {
views
} = this._getVisiblePages(),
expectedId = previous ? currentPageNumber - 1 : currentPageNumber + 1;
for (const {
id,
percent,
widthPercent
} of views) {
if (id !== expectedId) {
continue;
}
if (percent > 0 && widthPercent === 100) {
return 2;
}
break;
}
break;
}
}
return 1;
}
nextPage() {
const currentPageNumber = this._currentPageNumber,
pagesCount = this.pagesCount;
if (currentPageNumber >= pagesCount) {
return false;
}
const advance = this._getPageAdvance(currentPageNumber, false) || 1;
this.currentPageNumber = Math.min(currentPageNumber + advance, pagesCount);
return true;
}
previousPage() {
const currentPageNumber = this._currentPageNumber;
if (currentPageNumber <= 1) {
return false;
}
const advance = this._getPageAdvance(currentPageNumber, true) || 1;
this.currentPageNumber = Math.max(currentPageNumber - advance, 1);
return true;
}
updateScale({
drawingDelay,
scaleFactor = null,
steps = null,
origin
}) {
if (steps === null && scaleFactor === null) {
throw new Error("Invalid updateScale options: either `steps` or `scaleFactor` must be provided.");
}
if (!this.pdfDocument) {
return;
}
let newScale = this._currentScale;
if (scaleFactor > 0 && scaleFactor !== 1) {
newScale = Math.round(newScale * scaleFactor * 100) / 100;
} else if (steps) {
const delta = steps > 0 ? DEFAULT_SCALE_DELTA : 1 / DEFAULT_SCALE_DELTA;
const round = steps > 0 ? Math.ceil : Math.floor;
steps = Math.abs(steps);
do {
newScale = round((newScale * delta).toFixed(2) * 10) / 10;
} while (--steps > 0);
}
const minScale = Number(this.minZoom) ?? MIN_SCALE;
newScale = Math.max(minScale, newScale);
const maxScale = Number(this.maxZoom) ?? MAX_SCALE;
newScale = Math.min(maxScale, newScale);
this.#setScale(newScale, {
noScroll: false,
drawingDelay,
origin
});
}
increaseScale(options = {}) {
this.updateScale({
...options,
steps: options.steps ?? 1
});
}
decreaseScale(options = {}) {
this.updateScale({
...options,
steps: -(options.steps ?? 1)
});
}
#updateContainerHeightCss(height = this.container.clientHeight) {
if (height !== this.#previousContainerHeight) {
this.#previousContainerHeight = height;
docStyle.setProperty("--viewer-container-height", `${height}px`);
}
}
#resizeObserverCallback(entries) {
for (const entry of entries) {
if (entry.target === this.container) {
this.#updateContainerHeightCss(Math.floor(entry.borderBoxSize[0].blockSize));
this.#containerTopLeft = null;
break;
}
}
}
get containerTopLeft() {
return this.#containerTopLeft ||= [this.container.offsetTop, this.container.offsetLeft];
}
#cleanupSwitchAnnotationEditorMode() {
this.#switchAnnotationEditorModeAC?.abort();
this.#switchAnnotationEditorModeAC = null;
if (this.#switchAnnotationEditorModeTimeoutId !== null) {
clearTimeout(this.#switchAnnotationEditorModeTimeoutId);
this.#switchAnnotationEditorModeTimeoutId = null;
}
}
get annotationEditorMode() {
return this.#annotationEditorUIManager ? this.#annotationEditorMode : util.AnnotationEditorType.DISABLE;
}
set annotationEditorMode({
mode,
editId = null,
isFromKeyboard = false
}) {
if (!this.#annotationEditorUIManager) {
throw new Error(`The AnnotationEditor is not enabled.`);
}
if (this.#annotationEditorMode === mode) {
return;
}
if (!isValidAnnotationEditorMode(mode)) {
throw new Error(`Invalid AnnotationEditor mode: ${mode}`);
}
if (!this.pdfDocument) {
return;
}
if (mode === util.AnnotationEditorType.STAMP) {
this.#mlManager?.loadModel("altText");
}
const {
eventBus
} = this;
const updater = () => {
this.#cleanupSwitchAnnotationEditorMode();
this.#annotationEditorMode = mode;
this.#annotationEditorUIManager.updateMode(mode, editId, isFromKeyboard);
eventBus.dispatch("annotationeditormodechanged", {
source: this,
mode
});
};
if (mode === util.AnnotationEditorType.NONE || this.#annotationEditorMode === util.AnnotationEditorType.NONE) {
const isEditing = mode !== util.AnnotationEditorType.NONE;
if (!isEditing) {
this.pdfDocument.annotationStorage.resetModifiedIds();
}
for (const pageView of this._pages) {
pageView.toggleEditingMode(isEditing);
}
const idsToRefresh = this.#switchToEditAnnotationMode();
if (isEditing && idsToRefresh) {
this.#cleanupSwitchAnnotationEditorMode();
this.#switchAnnotationEditorModeAC = new AbortController();
const signal = AbortSignal.any([this.#eventAbortController.signal, this.#switchAnnotationEditorModeAC.signal]);
eventBus._on("pagerendered", ({
pageNumber
}) => {
idsToRefresh.delete(pageNumber);
if (idsToRefresh.size === 0) {
this.#switchAnnotationEditorModeTimeoutId = setTimeout(updater, 0);
}
}, {
signal
});
return;
}
}
updater();
}
refresh(noUpdate = false, updateArgs = Object.create(null)) {
if (!this.pdfDocument) {
return;
}
for (const pageView of this._pages) {
pageView.update(updateArgs);
}
if (this.#scaleTimeoutId !== null) {
clearTimeout(this.#scaleTimeoutId);
this.#scaleTimeoutId = null;
}
if (!noUpdate) {
this.update();
}
}
getSerializedAnnotations() {
const rawAnnotations = this.pdfDocument.annotationStorage.getAll();
if (rawAnnotations) {
const annotations = Object.values(rawAnnotations);
return annotations.filter(a => a.serialize).map(a => a.serialize()).filter(a => a?.annotationType !== undefined);
}
return null;
}
async addEditorAnnotation(data) {
try {
if (typeof data === "string") {
data = JSON.parse(data);
}
} catch (ex) {
ngx_console.NgxConsole.error(`Please pass a JSON string or an Array of JSON objects to addEditorAnnotation: "${ex.message}".`);
return;
}
if (!Array.isArray(data)) {
data = [data];
}
await this.#annotationEditorUIManager.addSerializedEditor(data, true, true, false);
}
removeEditorAnnotations(filter = () => true) {
this.#annotationEditorUIManager.removeEditors(filter);
}
destroyBookMode() {
if (this.pageFlip) {
this.pageFlip.destroy();
this.pageFlip = null;
}
}
stopRendering() {
this._cancelRendering();
}
}
;// ./web/secondary_toolbar.js
class SecondaryToolbar {
#opts;
constructor(options, eventBus) {
this.#opts = options;
const buttons = [{
element: options.presentationModeButton,
eventName: "presentationmode",
close: true
}, {
element: options.printButton,
eventName: "print",
close: true
}, {
element: options.downloadButton,
eventName: "download",
close: true
}, {
element: options.viewBookmarkButton,
eventName: null,
close: true
}, {
element: options.firstPageButton,
eventName: "firstpage",
close: true
}, {
element: options.lastPageButton,
eventName: "lastpage",
close: true
}, {
element: options.pageRotateCwButton,
eventName: "rotatecw",
close: false
}, {
element: options.pageRotateCcwButton,
eventName: "rotateccw",
close: false
}, {
element: options.cursorSelectToolButton,
eventName: "switchcursortool",
eventDetails: {
tool: CursorTool.SELECT
},
close: true
}, {
element: options.cursorHandToolButton,
eventName: "switchcursortool",
eventDetails: {
tool: CursorTool.HAND
},
close: true
}, {
element: options.scrollPageButton,
eventName: "switchscrollmode",
eventDetails: {
mode: ScrollMode.PAGE
},
close: true
}, {
element: options.scrollVerticalButton,
eventName: "switchscrollmode",
eventDetails: {
mode: ScrollMode.VERTICAL
},
close: true
}, {
element: options.scrollHorizontalButton,
eventName: "switchscrollmode",
eventDetails: {
mode: ScrollMode.HORIZONTAL
},
close: true
}, {
element: options.scrollWrappedButton,
eventName: "switchscrollmode",
eventDetails: {
mode: ScrollMode.WRAPPED
},
close: true
}, {
element: options.spreadNoneButton,
eventName: "switchspreadmode",
eventDetails: {
mode: SpreadMode.NONE
},
close: true
}, {
element: options.spreadOddButton,
eventName: "switchspreadmode",
eventDetails: {
mode: SpreadMode.ODD
},
close: true
}, {
element: options.spreadEvenButton,
eventName: "switchspreadmode",
eventDetails: {
mode: SpreadMode.EVEN
},
close: true
}, {
element: options.imageAltTextSettingsButton,
eventName: "imagealttextsettings",
close: true
}, {
element: options.documentPropertiesButton,
eventName: "documentproperties",
close: true
}];
buttons.push({
element: options.openFileButton,
eventName: "openfile",
close: true
});
this.eventBus = eventBus;
this.opened = false;
this.#bindListeners(buttons);
this.reset();
}
get isOpen() {
return this.opened;
}
setPageNumber(pageNumber) {
this.pageNumber = pageNumber;
this.#updateUIState();
}
setPagesCount(pagesCount) {
this.pagesCount = pagesCount;
this.#updateUIState();
}
reset() {
this.pageNumber = 0;
this.pagesCount = 0;
this.#updateUIState();
this.eventBus.dispatch("switchcursortool", {
source: this,
reset: true
});
this.#scrollModeChanged({
mode: ScrollMode.VERTICAL
});
this.#spreadModeChanged({
mode: SpreadMode.NONE
});
}
#updateUIState() {
const {
firstPageButton,
lastPageButton,
pageRotateCwButton,
pageRotateCcwButton
} = this.#opts;
firstPageButton.disabled = this.pageNumber <= 1;
lastPageButton.disabled = this.pageNumber >= this.pagesCount;
pageRotateCwButton.disabled = this.pagesCount === 0;
pageRotateCcwButton.disabled = this.pagesCount === 0;
}
#bindListeners(buttons) {
const {
eventBus
} = this;
const {
toggleButton
} = this.#opts;
toggleButton.addEventListener("click", this.toggle.bind(this));
for (const {
element,
eventName,
close,
eventDetails
} of buttons) {
if (!element) {
continue;
}
element.addEventListener("click", evt => {
if (eventName !== null) {
eventBus.dispatch(eventName, {
source: this,
...eventDetails
});
}
if (close) {
this.close();
}
eventBus.dispatch("reporttelemetry", {
source: this,
details: {
type: "buttons",
data: {
id: element.id
}
}
});
});
}
eventBus._on("cursortoolchanged", this.#cursorToolChanged.bind(this));
eventBus._on("scrollmodechanged", this.#scrollModeChanged.bind(this));
eventBus._on("spreadmodechanged", this.#spreadModeChanged.bind(this));
}
#cursorToolChanged({
tool,
disabled
}) {
const {
cursorSelectToolButton,
cursorHandToolButton
} = this.#opts;
toggleCheckedBtn(cursorSelectToolButton, tool === CursorTool.SELECT);
toggleCheckedBtn(cursorHandToolButton, tool === CursorTool.HAND);
cursorSelectToolButton.disabled = disabled;
cursorHandToolButton.disabled = disabled;
}
#scrollModeChanged({
mode
}) {
const {
scrollPageButton,
scrollVerticalButton,
scrollHorizontalButton,
scrollWrappedButton,
spreadNoneButton,
spreadOddButton,
spreadEvenButton
} = this.#opts;
toggleCheckedBtn(scrollPageButton, mode === ScrollMode.PAGE);
toggleCheckedBtn(scrollVerticalButton, mode === ScrollMode.VERTICAL);
toggleCheckedBtn(scrollHorizontalButton, mode === ScrollMode.HORIZONTAL);
toggleCheckedBtn(scrollWrappedButton, mode === ScrollMode.WRAPPED);
const forceScrollModePage = this.pagesCount > PagesCountLimit.FORCE_SCROLL_MODE_PAGE;
scrollPageButton.disabled = forceScrollModePage;
scrollVerticalButton.disabled = forceScrollModePage;
scrollHorizontalButton.disabled = forceScrollModePage;
scrollWrappedButton.disabled = forceScrollModePage;
const isHorizontal = mode === ScrollMode.HORIZONTAL;
spreadNoneButton.disabled = isHorizontal;
spreadOddButton.disabled = isHorizontal;
spreadEvenButton.disabled = isHorizontal;
}
#spreadModeChanged({
mode
}) {
const {
spreadNoneButton,
spreadOddButton,
spreadEvenButton
} = this.#opts;
toggleCheckedBtn(spreadNoneButton, mode === SpreadMode.NONE);
toggleCheckedBtn(spreadOddButton, mode === SpreadMode.ODD);
toggleCheckedBtn(spreadEvenButton, mode === SpreadMode.EVEN);
}
open() {
if (this.opened) {
return;
}
this.opened = true;
const {
toggleButton,
toolbar
} = this.#opts;
toggleExpandedBtn(toggleButton, true, toolbar);
}
close() {
if (!this.opened) {
return;
}
this.opened = false;
const {
toggleButton,
toolbar
} = this.#opts;
toggleExpandedBtn(toggleButton, false, toolbar);
}
toggle() {
if (this.opened) {
this.close();
} else {
this.open();
}
}
}
;// ./web/toolbar.js
class Toolbar {
#opts;
#defaultZoomValue = DEFAULT_SCALE_VALUE;
#maxZoom = MAX_SCALE;
#minZoom = MIN_SCALE;
constructor(options, eventBus, defaultZoomValue, minZoom, maxZoom, toolbarDensity = 0) {
this.#opts = options;
this.#defaultZoomValue = defaultZoomValue;
this.#maxZoom = maxZoom;
this.#minZoom = minZoom;
this.eventBus = eventBus;
const buttons = [{
element: options.previous,
eventName: "previouspage"
}, {
element: options.next,
eventName: "nextpage"
}, {
element: options.zoomIn,
eventName: "zoomin"
}, {
element: options.zoomOut,
eventName: "zoomout"
}, {
element: options.print,
eventName: "print"
}, {
element: options.presentationModeButton,
eventName: "presentationmode"
}, {
element: options.download,
eventName: "download"
}, {
element: options.editorFreeTextButton,
eventName: "switchannotationeditormode",
eventDetails: {
get mode() {
const {
classList
} = options.editorFreeTextButton;
return classList.contains("toggled") ? util.AnnotationEditorType.NONE : util.AnnotationEditorType.FREETEXT;
}
}
}, {
element: options.editorHighlightButton,
eventName: "switchannotationeditormode",
eventDetails: {
get mode() {
const {
classList
} = options.editorHighlightButton;
return classList.contains("toggled") ? util.AnnotationEditorType.NONE : util.AnnotationEditorType.HIGHLIGHT;
}
}
}, {
element: options.editorInkButton,
eventName: "switchannotationeditormode",
eventDetails: {
get mode() {
const {
classList
} = options.editorInkButton;
return classList.contains("toggled") ? util.AnnotationEditorType.NONE : util.AnnotationEditorType.INK;
}
}
}, {
element: options.editorStampButton,
eventName: "switchannotationeditormode",
eventDetails: {
get mode() {
const {
classList
} = options.editorStampButton;
return classList.contains("toggled") ? util.AnnotationEditorType.NONE : util.AnnotationEditorType.STAMP;
}
},
telemetry: {
type: "editing",
data: {
action: "pdfjs.image.icon_click"
}
}
}];
this.#bindListeners(buttons);
this.#updateToolbarDensity({
value: toolbarDensity
});
this.reset();
}
get maxZoom() {
return this.#maxZoom;
}
set maxZoom(value) {
this.#maxZoom = value;
}
get minZoom() {
return this.#minZoom;
}
set minZoom(value) {
this.#minZoom = value;
}
#updateToolbarDensity({
value
}) {
let name = "normal";
switch (value) {
case 1:
name = "compact";
break;
case 2:
name = "touch";
break;
}
document.documentElement.setAttribute("data-toolbar-density", name);
}
#setAnnotationEditorUIManager(uiManager, parentContainer) {
const colorPicker = new pdf.ColorPicker({
uiManager
});
uiManager.setMainHighlightColorPicker(colorPicker);
parentContainer.append(colorPicker.renderMainDropdown());
}
setPageNumber(pageNumber, pageLabel) {
this.pageNumber = pageNumber;
this.pageLabel = pageLabel;
this.#updateUIState(false);
}
setPagesCount(pagesCount, hasPageLabels) {
this.pagesCount = pagesCount;
this.hasPageLabels = hasPageLabels;
this.#updateUIState(true);
}
setPageScale(pageScaleValue, pageScale) {
this.pageScaleValue = (pageScaleValue || pageScale).toString();
this.pageScale = pageScale;
this.#updateUIState(false);
}
reset() {
this.pageNumber = 0;
this.pageLabel = null;
this.hasPageLabels = false;
this.pagesCount = 0;
const defaultZoomOption = this.#defaultZoomValue || DEFAULT_SCALE_VALUE;
this.pageScaleValue = defaultZoomOption;
if (Number(defaultZoomOption)) {
this.pageScale = Number(defaultZoomOption);
}
this.#updateUIState(true);
this.updateLoadingIndicatorState();
this.#editorModeChanged({
mode: util.AnnotationEditorType.DISABLE
});
}
#bindListeners(buttons) {
const {
eventBus
} = this;
const {
editorHighlightColorPicker,
editorHighlightButton,
pageNumber,
scaleSelect
} = this.#opts;
const self = this;
for (const {
element,
eventName,
eventDetails,
telemetry
} of buttons) {
if (!element) {
continue;
}
element.addEventListener("click", evt => {
if (eventName !== null) {
eventBus.dispatch(eventName, {
source: this,
...eventDetails,
isFromKeyboard: evt.detail === 0
});
}
if (telemetry) {
eventBus.dispatch("reporttelemetry", {
source: this,
details: telemetry
});
}
});
}
pageNumber.addEventListener("click", function () {
this.select();
});
pageNumber.addEventListener("change", function () {
eventBus.dispatch("pagenumberchanged", {
source: self,
value: this.value
});
});
scaleSelect.addEventListener("change", function () {
if (this.value === "custom") {
return;
}
eventBus.dispatch("scalechanged", {
source: self,
value: this.value
});
});
scaleSelect.addEventListener("click", function ({
target
}) {
if (this.value === self.pageScaleValue && target.tagName.toUpperCase() === "OPTION") {
this.blur();
}
});
scaleSelect.oncontextmenu = display_utils.noContextMenu;
eventBus._on("annotationeditormodechanged", this.#editorModeChanged.bind(this));
eventBus._on("showannotationeditorui", ({
mode
}) => {
switch (mode) {
case util.AnnotationEditorType.HIGHLIGHT:
editorHighlightButton.click();
break;
}
});
eventBus._on("toolbardensity", this.#updateToolbarDensity.bind(this));
if (editorHighlightColorPicker) {
eventBus._on("annotationeditoruimanager", ({
uiManager
}) => {
this.#setAnnotationEditorUIManager(uiManager, editorHighlightColorPicker);
}, {
once: true
});
}
}
#editorModeChanged({
mode
}) {
const {
editorFreeTextButton,
editorFreeTextParamsToolbar,
editorHighlightButton,
editorHighlightParamsToolbar,
editorInkButton,
editorInkParamsToolbar,
editorStampButton,
editorStampParamsToolbar
} = this.#opts;
toggleExpandedBtn(editorFreeTextButton, mode === util.AnnotationEditorType.FREETEXT, editorFreeTextParamsToolbar);
toggleExpandedBtn(editorHighlightButton, mode === util.AnnotationEditorType.HIGHLIGHT, editorHighlightParamsToolbar);
toggleExpandedBtn(editorInkButton, mode === util.AnnotationEditorType.INK, editorInkParamsToolbar);
toggleExpandedBtn(editorStampButton, mode === util.AnnotationEditorType.STAMP, editorStampParamsToolbar);
const isDisable = mode === util.AnnotationEditorType.DISABLE;
editorFreeTextButton.disabled = isDisable;
editorHighlightButton.disabled = isDisable;
editorInkButton.disabled = isDisable;
editorStampButton.disabled = isDisable;
}
#updateUIState(resetNumPages = false) {
const {
pageNumber,
pagesCount,
pageScaleValue,
pageScale
} = this;
const opts = this.#opts;
if (resetNumPages) {
if (this.hasPageLabels) {
opts.pageNumber.type = "text";
opts.numPages.setAttribute("data-l10n-id", "pdfjs-page-of-pages");
} else {
opts.pageNumber.type = "number";
opts.numPages.setAttribute("data-l10n-id", "pdfjs-of-pages");
opts.numPages.setAttribute("data-l10n-args", JSON.stringify({
pagesCount
}));
}
opts.pageNumber.max = pagesCount;
}
if (this.hasPageLabels) {
opts.pageNumber.value = this.pageLabel;
opts.numPages.setAttribute("data-l10n-args", JSON.stringify({
pageNumber,
pagesCount
}));
} else {
opts.pageNumber.value = pageNumber;
}
opts.previous.disabled = pageNumber <= 1;
opts.next.disabled = pageNumber >= pagesCount;
const minScale = Number(this.minZoom) ?? MIN_SCALE;
const maxScale = Number(this.maxZoom) ?? MAX_SCALE;
if (opts.zoomOut) {
opts.zoomOut.disabled = pageScale <= minScale;
}
if (opts.zoomIn) {
opts.zoomIn.disabled = pageScale >= maxScale;
}
let predefinedValueFound = false;
if (opts.scaleSelect.options) {
for (const option of opts.scaleSelect.options) {
if (option.value !== pageScaleValue) {
option.selected = false;
continue;
}
option.selected = true;
predefinedValueFound = true;
}
}
if (!predefinedValueFound) {
opts.customScaleOption.selected = true;
opts.customScaleOption.setAttribute("data-l10n-args", JSON.stringify({
scale: Math.round(pageScale * 10000) / 100
}));
}
this.eventBus.dispatch("updateuistate", {
source: this,
widget: "Toolbar",
pageNumber,
pagesCount,
pageScaleValue,
pageScale
});
}
updateLoadingIndicatorState(loading = false) {
const {
pageNumber
} = this.#opts;
pageNumber.classList.toggle("loading", loading);
}
}
;// ./web/view_history.js
const DEFAULT_VIEW_HISTORY_CACHE_SIZE = 20;
class ViewHistory {
constructor(fingerprint, cacheSize = DEFAULT_VIEW_HISTORY_CACHE_SIZE) {
this.fingerprint = fingerprint;
this.cacheSize = cacheSize;
this._initializedPromise = this._readFromStorage().then(databaseStr => {
const database = JSON.parse(databaseStr || "{}");
let index = -1;
if (!Array.isArray(database.files)) {
database.files = [];
} else {
while (database.files.length >= this.cacheSize) {
database.files.shift();
}
for (let i = 0, ii = database.files.length; i < ii; i++) {
const branch = database.files[i];
if (branch.fingerprint === this.fingerprint) {
index = i;
break;
}
}
}
if (index === -1) {
index = database.files.push({
fingerprint: this.fingerprint
}) - 1;
}
this.file = database.files[index];
this.database = database;
});
}
async _writeToStorage() {
if (AppOptions.get("disableHistory")) {
return;
}
const databaseStr = JSON.stringify(this.database);
try {
localStorage.setItem("pdfjs.history", databaseStr);
} catch (safariSecurityException) {}
}
async _readFromStorage() {
if (AppOptions.get("disableHistory")) {
return undefined;
}
try {
return localStorage.getItem("pdfjs.history");
} catch (safariSecurityException) {
return undefined;
}
}
async set(name, val) {
await this._initializedPromise;
this.file[name] = val;
return this._writeToStorage();
}
async setMultiple(properties) {
await this._initializedPromise;
for (const name in properties) {
this.file[name] = properties[name];
}
return this._writeToStorage();
}
async get(name, defaultValue) {
await this._initializedPromise;
const val = this.file[name];
return val !== undefined ? val : defaultValue;
}
async getMultiple(properties) {
await this._initializedPromise;
const values = Object.create(null);
for (const name in properties) {
const val = this.file[name];
values[name] = val !== undefined ? val : properties[name];
}
return values;
}
}
;// ./web/app.js
const FORCE_PAGES_LOADED_TIMEOUT = 10;
const WHEEL_ZOOM_DISABLED_TIMEOUT = 1000;
const ViewOnLoad = {
UNKNOWN: -1,
PREVIOUS: 0,
INITIAL: 1
};
const PDFViewerApplication = {
initialBookmark: document.location.hash.substring(1),
_initializedCapability: {
...Promise.withResolvers(),
settled: false
},
appConfig: null,
pdfDocument: null,
pdfLoadingTask: null,
printService: null,
pdfViewer: null,
pdfThumbnailViewer: null,
pdfRenderingQueue: null,
pdfPresentationMode: null,
pdfDocumentProperties: null,
pdfLinkService: null,
pdfHistory: null,
pdfSidebar: null,
pdfOutlineViewer: null,
pdfAttachmentViewer: null,
pdfLayerViewer: null,
pdfCursorTools: null,
pdfScriptingManager: null,
pdfWorker: null,
store: null,
downloadManager: null,
overlayManager: null,
preferences: new Preferences(),
toolbar: null,
secondaryToolbar: null,
eventBus: null,
l10n: null,
annotationEditorParams: null,
imageAltTextSettings: null,
isInitialViewSet: false,
isViewerEmbedded: window.parent !== window,
url: "",
baseUrl: "",
mlManager: null,
_downloadUrl: "",
_eventBusAbortController: null,
_windowAbortController: null,
_globalAbortController: new AbortController(),
documentInfo: null,
metadata: null,
_contentDispositionFilename: null,
_contentLength: null,
_saveInProgress: false,
_wheelUnusedTicks: 0,
_wheelUnusedFactor: 1,
_touchManager: null,
_touchUnusedTicks: 0,
_touchUnusedFactor: 1,
_PDFBug: null,
_hasAnnotationEditors: false,
_title: document.title,
_printAnnotationStoragePromise: null,
_isCtrlKeyDown: false,
_caretBrowsing: null,
_isScrolling: false,
editorUndoBar: null,
async initialize(appConfig) {
this.appConfig = appConfig;
this.cspPolicyService = appConfig.cspPolicyService;
try {
await this.preferences.initializedPromise;
} catch (ex) {
console.error("initialize:", ex);
}
if (AppOptions.get("pdfBugEnabled")) {
await this._parseHashParams();
}
let mode;
switch (AppOptions.get("viewerCssTheme")) {
case 1:
mode = "is-light";
break;
case 2:
mode = "is-dark";
break;
}
if (mode) {
document.documentElement.classList.add(mode);
}
this.l10n = await this.externalServices.createL10n();
document.getElementsByTagName("html")[0].dir = this.l10n.getDirection();
this.l10n.translate(appConfig.appContainer || document.documentElement);
if (this.isViewerEmbedded && AppOptions.get("externalLinkTarget") === LinkTarget.NONE) {
AppOptions.set("externalLinkTarget", LinkTarget.TOP);
}
await this._initializeViewerComponents();
this.bindEvents();
this.bindWindowEvents();
this._initializedCapability.settled = true;
this._initializedCapability.resolve();
this.initializeLoadingBar();
},
async _parseHashParams() {
const hash = document.location.hash.substring(1);
if (!hash) {
return;
}
const {
mainContainer,
viewerContainer
} = this.appConfig,
params = parseQueryString(hash);
const loadPDFBug = async () => {
if (this._PDFBug) {
return;
}
const {
PDFBug
} = await import(/*webpackIgnore: true*/AppOptions.get("debuggerSrc"));
this._PDFBug = PDFBug;
};
if (params.get("disableworker") === "true") {
try {
pdf.GlobalWorkerOptions.workerSrc ||= AppOptions.get("workerSrc");
await import(/*webpackIgnore: true*/pdf.PDFWorker.workerSrc);
} catch (ex) {
console.error("_parseHashParams:", ex);
}
}
if (params.has("textlayer")) {
switch (params.get("textlayer")) {
case "off":
AppOptions.set("textLayerMode", TextLayerMode.DISABLE);
break;
case "visible":
case "shadow":
case "hover":
viewerContainer.classList.add(`textLayer-${params.get("textlayer")}`);
try {
await loadPDFBug();
this._PDFBug.loadCSS();
} catch (ex) {
console.error("_parseHashParams:", ex);
}
break;
}
}
if (params.has("pdfbug")) {
AppOptions.setAll({
pdfBug: true,
fontExtraProperties: true
});
const enabled = params.get("pdfbug").split(",");
try {
await loadPDFBug();
this._PDFBug.init(mainContainer, enabled);
} catch (ex) {
console.error("_parseHashParams:", ex);
}
}
if (params.has("locale")) {
AppOptions.set("localeProperties", {
lang: params.get("locale")
});
}
const opts = {
disableAutoFetch: x => x === "true",
disableFontFace: x => x === "true",
disableHistory: x => x === "true",
disableRange: x => x === "true",
disableStream: x => x === "true",
verbosity: x => x | 0
};
for (const name in opts) {
const check = opts[name],
key = name.toLowerCase();
if (params.has(key)) {
AppOptions.set(name, check(params.get(key)));
}
}
},
async _initializeViewerComponents() {
const {
appConfig,
externalServices,
l10n
} = this;
const eventBus = new EventBus();
this.eventBus = AppOptions.eventBus = eventBus;
this.mlManager?.setEventBus(eventBus, this._globalAbortController.signal);
this.overlayManager = new OverlayManager();
const pdfRenderingQueue = new PDFRenderingQueue();
pdfRenderingQueue.onIdle = this._cleanup.bind(this);
this.pdfRenderingQueue = pdfRenderingQueue;
const pdfLinkService = new PDFLinkService({
eventBus,
externalLinkTarget: AppOptions.get("externalLinkTarget"),
externalLinkRel: AppOptions.get("externalLinkRel"),
ignoreDestinationZoom: AppOptions.get("ignoreDestinationZoom")
});
this.pdfLinkService = pdfLinkService;
const downloadManager = this.downloadManager = new DownloadManager();
let FindControllerConstructor = PDFFindController;
if (AppOptions.get("findController")) {
FindControllerConstructor = AppOptions.get("findController");
}
const customFindController = new FindControllerConstructor({
linkService: pdfLinkService,
eventBus,
pageViewMode: AppOptions.get("pageViewMode"),
updateMatchesCountOnProgress: true,
listenToEventBus: false
});
this.customFindController = customFindController;
const findController = new FindControllerConstructor({
linkService: pdfLinkService,
eventBus,
pageViewMode: AppOptions.get("pageViewMode"),
updateMatchesCountOnProgress: true,
listenToEventBus: true
});
this.findController = findController;
const pdfScriptingManager = new PDFScriptingManager({
eventBus,
externalServices,
docProperties: this._scriptingDocProperties.bind(this)
});
this.pdfScriptingManager = pdfScriptingManager;
const container = appConfig.mainContainer,
viewer = appConfig.viewerContainer;
const annotationEditorMode = AppOptions.get("annotationEditorMode");
const pageColors = AppOptions.get("forcePageColors") || window.matchMedia("(forced-colors: active)").matches ? {
background: AppOptions.get("pageColorsBackground"),
foreground: AppOptions.get("pageColorsForeground")
} : null;
let altTextManager;
if (AppOptions.get("enableUpdatedAddImage")) {
altTextManager = appConfig.newAltTextDialog ? new NewAltTextManager(appConfig.newAltTextDialog, this.overlayManager, eventBus) : null;
} else {
altTextManager = appConfig.altTextDialog ? new AltTextManager(appConfig.altTextDialog, container, this.overlayManager, eventBus) : null;
}
if (appConfig.editorUndoBar) {
this.editorUndoBar = new EditorUndoBar(appConfig.editorUndoBar, eventBus);
}
const enableHWA = AppOptions.get("enableHWA");
const pdfViewer = new PDFViewer({
container,
viewer,
eventBus,
renderingQueue: pdfRenderingQueue,
linkService: pdfLinkService,
downloadManager,
altTextManager,
editorUndoBar: this.editorUndoBar,
findController,
customFindController,
scriptingManager: AppOptions.get("enableScripting") && pdfScriptingManager,
l10n,
textLayerMode: AppOptions.get("textLayerMode"),
annotationMode: AppOptions.get("annotationMode"),
annotationEditorMode,
annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
enableHighlightFloatingButton: AppOptions.get("enableHighlightFloatingButton"),
enableUpdatedAddImage: AppOptions.get("enableUpdatedAddImage"),
enableNewAltTextWhenAddingImage: AppOptions.get("enableNewAltTextWhenAddingImage"),
imageResourcesPath: AppOptions.get("imageResourcesPath"),
removePageBorders: AppOptions.get("removePageBorders"),
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
pageViewMode: AppOptions.get("pageViewMode"),
enablePermissions: AppOptions.get("enablePermissions"),
pageColors,
mlManager: this.mlManager,
abortSignal: this._globalAbortController.signal,
enableHWA,
defaultCacheSize: AppOptions.get("defaultCacheSize"),
minZoom: AppOptions.get("minZoom"),
maxZoom: AppOptions.get("maxZoom"),
cspPolicyService: this.cspPolicyService,
supportsPinchToZoom: this.supportsPinchToZoom
});
this.pdfViewer = pdfViewer;
pdfRenderingQueue.setViewer(pdfViewer);
pdfLinkService.setViewer(pdfViewer);
pdfScriptingManager.setViewer(pdfViewer);
if (appConfig.sidebar?.thumbnailView) {
this.pdfThumbnailViewer = new PDFThumbnailViewer({
container: appConfig.sidebar.thumbnailView,
eventBus,
renderingQueue: pdfRenderingQueue,
linkService: pdfLinkService,
pageColors,
abortSignal: this._globalAbortController.signal,
enableHWA
});
pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
}
if (!this.isViewerEmbedded && !AppOptions.get("disableHistory")) {
this.pdfHistory = new PDFHistory({
linkService: pdfLinkService,
eventBus
});
pdfLinkService.setHistory(this.pdfHistory);
}
if (!this.supportsIntegratedFind && appConfig.findBar) {
this.findBar = new PDFFindBar(appConfig.findBar, appConfig.principalContainer, eventBus);
}
if (appConfig.annotationEditorParams) {
if (typeof AbortSignal.any === "function" && annotationEditorMode !== util.AnnotationEditorType.DISABLE) {
this.annotationEditorParams = new AnnotationEditorParams(appConfig.annotationEditorParams, eventBus);
} else {
for (const id of ["editorModeButtons", "editorModeSeparator"]) {
document.getElementById(id)?.classList.add("hidden");
}
}
}
if (this.mlManager && appConfig.secondaryToolbar?.imageAltTextSettingsButton) {
this.imageAltTextSettings = new ImageAltTextSettings(appConfig.altTextSettingsDialog, this.overlayManager, eventBus, this.mlManager);
}
if (appConfig.documentProperties) {
this.pdfDocumentProperties = new PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, l10n, () => this._docFilename);
}
if (appConfig.secondaryToolbar?.cursorHandToolButton) {
this.pdfCursorTools = new PDFCursorTools({
container,
eventBus,
cursorToolOnLoad: AppOptions.get("cursorToolOnLoad")
});
}
if (appConfig.toolbar) {
this.toolbar = new Toolbar(appConfig.toolbar, eventBus, AppOptions.get("defaultZoomValue"), AppOptions.get("minZoom"), AppOptions.get("maxZoom"), AppOptions.get("toolbarDensity"));
}
if (appConfig.secondaryToolbar) {
if (AppOptions.get("enableAltText")) {
appConfig.secondaryToolbar.imageAltTextSettingsButton?.classList.remove("hidden");
appConfig.secondaryToolbar.imageAltTextSettingsSeparator?.classList.remove("hidden");
}
this.secondaryToolbar = new SecondaryToolbar(appConfig.secondaryToolbar, eventBus);
}
if (this.supportsFullscreen && (appConfig.toolbar?.presentationModeButton || appConfig.secondaryToolbar?.presentationModeButton)) {
this.pdfPresentationMode = new PDFPresentationMode({
container,
pdfViewer,
eventBus
});
}
const prompt = AppOptions.get("passwordPrompt");
if (!prompt) {
if (appConfig.passwordOverlay) {
this.passwordPrompt = new PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.isViewerEmbedded);
}
} else {
this.passwordPrompt = prompt;
}
if (appConfig.sidebar?.outlineView) {
this.pdfOutlineViewer = new PDFOutlineViewer({
container: appConfig.sidebar.outlineView,
eventBus,
l10n,
linkService: pdfLinkService,
downloadManager
});
}
if (appConfig.sidebar?.attachmentsView) {
this.pdfAttachmentViewer = new PDFAttachmentViewer({
container: appConfig.sidebar.attachmentsView,
eventBus,
l10n,
downloadManager
});
}
if (appConfig.sidebar?.layersView) {
this.pdfLayerViewer = new PDFLayerViewer({
container: appConfig.sidebar.layersView,
eventBus,
l10n
});
}
if (appConfig.sidebar) {
this.pdfSidebar = new PDFSidebar({
elements: appConfig.sidebar,
eventBus,
l10n
});
this.pdfSidebar.onToggled = this.forceRendering.bind(this);
this.pdfSidebar.onUpdateThumbnails = () => {
for (const pageView of pdfViewer.getCachedPageViews()) {
if (pageView.renderingState === RenderingStates.FINISHED) {
this.pdfThumbnailViewer.getThumbnail(pageView.id - 1)?.setImage(pageView);
}
}
this.pdfThumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
};
}
},
async run(config) {
await this.initialize(config);
const {
appConfig,
eventBus
} = this;
let file;
const queryString = document.location.search.substring(1);
const params = parseQueryString(queryString);
file = params.get("file") ?? AppOptions.get("defaultUrl");
validateFileURL(file);
const fileInput = this._openFileInput = document.createElement("input");
fileInput.id = "fileInput";
fileInput.className = "ngx-extended-pdf-viewer-file-input";
fileInput.hidden = true;
fileInput.type = "file";
fileInput.value = null;
fileInput.accept = ".pdf,application/pdf";
document.body.append(fileInput);
fileInput.addEventListener("change", function (evt) {
const {
files
} = evt.target;
if (!files || files.length === 0) {
return;
}
eventBus.dispatch("fileinputchange", {
source: this,
fileInput: evt.target
});
});
appConfig.mainContainer.addEventListener("dragover", function (evt) {
if (AppOptions.get("enableDragAndDrop")) {
if (!evt.dataTransfer?.items) {
return;
}
for (const item of evt.dataTransfer.items) {
if (item.type === "application/pdf") {
evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === "copy" ? "copy" : "move";
(0,display_utils.stopEvent)(evt);
return;
}
}
}
});
appConfig.mainContainer.addEventListener("drop", function (evt) {
if (AppOptions.get("enableDragAndDrop")) {
const files = evt.dataTransfer?.files;
if (!files || files.length === 0 || files[0].type !== "application/pdf") {
return;
}
(0,display_utils.stopEvent)(evt);
eventBus.dispatch("fileinputchange", {
source: this,
fileInput: evt.dataTransfer,
dropEvent: evt
});
}
});
if (!AppOptions.get("supportsDocumentFonts")) {
AppOptions.set("disableFontFace", true);
this.l10n.get("pdfjs-web-fonts-disabled").then(msg => {
console.warn(msg);
});
}
if (!this.supportsPrinting) {
appConfig.toolbar?.print?.classList.add("hidden");
appConfig.secondaryToolbar?.printButton.classList.add("hidden");
}
if (!this.supportsFullscreen) {
appConfig.toolbar.presentationModeButton.classList.add("hidden");
appConfig.secondaryToolbar?.presentationModeButton?.classList.add("hidden");
}
if (this.supportsIntegratedFind) {
appConfig.findBar?.toggleButton?.classList.add("hidden");
}
if (file) {
this.open({
url: file
});
} else {
this._hideViewBookmark();
}
const event = new CustomEvent("webviewerinitialized", {
bubbles: true,
cancelable: true,
detail: {
source: window
}
});
try {
document.dispatchEvent(event);
} catch (ex) {
console.error(`webviewerinitialized: ${ex}`);
parent.document.dispatchEvent(event);
}
},
get externalServices() {
return (0,util.shadow)(this, "externalServices", new ExternalServices());
},
get initialized() {
return this._initializedCapability.settled;
},
get initializedPromise() {
return this._initializedCapability.promise;
},
updateZoom(steps, scaleFactor, origin) {
if (this.pdfViewer.isInPresentationMode) {
return;
}
this.pdfViewer.updateScale({
drawingDelay: AppOptions.get("defaultZoomDelay"),
steps,
scaleFactor,
origin
});
},
zoomIn() {
this.updateZoom(1);
},
zoomOut() {
this.updateZoom(-1);
},
zoomReset() {
if (this.pdfViewer.isInPresentationMode) {
return;
}
this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
},
touchPinchCallback(origin, prevDistance, distance) {
if (this.supportsPinchToZoom) {
const newScaleFactor = this._accumulateFactor(this.pdfViewer.currentScale, distance / prevDistance, "_touchUnusedFactor");
this.updateZoom(null, newScaleFactor, origin);
} else {
const PIXELS_PER_LINE_SCALE = 30;
const ticks = this._accumulateTicks((distance - prevDistance) / PIXELS_PER_LINE_SCALE, "_touchUnusedTicks");
this.updateZoom(ticks, null, origin);
}
},
touchPinchEndCallback() {
this._touchUnusedTicks = 0;
this._touchUnusedFactor = 1;
},
get pagesCount() {
return this.pdfDocument ? this.pdfDocument.numPages : 0;
},
get page() {
return this.pdfViewer.currentPageNumber;
},
set page(val) {
this.pdfViewer.currentPageNumber = val;
},
get supportsPrinting() {
return PDFPrintServiceFactory.supportsPrinting;
},
get supportsFullscreen() {
return (0,util.shadow)(this, "supportsFullscreen", document.fullscreenEnabled);
},
get supportsPinchToZoom() {
return (0,util.shadow)(this, "supportsPinchToZoom", AppOptions.get("supportsPinchToZoom"));
},
get supportsIntegratedFind() {
return (0,util.shadow)(this, "supportsIntegratedFind", AppOptions.get("supportsIntegratedFind"));
},
initializeLoadingBar() {
const barElement = document.getElementById("loadingBar");
const bar = barElement ? new ProgressBar(barElement) : null;
bar?.hide();
return (0,util.shadow)(this, "loadingBar", bar);
},
get supportsMouseWheelZoomCtrlKey() {
return (0,util.shadow)(this, "supportsMouseWheelZoomCtrlKey", AppOptions.get("supportsMouseWheelZoomCtrlKey"));
},
get supportsMouseWheelZoomMetaKey() {
return (0,util.shadow)(this, "supportsMouseWheelZoomMetaKey", AppOptions.get("supportsMouseWheelZoomMetaKey"));
},
get supportsCaretBrowsingMode() {
return AppOptions.get("supportsCaretBrowsingMode");
},
moveCaret(isUp, select) {
this._caretBrowsing ||= new CaretBrowsingMode(this._globalAbortController.signal, this.appConfig.mainContainer, this.appConfig.viewerContainer, this.appConfig.toolbar?.container);
this._caretBrowsing.moveCaret(isUp, select);
},
setTitleUsingUrl(url = "", downloadUrl = null) {},
setTitle(title = this._title) {
this._title = title;
if (this.isViewerEmbedded) {
return;
}
const editorIndicator = this._hasAnnotationEditors && !this.pdfRenderingQueue.printing;
document.title = `${editorIndicator ? "* " : ""}${title}`;
},
get _docFilename() {
return this._contentDispositionFilename || (0,display_utils.getPdfFilenameFromUrl)(this.url, PDFViewerApplication.appConfig.filenameForDownload);
},
_hideViewBookmark() {
if (!this.appConfig) {
return;
}
const {
secondaryToolbar
} = this.appConfig;
secondaryToolbar?.viewBookmarkButton.classList.add("hidden");
if (secondaryToolbar?.presentationModeButton?.classList.contains("hidden")) {
document.getElementById("viewBookmarkSeparator")?.classList.add("hidden");
}
},
async close() {
this._unblockDocumentLoadEvent();
this._hideViewBookmark();
if (!this.pdfLoadingTask) {
return;
}
if (this.pdfDocument?.annotationStorage.size > 0 && this._annotationStorageModified) {
try {
await this.save();
} catch {}
}
const promises = [];
promises.push(this.pdfLoadingTask.destroy());
this.pdfLoadingTask = null;
if (this.pdfDocument) {
this.pdfDocument = null;
this.pdfThumbnailViewer?.setDocument(null);
this.pdfViewer.setDocument(null);
this.pdfLinkService.setDocument(null);
this.pdfDocumentProperties?.setDocument(null);
}
this.pdfLinkService.externalLinkEnabled = true;
this.store = null;
this.isInitialViewSet = false;
this.url = "";
this.baseUrl = "";
this._downloadUrl = "";
this.documentInfo = null;
this.metadata = null;
this._contentDispositionFilename = null;
this._contentLength = null;
this._saveInProgress = false;
this._hasAnnotationEditors = false;
promises.push(this.pdfScriptingManager.destroyPromise, this.passwordPrompt.close());
this.setTitle();
this.pdfSidebar?.reset();
this.pdfOutlineViewer?.reset();
this.pdfAttachmentViewer?.reset();
this.pdfLayerViewer?.reset();
this.pdfHistory?.reset();
this.findBar?.reset();
this.toolbar?.reset();
this.secondaryToolbar?.reset();
this._PDFBug?.cleanup();
await Promise.all(promises);
},
async open(args) {
this.pdfViewer?.destroyBookMode();
if (this.pdfLoadingTask) {
await this.close();
}
const workerParams = AppOptions.getAll(OptionKind.WORKER);
workerParams.cspPolicyService = args.cspPolicyService;
if (args.workerSrc && args.workerSrc !== workerParams.workerSrc) {
workerParams.workerSrc = args.workerSrc;
this.pdfWorker = null;
}
Object.assign(pdf.GlobalWorkerOptions, workerParams);
workerParams.port = workerParams.port ?? pdf.GlobalWorkerOptions.workerPort;
workerParams.cspPolicyService = this.cspPolicyService;
if (this.pdfWorker === null) {
this.pdfWorker = workerParams.port ? pdf.PDFWorker.fromPort(workerParams) : new pdf.PDFWorker(workerParams);
}
if (args.url) {
this.setTitleUsingUrl(args.originalUrl || args.url, args.url);
}
const apiParams = AppOptions.getAll(OptionKind.API);
const loadingTask = (0,api.getDocument)({
...apiParams,
...args,
worker: this.pdfWorker,
cspPolicyService: args.cspPolicyService
});
this.pdfLoadingTask = loadingTask;
loadingTask.onPassword = (updateCallback, reason) => {
if (this.isViewerEmbedded) {
this._unblockDocumentLoadEvent();
}
this.pdfLinkService.externalLinkEnabled = false;
this.passwordPrompt.setUpdateCallback(updateCallback, reason);
this.passwordPrompt.open();
};
loadingTask.onProgress = ({
loaded,
total
}) => {
this.progress(loaded / total);
this.eventBus?.dispatch("progress", {
source: this,
type: "load",
total,
loaded,
percent: 100 * loaded / total
});
};
const showUnverifiedSignatures = this.serviceWorkerOptions.showUnverifiedSignatures;
return loadingTask.promise.then(pdfDocument => {
pdfDocument._transport.messageHandler.send('showUnverifiedSignatures', showUnverifiedSignatures);
this.load(pdfDocument);
}, reason => {
if (loadingTask !== this.pdfLoadingTask) {
return undefined;
}
let key = "pdfjs-loading-error";
if (reason instanceof util.InvalidPDFException) {
key = "pdfjs-invalid-file-error";
} else if (reason instanceof pdf.MissingPDFException) {
key = "pdfjs-missing-file-error";
} else if (reason instanceof util.UnexpectedResponseException) {
key = "pdfjs-unexpected-response-error";
}
if (PDFViewerApplication.onError) {
PDFViewerApplication.onError(reason);
}
return this._documentError(key, {
message: reason.message
}).then(() => {
throw reason;
});
});
},
async download() {
let data;
try {
data = await this.pdfDocument.getData();
} catch {}
this.downloadManager.download(data, this._downloadUrl, this._docFilename);
},
async save() {
if (this._saveInProgress) {
return;
}
this._saveInProgress = true;
await this.pdfScriptingManager.dispatchWillSave();
try {
const data = await this.pdfDocument.saveDocument();
this.downloadManager.download(data, this._downloadUrl, this._docFilename);
} catch (reason) {
ngx_console.NgxConsole.error(`Error when saving the document:`, reason);
await this.download();
} finally {
await this.pdfScriptingManager.dispatchDidSave();
this._saveInProgress = false;
}
if (this._hasAnnotationEditors) {
this.externalServices.reportTelemetry({
type: "editing",
data: {
type: "save",
stats: this.pdfDocument?.annotationStorage.editorStats
}
});
}
},
async downloadOrSave() {
const {
classList
} = this.appConfig.appContainer;
classList.add("wait");
await (this.pdfDocument?.annotationStorage.size > 0 ? this.save() : this.download());
classList.remove("wait");
},
async _exportWithAnnotations() {
if (this._saveInProgress) {
throw new Error(`Already downloading`);
}
this._saveInProgress = true;
await this.pdfScriptingManager.dispatchWillSave();
try {
this._ensureDownloadComplete();
const data = await this.pdfDocument.saveDocument();
const blob = new Blob([data], {
type: "application/pdf"
});
return blob;
} catch (reason) {
throw new Error(`Error when saving the document: ${reason.message}`);
} finally {
await this.pdfScriptingManager.dispatchDidSave();
this._saveInProgress = false;
}
},
_ensureDownloadComplete() {
if (this.pdfDocument) {
return;
}
throw new Error("PDF document not downloaded.");
},
async _exportWithoutAnnotations() {
try {
this._ensureDownloadComplete();
const data = await this.pdfDocument.getData();
const blob = new Blob([data], {
type: "application/pdf"
});
return blob;
} catch (reason) {
throw new Error(`Error when saving the document: ${reason.message}`);
}
},
async export() {
if (this.pdfDocument?.annotationStorage.size > 0) {
return this._exportWithAnnotations();
}
return this._exportWithoutAnnotations();
},
openInExternalApp() {
this.downloadOrSave({
openInExternalApp: true
});
},
async _documentError(key, moreInfo = null) {
this._unblockDocumentLoadEvent();
const message = await this._otherError(key || "pdfjs-loading-error", moreInfo);
this.eventBus.dispatch("documenterror", {
source: this,
message,
reason: moreInfo?.message ?? null
});
},
async _otherError(key, moreInfo = null) {
const message = await this.l10n.get(key);
const moreInfoText = [`PDF.js v${pdf.version || "?"} (build: ${api.build || "?"})`];
if (moreInfo) {
moreInfoText.push(`Message: ${moreInfo.message}`);
if (moreInfo.stack) {
moreInfoText.push(`Stack: ${moreInfo.stack}`);
} else {
if (moreInfo.filename) {
moreInfoText.push(`File: ${moreInfo.filename}`);
}
if (moreInfo.lineNumber) {
moreInfoText.push(`Line: ${moreInfo.lineNumber}`);
}
}
}
console.error(`${message}\n\n${moreInfoText.join("\n")}`);
return message;
},
progress(level) {
const percent = Math.round(level * 100);
if (!this.loadingBar || percent <= this.loadingBar.percent) {
return;
}
this.loadingBar.percent = percent;
if (this.pdfDocument?.loadingParams.disableAutoFetch ?? AppOptions.get("disableAutoFetch")) {
this.loadingBar.setDisableAutoFetch();
}
},
load(pdfDocument) {
this.pdfDocument = pdfDocument;
pdfDocument.getDownloadInfo().then(({
length
}) => {
this._contentLength = length;
this.loadingBar?.hide();
firstPagePromise.then(() => {
this.eventBus?.dispatch("documentloaded", {
source: this
});
});
});
const pageLayoutPromise = pdfDocument.getPageLayout().catch(() => {});
const pageModePromise = pdfDocument.getPageMode().catch(() => {});
const openActionPromise = pdfDocument.getOpenAction().catch(() => {});
this.toolbar?.setPagesCount(pdfDocument.numPages, false);
this.secondaryToolbar?.setPagesCount(pdfDocument.numPages);
this.pdfLinkService.setDocument(pdfDocument);
this.pdfDocumentProperties?.setDocument(pdfDocument);
const pdfViewer = this.pdfViewer;
pdfViewer.setDocument(pdfDocument);
const {
firstPagePromise,
onePageRendered,
pagesPromise
} = pdfViewer;
this.pdfThumbnailViewer?.setDocument(pdfDocument);
const storedPromise = (this.store = new ViewHistory(pdfDocument.fingerprints[0])).getMultiple({
page: null,
zoom: DEFAULT_SCALE_VALUE,
scrollLeft: "0",
scrollTop: "0",
rotation: null,
sidebarView: SidebarView.UNKNOWN,
scrollMode: ScrollMode.UNKNOWN,
spreadMode: SpreadMode.UNKNOWN
}).catch(() => {});
firstPagePromise.then(pdfPage => {
this.loadingBar?.setWidth(this.appConfig.viewerContainer);
this._initializeAnnotationStorageCallbacks(pdfDocument);
Promise.all([animationStarted, storedPromise, pageLayoutPromise, pageModePromise, openActionPromise]).then(async ([timeStamp, stored, pageLayout, pageMode, openAction]) => {
const viewOnLoad = AppOptions.get("viewOnLoad");
this._initializePdfHistory({
fingerprint: pdfDocument.fingerprints[0],
viewOnLoad,
initialDest: openAction?.dest
});
const initialBookmark = this.initialBookmark;
let zoom = AppOptions.get("defaultZoomValue");
if (!zoom || zoom === '') {
try {
zoom = await this.store.get('zoom');
if (typeof zoom === 'string') {
zoom = zoom?.replace("%", "");
}
if (!isNaN(Number(zoom))) {
zoom = Number(zoom) / 100;
}
} catch (error) {}
}
if (!pdfViewer.currentScaleValue && zoom && zoom !== '') {
pdfViewer.currentScaleValue = zoom;
}
let hash = zoom ? `zoom=${zoom}` : null;
let rotation = null;
let sidebarView = AppOptions.get("sidebarViewOnLoad");
let scrollMode = AppOptions.get("scrollModeOnLoad");
let spreadMode = AppOptions.get("spreadModeOnLoad");
if (stored?.page && viewOnLoad !== ViewOnLoad.INITIAL) {
hash = `page=${stored.page}&zoom=${zoom || stored.zoom},` + `${stored.scrollLeft},${stored.scrollTop}`;
rotation = parseInt(stored.rotation, 10);
if (sidebarView === SidebarView.UNKNOWN) {
sidebarView = stored.sidebarView | 0;
}
if (scrollMode === ScrollMode.UNKNOWN) {
scrollMode = stored.scrollMode | 0;
}
if (spreadMode === SpreadMode.UNKNOWN) {
spreadMode = stored.spreadMode | 0;
}
}
if (pageMode && sidebarView === SidebarView.UNKNOWN) {
sidebarView = apiPageModeToSidebarView(pageMode);
}
if (pageLayout && scrollMode === ScrollMode.UNKNOWN && spreadMode === SpreadMode.UNKNOWN) {
const modes = apiPageLayoutToViewerModes(pageLayout);
spreadMode = modes.spreadMode;
}
this.setInitialView(hash, {
rotation,
sidebarView,
scrollMode,
spreadMode
});
this.eventBus.dispatch("documentinit", {
source: this
});
if (!this.isViewerEmbedded) {
pdfViewer.focus();
}
await Promise.race([pagesPromise, new Promise(resolve => {
setTimeout(resolve, FORCE_PAGES_LOADED_TIMEOUT);
})]);
if (!initialBookmark && !hash) {
return;
}
if (pdfViewer.hasEqualPageSizes) {
return;
}
this.initialBookmark = initialBookmark;
pdfViewer.currentScaleValue = pdfViewer.currentScaleValue;
this.setInitialView(hash);
}).catch(() => {
this.setInitialView();
}).then(function () {
pdfViewer.update();
});
});
pagesPromise.then(() => {
this._unblockDocumentLoadEvent();
this._initializeAutoPrint(pdfDocument, openActionPromise);
}, reason => {
if (PDFViewerApplication.onError) {
PDFViewerApplication.onError(reason);
}
this._documentError("pdfjs-loading-error", {
message: reason.message
});
});
onePageRendered.then(data => {
this.externalServices.reportTelemetry({
type: "pageInfo",
timestamp: data.timestamp
});
if (this.pdfOutlineViewer) {
pdfDocument.getOutline().then(outline => {
if (pdfDocument !== this.pdfDocument) {
return;
}
this.pdfOutlineViewer.render({
outline,
pdfDocument
});
});
}
if (this.pdfAttachmentViewer) {
pdfDocument.getAttachments().then(attachments => {
if (pdfDocument !== this.pdfDocument) {
return;
}
this.pdfAttachmentViewer.render({
attachments
});
});
}
if (this.pdfLayerViewer) {
pdfViewer.optionalContentConfigPromise.then(optionalContentConfig => {
if (pdfDocument !== this.pdfDocument) {
return;
}
this.pdfLayerViewer.render({
optionalContentConfig,
pdfDocument
});
});
}
});
this._initializePageLabels(pdfDocument);
this._initializeMetadata(pdfDocument);
},
async _scriptingDocProperties(pdfDocument) {
if (!this.documentInfo) {
await new Promise(resolve => {
this.eventBus._on("metadataloaded", resolve, {
once: true
});
});
if (pdfDocument !== this.pdfDocument) {
return null;
}
}
if (!this._contentLength) {
await new Promise(resolve => {
this.eventBus._on("documentloaded", resolve, {
once: true
});
});
if (pdfDocument !== this.pdfDocument) {
return null;
}
}
return {
...this.documentInfo,
baseURL: this.baseUrl,
filesize: this._contentLength,
filename: this._docFilename,
metadata: this.metadata?.getRaw(),
authors: this.metadata?.get("dc:creator"),
numPages: this.pagesCount,
URL: this.url
};
},
async _initializeAutoPrint(pdfDocument, openActionPromise) {
const [openAction, jsActions] = await Promise.all([openActionPromise, this.pdfViewer.enableScripting ? null : pdfDocument.getJSActions()]);
if (pdfDocument !== this.pdfDocument) {
return;
}
let triggerAutoPrint = openAction?.action === "Print";
if (jsActions) {
ngx_console.NgxConsole.warn("Warning: JavaScript support is not enabled");
for (const name in jsActions) {
if (triggerAutoPrint) {
break;
}
switch (name) {
case "WillClose":
case "WillSave":
case "DidSave":
case "WillPrint":
case "DidPrint":
continue;
}
triggerAutoPrint = jsActions[name].some(js => AutoPrintRegExp.test(js));
}
}
if (triggerAutoPrint) {
this.triggerPrinting();
}
},
async _initializeMetadata(pdfDocument) {
const {
info,
metadata,
contentDispositionFilename,
contentLength
} = await pdfDocument.getMetadata();
if (pdfDocument !== this.pdfDocument) {
return;
}
this.documentInfo = info;
this.metadata = metadata;
this._contentDispositionFilename ??= contentDispositionFilename;
this._contentLength ??= contentLength;
if (AppOptions?.get("verbosity") > 0) {
ngx_console.NgxConsole.log(`PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` + `${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` + `(PDF.js: ${pdf.version || "?"} [${api.build || "?"}]) modified by ngx-extended-pdf-viewer ${ngxExtendedPdfViewerVersion}`);
}
let pdfTitle = info.Title;
const metadataTitle = metadata?.get("dc:title");
if (metadataTitle) {
if (metadataTitle !== "Untitled" && !/[\uFFF0-\uFFFF]/g.test(metadataTitle)) {
pdfTitle = metadataTitle;
}
}
if (pdfTitle) {
this.setTitle(`${pdfTitle} - ${this._contentDispositionFilename || this._title}`);
} else if (this._contentDispositionFilename) {
this.setTitle(this._contentDispositionFilename);
}
if (info.IsXFAPresent && !info.IsAcroFormPresent && !pdfDocument.isPureXfa) {
if (pdfDocument.loadingParams.enableXfa) {
ngx_console.NgxConsole.warn("Warning: XFA Foreground documents are not supported");
} else {
ngx_console.NgxConsole.warn("Warning: XFA support is not enabled");
}
} else if ((info.IsAcroFormPresent || info.IsXFAPresent) && !this.pdfViewer.renderForms) {
console.warn("Warning: Interactive form support is not enabled");
}
if (info.IsSignaturesPresent) {
console.warn("Warning: Digital signatures validation is not supported");
}
this.eventBus.dispatch("metadataloaded", {
source: this
});
},
async _initializePageLabels(pdfDocument) {
const labels = await pdfDocument.getPageLabels();
if (pdfDocument !== this.pdfDocument) {
return;
}
if (!labels || AppOptions.get("disablePageLabels")) {
return;
}
const numLabels = labels.length;
let standardLabels = 0,
emptyLabels = 0;
for (let i = 0; i < numLabels; i++) {
const label = labels[i];
if (label === (i + 1).toString()) {
standardLabels++;
} else if (label === "") {
emptyLabels++;
} else {
break;
}
}
if (standardLabels >= numLabels || emptyLabels >= numLabels) {
return;
}
const {
pdfViewer,
pdfThumbnailViewer,
toolbar
} = this;
pdfViewer.setPageLabels(labels);
pdfThumbnailViewer?.setPageLabels(labels);
toolbar?.setPagesCount(numLabels, true);
toolbar?.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel);
},
_initializePdfHistory({
fingerprint,
viewOnLoad,
initialDest = null
}) {
if (!this.pdfHistory) {
return;
}
this.pdfHistory.initialize({
fingerprint,
resetHistory: viewOnLoad === ViewOnLoad.INITIAL,
updateUrl: AppOptions.get("historyUpdateUrl")
});
if (this.pdfHistory.initialBookmark) {
this.initialBookmark = this.pdfHistory.initialBookmark;
this.initialRotation = this.pdfHistory.initialRotation;
}
if (initialDest && !this.initialBookmark && viewOnLoad === ViewOnLoad.UNKNOWN) {
this.initialBookmark = JSON.stringify(initialDest);
this.pdfHistory.push({
explicitDest: initialDest,
pageNumber: null
});
}
},
_initializeAnnotationStorageCallbacks(pdfDocument) {
if (pdfDocument !== this.pdfDocument) {
return;
}
const {
annotationStorage
} = pdfDocument;
annotationStorage.onSetModified = () => {
window.addEventListener("beforeunload", beforeUnload);
this._annotationStorageModified = true;
};
annotationStorage.onResetModified = () => {
window.removeEventListener("beforeunload", beforeUnload);
delete this._annotationStorageModified;
};
annotationStorage.onAnnotationEditor = typeStr => {
this._hasAnnotationEditors = !!typeStr;
this.setTitle();
};
},
setInitialView(storedHash, {
rotation,
sidebarView,
scrollMode,
spreadMode
} = {}) {
const setRotation = angle => {
if (isValidRotation(angle)) {
this.pdfViewer.pagesRotation = angle;
}
};
const setViewerModes = (scroll, spread) => {
if (isValidScrollMode(scroll)) {
this.pdfViewer.scrollMode = scroll;
}
if (isValidSpreadMode(spread)) {
this.pdfViewer.spreadMode = spread;
}
};
this.isInitialViewSet = true;
this.pdfSidebar?.setInitialView(sidebarView);
setViewerModes(scrollMode, spreadMode);
if (this.initialBookmark) {
setRotation(this.initialRotation);
delete this.initialRotation;
if (this.pdfLinkService.setHash) {
this.pdfLinkService.setHash(this.initialBookmark);
}
this.initialBookmark = null;
} else if (storedHash) {
setRotation(rotation);
if (this.pdfLinkService.setHash) {
this.pdfLinkService.setHash(storedHash);
}
}
this.toolbar?.setPageNumber(this.pdfViewer.currentPageNumber, this.pdfViewer.currentPageLabel);
this.secondaryToolbar?.setPageNumber(this.pdfViewer.currentPageNumber);
if (!this.pdfViewer.currentScaleValue) {
const defaultZoomOption = AppOptions?.get('defaultZoomValue');
if (defaultZoomOption) {
this.pdfViewer.currentScaleValue = defaultZoomOption;
}
}
},
_cleanup() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
if (!this.pdfDocument) {
return;
}
this.pdfViewer.cleanup();
this.pdfThumbnailViewer?.cleanup();
this.pdfDocument.cleanup(AppOptions.get("fontExtraProperties"));
},
forceRendering() {
this.pdfRenderingQueue.printing = !!this.printService;
this.pdfRenderingQueue.isThumbnailViewEnabled = this.pdfSidebar?.visibleView === SidebarView.THUMBS;
this.pdfRenderingQueue.renderHighestPriority();
},
beforePrint() {
this._printAnnotationStoragePromise = this.pdfScriptingManager.dispatchWillPrint().catch(() => {}).then(() => this.pdfDocument?.annotationStorage.print);
if (this.printService) {
return;
}
if (!this.supportsPrinting) {
this._otherError("pdfjs-printing-not-supported");
return;
}
if (!this.pdfViewer.pageViewsReady) {
this.l10n.get("pdfjs-printing-not-ready").then(msg => {
window.alert(msg);
});
return;
}
this.printService = PDFPrintServiceFactory.createPrintService({
pdfDocument: this.pdfDocument,
pagesOverview: this.pdfViewer.getPagesOverview(),
printResolution: AppOptions.get("printResolution"),
printAnnotationStoragePromise: this._printAnnotationStoragePromise,
eventBus: this.pdfViewer.eventBus,
cspPolicyService: this.cspPolicyService
});
this.forceRendering();
this.setTitle();
this.printService.layout();
if (this._hasAnnotationEditors) {
this.externalServices.reportTelemetry({
type: "editing",
data: {
type: "print",
stats: this.pdfDocument?.annotationStorage.editorStats
}
});
}
},
afterPrint() {
if (this._printAnnotationStoragePromise) {
this._printAnnotationStoragePromise.then(() => {
this.pdfScriptingManager.dispatchDidPrint();
});
this._printAnnotationStoragePromise = null;
}
if (this.printService) {
this.printService.destroy();
this.printService = null;
this.pdfDocument?.annotationStorage.resetModified();
}
this.forceRendering();
this.setTitle();
},
rotatePages(delta) {
this.pdfViewer.pagesRotation += delta;
},
requestPresentationMode() {
this.pdfPresentationMode?.request();
},
triggerPrinting() {
if (this.supportsPrinting) {
this.printPdf();
}
},
bindEvents() {
if (this._eventBusAbortController) {
return;
}
const ac = this._eventBusAbortController = new AbortController();
const opts = {
signal: ac.signal
};
const {
eventBus,
externalServices,
pdfDocumentProperties,
pdfViewer,
preferences
} = this;
eventBus._on("resize", onResize.bind(this), opts);
eventBus._on("hashchange", onHashchange.bind(this), opts);
eventBus._on("beforeprint", this.beforePrint.bind(this), opts);
eventBus._on("afterprint", this.afterPrint.bind(this), opts);
eventBus._on("pagerender", onPageRender.bind(this), opts);
eventBus._on("pagerendered", onPageRendered.bind(this), opts);
eventBus._on("updateviewarea", onUpdateViewarea.bind(this), opts);
eventBus._on("pagechanging", onPageChanging.bind(this), opts);
eventBus._on("scalechanging", onScaleChanging.bind(this), opts);
eventBus._on("rotationchanging", onRotationChanging.bind(this), opts);
eventBus._on("sidebarviewchanged", onSidebarViewChanged.bind(this), opts);
eventBus._on("pagemode", onPageMode.bind(this), opts);
eventBus._on("namedaction", onNamedAction.bind(this), opts);
eventBus._on("presentationmodechanged", evt => pdfViewer.presentationModeState = evt.state, opts);
eventBus._on("presentationmode", this.requestPresentationMode.bind(this), opts);
eventBus._on("switchannotationeditormode", evt => pdfViewer.annotationEditorMode = evt, opts);
eventBus._on("print", this.triggerPrinting.bind(this), opts);
eventBus._on("download", this.downloadOrSave.bind(this), opts);
eventBus._on("firstpage", () => this.page = 1, opts);
eventBus._on("lastpage", () => this.page = this.pagesCount, opts);
eventBus._on("nextpage", () => pdfViewer.nextPage(), opts);
eventBus._on("previouspage", () => pdfViewer.previousPage(), opts);
eventBus._on("zoomin", this.zoomIn.bind(this), opts);
eventBus._on("zoomout", this.zoomOut.bind(this), opts);
eventBus._on("zoomreset", this.zoomReset.bind(this), opts);
eventBus._on("pagenumberchanged", onPageNumberChanged.bind(this), opts);
eventBus._on("scalechanged", evt => pdfViewer.currentScaleValue = evt.value, opts);
eventBus._on("rotatecw", this.rotatePages.bind(this, 90), opts);
eventBus._on("rotateccw", this.rotatePages.bind(this, -90), opts);
eventBus._on("optionalcontentconfig", evt => pdfViewer.optionalContentConfigPromise = evt.promise, opts);
eventBus._on("switchscrollmode", evt => pdfViewer.scrollMode = evt.mode, opts);
eventBus._on("scrollmodechanged", onViewerModesChanged.bind(this, "scrollMode"), opts);
eventBus._on("switchspreadmode", evt => pdfViewer.spreadMode = evt.mode, opts);
eventBus._on("spreadmodechanged", onViewerModesChanged.bind(this, "spreadMode"), opts);
eventBus._on("imagealttextsettings", onImageAltTextSettings.bind(this), opts);
eventBus._on("documentproperties", () => pdfDocumentProperties?.open(), opts);
eventBus._on("findfromurlhash", onFindFromUrlHash.bind(this), opts);
eventBus._on("updatefindmatchescount", onUpdateFindMatchesCount.bind(this), opts);
eventBus._on("updatefindcontrolstate", onUpdateFindControlState.bind(this), opts);
eventBus._on("fileinputchange", onFileInputChange.bind(this), opts);
eventBus._on("openfile", onOpenFile.bind(this), opts);
},
bindWindowEvents() {
if (this._windowAbortController) {
return;
}
this._windowAbortController = new AbortController();
const {
eventBus,
appConfig: {
mainContainer
},
pdfViewer,
_windowAbortController: {
signal
}
} = this;
if (typeof AbortSignal.any === "function") {
this._touchManager = new touch_manager.TouchManager({
container: window,
isPinchingDisabled: () => pdfViewer.isInPresentationMode,
isPinchingStopped: () => this.overlayManager?.active,
onPinching: this.touchPinchCallback.bind(this),
onPinchEnd: this.touchPinchEndCallback.bind(this),
signal
});
}
function addWindowResolutionChange(evt = null) {
if (evt) {
pdfViewer.refresh();
}
const mediaQueryList = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`);
mediaQueryList.addEventListener("change", addWindowResolutionChange, {
once: true,
signal
});
}
addWindowResolutionChange();
const viewerContainer = document.getElementById("viewerContainer");
viewerContainer?.addEventListener("wheel", onWheel.bind(this), {
passive: false,
signal
});
window.addEventListener("wheel", onWheel.bind(this), {
passive: false,
signal
});
window.addEventListener("click", onClick.bind(this), {
signal
});
window.addEventListener("keydown", onKeyDown.bind(this), {
signal
});
window.addEventListener("keyup", onKeyUp.bind(this), {
signal
});
if (viewerContainer) {
let resizeTimeout;
let previousWidth = mainContainer.clientWidth;
let previousHeight = mainContainer.clientHeight;
this.resizeObserver = new ResizeObserver(entries => {
const newWidth = mainContainer.clientWidth;
const newHeight = mainContainer.clientHeight;
if (newWidth !== previousWidth || newHeight !== previousHeight) {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
if (mainContainer.offsetParent) {
eventBus.dispatch("resize", {
source: mainContainer
});
}
}, 50);
previousWidth = newWidth;
previousHeight = newHeight;
}
});
this.resizeObserver.observe(mainContainer);
} else {
window.addEventListener("resize", () => eventBus.dispatch("resize", {
source: window
}), {
signal
});
}
window.addEventListener("hashchange", () => {
eventBus.dispatch("hashchange", {
source: window,
hash: document.location.hash.substring(1)
});
}, {
signal
});
window.addEventListener("beforeprint", () => eventBus.dispatch("beforeprint", {
source: window
}), {
signal
});
window.addEventListener("afterprint", () => eventBus.dispatch("afterprint", {
source: window
}), {
signal
});
window.addEventListener("updatefromsandbox", evt => {
eventBus.dispatch("updatefromsandbox", {
source: window,
detail: evt.detail
});
}, {
signal
});
if (!("onscrollend" in document.documentElement)) {
return;
}
({
scrollTop: this._lastScrollTop,
scrollLeft: this._lastScrollLeft
} = mainContainer);
const scrollend = () => {
({
scrollTop: this._lastScrollTop,
scrollLeft: this._lastScrollLeft
} = mainContainer);
this._isScrolling = false;
mainContainer.addEventListener("scroll", scroll, {
passive: true,
signal
});
mainContainer.removeEventListener("scrollend", scrollend);
mainContainer.removeEventListener("blur", scrollend);
};
const scroll = () => {
if (this._isCtrlKeyDown) {
return;
}
if (this._lastScrollTop === mainContainer.scrollTop && this._lastScrollLeft === mainContainer.scrollLeft) {
return;
}
mainContainer.removeEventListener("scroll", scroll);
this._isScrolling = true;
mainContainer.addEventListener("scrollend", scrollend, {
signal
});
mainContainer.addEventListener("blur", scrollend, {
signal
});
};
mainContainer.addEventListener("scroll", scroll, {
passive: true,
signal
});
},
unbindEvents() {
this._eventBusAbortController?.abort();
this._eventBusAbortController = null;
},
unbindWindowEvents() {
this._windowAbortController?.abort();
this._windowAbortController = null;
this._touchManager = null;
},
async testingClose() {
this.unbindEvents();
this.unbindWindowEvents();
this._globalAbortController?.abort();
this._globalAbortController = null;
this.findBar?.close();
await Promise.all([this.l10n?.destroy(), this.close()]);
},
_accumulateTicks(ticks, prop) {
if (this[prop] > 0 && ticks < 0 || this[prop] < 0 && ticks > 0) {
this[prop] = 0;
}
this[prop] += ticks;
const wholeTicks = Math.trunc(this[prop]);
this[prop] -= wholeTicks;
return wholeTicks;
},
_accumulateFactor(previousScale, factor, prop) {
if (factor === 1) {
return 1;
}
if (this[prop] > 1 && factor < 1 || this[prop] < 1 && factor > 1) {
this[prop] = 1;
}
const newFactor = Math.floor(previousScale * factor * this[prop] * 100) / (100 * previousScale);
this[prop] = factor / newFactor;
return newFactor;
},
_unblockDocumentLoadEvent() {
document.blockUnblockOnload?.(false);
this._unblockDocumentLoadEvent = () => {};
},
get scriptingReady() {
return this.pdfScriptingManager.ready;
}
};
initCom(PDFViewerApplication);
{
PDFPrintServiceFactory.initGlobals(PDFViewerApplication);
}
{
const HOSTED_VIEWER_ORIGINS = ["null", "http://mozilla.github.io", "https://mozilla.github.io"];
var validateFileURL = function (file) {
if (!file) {
return;
}
try {
const viewerOrigin = new URL(window.location.href).origin || "null";
if (HOSTED_VIEWER_ORIGINS.includes(viewerOrigin)) {
return;
}
const fileOrigin = new URL(file, window.location.href).origin;
if (fileOrigin !== viewerOrigin) {
throw new Error("file origin does not match viewer's");
}
} catch (ex) {
if (PDFViewerApplication.onError) {
PDFViewerApplication.onError(ex);
}
PDFViewerApplication._documentError("pdfjs-loading-error", {
message: ex.message
});
throw ex;
}
};
var onFileInputChange = function (evt) {
if (this.pdfViewer?.isInPresentationMode) {
return;
}
const file = evt.fileInput.files[0];
this.open({
url: URL.createObjectURL(file),
originalUrl: file.name
});
};
var onOpenFile = function (evt) {
this._openFileInput?.click();
};
}
function onPageRender({
pageNumber
}) {
if (pageNumber === this.page) {
this.toolbar?.updateLoadingIndicatorState(true);
}
}
function onPageRendered({
pageNumber,
error
}) {
if (pageNumber === this.page) {
this.toolbar?.updateLoadingIndicatorState(false);
}
if (this.pdfSidebar?.visibleView === SidebarView.THUMBS) {
const pageView = this.pdfViewer.getPageView(pageNumber - 1);
const thumbnailView = this.pdfThumbnailViewer?.getThumbnail(pageNumber - 1);
if (pageView) {
thumbnailView?.setImage(pageView);
}
}
if (error) {
this._otherError("pdfjs-rendering-error", error);
}
}
function onPageMode({
mode
}) {
let view;
switch (mode) {
case "thumbs":
view = SidebarView.THUMBS;
break;
case "bookmarks":
case "outline":
view = SidebarView.OUTLINE;
break;
case "attachments":
view = SidebarView.ATTACHMENTS;
break;
case "layers":
view = SidebarView.LAYERS;
break;
case "none":
view = SidebarView.NONE;
break;
default:
ngx_console.NgxConsole.error('Invalid "pagemode" hash parameter: ' + mode);
return;
}
this.pdfSidebar?.switchView(view, true);
}
function onNamedAction(evt) {
switch (evt.action) {
case "GoToPage":
this.appConfig.toolbar?.pageNumber.select();
break;
case "Find":
if (!this.supportsIntegratedFind) {
this.findBar?.toggle();
}
break;
case "Print":
this.triggerPrinting();
break;
case "SaveAs":
this.downloadOrSave();
break;
}
}
function onSidebarViewChanged({
view
}) {
this.pdfRenderingQueue.isThumbnailViewEnabled = view === SidebarView.THUMBS;
if (this.isInitialViewSet) {
this.store?.set("sidebarView", view).catch(() => {});
}
}
function onUpdateViewarea({
location
}) {
if (this.isInitialViewSet) {
const settings = {};
if (location.pageNumber !== undefined || location.pageNumber !== null) {
settings.page = location.pageNumber;
}
if (location.scale) {
settings.zoom = location.scale;
}
if (location.left) {
settings.scrollLeft = location.left;
}
if (location.top) {
settings.scrollTop = location.top;
}
if (location.rotation !== undefined || location.rotation !== null) {
settings.rotation = location.rotation;
}
PDFViewerApplication.store?.setMultiple(settings).catch(() => {});
}
if (this.appConfig.secondaryToolbar) {
this.appConfig.secondaryToolbar.viewBookmarkButton.href = this.pdfLinkService.getAnchorUrl(location.pdfOpenParams);
}
}
function onViewerModesChanged(name, evt) {
if (this.isInitialViewSet && !this.pdfViewer.isInPresentationMode) {
this.store?.set(name, evt.mode).catch(() => {});
}
}
function onResize() {
const {
pdfDocument,
pdfViewer,
pdfRenderingQueue
} = this;
if (pdfRenderingQueue.printing && window.matchMedia("print").matches) {
return;
}
if (!pdfDocument) {
return;
}
const currentScaleValue = pdfViewer.currentScaleValue;
if (currentScaleValue === "auto" || currentScaleValue === "page-fit" || currentScaleValue === "page-width") {
pdfViewer.currentScaleValue = currentScaleValue;
}
pdfViewer.update();
}
function onHashchange(evt) {
const hash = evt.hash;
if (!hash) {
return;
}
if (!this.isInitialViewSet) {
this.initialBookmark = hash;
} else if (!this.pdfHistory?.popStateInProgress) {
if (this.pdfLinkService.setHash) {
this.pdfLinkService.setHash(hash);
}
}
}
function onPageNumberChanged(evt) {
const {
pdfViewer
} = this;
if (evt.value !== "") {
this.pdfLinkService.goToPage(evt.value);
}
if (evt.value !== pdfViewer.currentPageNumber.toString() && evt.value !== pdfViewer.currentPageLabel) {
this.toolbar?.setPageNumber(pdfViewer.currentPageNumber, pdfViewer.currentPageLabel);
}
}
function onImageAltTextSettings() {
this.imageAltTextSettings?.open({
enableGuessAltText: AppOptions.get("enableGuessAltText"),
enableNewAltTextWhenAddingImage: AppOptions.get("enableNewAltTextWhenAddingImage")
});
}
function onFindFromUrlHash(evt) {
this.eventBus.dispatch("find", {
source: evt.source,
type: "",
query: evt.query,
caseSensitive: false,
entireWord: false,
highlightAll: true,
findPrevious: false,
matchDiacritics: true
});
}
function onUpdateFindMatchesCount({
matchesCount
}) {
if (this.supportsIntegratedFind) {
this.externalServices.updateFindMatchesCount(matchesCount);
} else {
this.findBar?.updateResultsCount(matchesCount);
}
}
function onUpdateFindControlState({
state,
previous,
entireWord,
matchesCount,
rawQuery
}) {
if (this.supportsIntegratedFind) {
this.externalServices.updateFindControlState({
result: state,
findPrevious: previous,
entireWord,
matchesCount,
rawQuery
});
} else {
this.findBar?.updateUIState(state, previous, matchesCount);
}
}
function onScaleChanging(evt) {
this.toolbar?.setPageScale(evt.presetValue, evt.scale);
this.pdfViewer.update(evt.noScroll);
}
function onRotationChanging(evt) {
if (this.pdfThumbnailViewer) {
this.pdfThumbnailViewer.pagesRotation = evt.pagesRotation;
}
this.forceRendering();
this.pdfViewer.currentPageNumber = evt.pageNumber;
}
function onPageChanging({
pageNumber,
pageLabel
}) {
this.toolbar?.setPageNumber(pageNumber, pageLabel);
this.secondaryToolbar?.setPageNumber(pageNumber);
if (this.pdfSidebar?.visibleView === SidebarView.THUMBS) {
this.pdfThumbnailViewer?.scrollThumbnailIntoView(pageNumber);
}
const currentPage = this.pdfViewer.getPageView(pageNumber - 1);
this.toolbar?.updateLoadingIndicatorState(currentPage?.renderingState === RenderingStates.RUNNING);
const pageNumberInput = document.getElementById("pageNumber");
if (pageNumberInput) {
const pageScrollEvent = new CustomEvent("page-change");
pageNumberInput.dispatchEvent(pageScrollEvent);
}
}
function onWheel(evt) {
const element = document.getElementById("viewerContainer");
const hover = element.parentNode.querySelector(":hover");
if (hover !== element) {
return;
}
const {
pdfViewer,
supportsMouseWheelZoomCtrlKey,
supportsMouseWheelZoomMetaKey,
supportsPinchToZoom
} = this;
if (pdfViewer.isInPresentationMode) {
return;
}
const cmd = (evt.ctrlKey ? 1 : 0) | (evt.altKey ? 2 : 0) | (evt.shiftKey ? 4 : 0) | (evt.metaKey ? 8 : 0);
if (PDFViewerApplication?.isKeyIgnored && PDFViewerApplication.isKeyIgnored(cmd, "WHEEL")) {
return;
}
const deltaMode = evt.deltaMode;
let scaleFactor = Math.exp(-evt.deltaY / 100);
const isBuiltInMac = false;
const isPinchToZoom = evt.ctrlKey && !this._isCtrlKeyDown && deltaMode === WheelEvent.DOM_DELTA_PIXEL && evt.deltaX === 0 && (Math.abs(scaleFactor - 1) < 0.05 || isBuiltInMac) && evt.deltaZ === 0;
const origin = [evt.clientX, evt.clientY];
if (isPinchToZoom || evt.ctrlKey && supportsMouseWheelZoomCtrlKey || evt.metaKey && supportsMouseWheelZoomMetaKey) {
evt.preventDefault();
if (this._isScrolling || document.visibilityState === "hidden" || this.overlayManager.active) {
return;
}
if (isPinchToZoom && supportsPinchToZoom) {
scaleFactor = this._accumulateFactor(pdfViewer.currentScale, scaleFactor, "_wheelUnusedFactor");
this.updateZoom(null, scaleFactor, origin);
} else {
const delta = normalizeWheelEventDirection(evt);
let ticks = 0;
if (deltaMode === WheelEvent.DOM_DELTA_LINE || deltaMode === WheelEvent.DOM_DELTA_PAGE) {
ticks = Math.abs(delta) >= 1 ? Math.sign(delta) : this._accumulateTicks(delta, "_wheelUnusedTicks");
} else {
const PIXELS_PER_LINE_SCALE = 30;
ticks = this._accumulateTicks(delta / PIXELS_PER_LINE_SCALE, "_wheelUnusedTicks");
}
this.updateZoom(ticks, null, origin);
}
}
}
function closeSecondaryToolbar(evt) {
if (!this.secondaryToolbar?.isOpen) {
return;
}
const appConfig = this.appConfig;
if (this.pdfViewer.containsElement(evt.target) || appConfig.toolbar?.container.contains(evt.target) && !appConfig.secondaryToolbar?.toggleButton.contains(evt.target)) {
if (evt.target && evt.target.parentElement === appConfig.secondaryToolbar.toggleButton) {
return;
}
if (evt.target && evt.target.parentElement && evt.target.parentElement.parentElement === appConfig.secondaryToolbar.toggleButton) {
return;
}
this.secondaryToolbar.close();
}
}
function closeEditorUndoBar(evt) {
if (!this.editorUndoBar?.isOpen) {
return;
}
if (this.appConfig.secondaryToolbar?.toolbar.contains(evt.target)) {
this.editorUndoBar.hide();
}
}
function onClick(evt) {
closeSecondaryToolbar.call(this, evt);
closeEditorUndoBar.call(this, evt);
}
function onKeyUp(evt) {
if (evt.key === "Control") {
this._isCtrlKeyDown = false;
}
}
function onKeyDown(evt) {
this._isCtrlKeyDown = evt.key === "Control";
if (this.editorUndoBar?.isOpen && evt.keyCode !== 9 && evt.keyCode !== 16 && !((evt.keyCode === 13 || evt.keyCode === 32) && getActiveOrFocusedElement() === this.appConfig.editorUndoBar.undoButton)) {
this.editorUndoBar.hide();
}
if (this.overlayManager.active) {
return;
}
const {
eventBus,
pdfViewer
} = this;
const isViewerInPresentationMode = pdfViewer.isInPresentationMode;
let handled = false,
ensureViewerFocused = false;
const cmd = (evt.ctrlKey ? 1 : 0) | (evt.altKey ? 2 : 0) | (evt.shiftKey ? 4 : 0) | (evt.metaKey ? 8 : 0);
if (PDFViewerApplication.ngxKeyboardManager) {
if (PDFViewerApplication.ngxKeyboardManager.isKeyIgnored.bind(PDFViewerApplication.ngxKeyboardManager)(cmd, evt.keyCode)) {
return;
}
}
if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
switch (evt.keyCode) {
case 70:
if (!this.supportsIntegratedFind && !evt.shiftKey) {
if (AppOptions.get("textLayerMode") !== TextLayerMode.DISABLE) {
let findButtonVisible = false;
const primaryViewFind = this.pdfViewer.viewer.closest("#outerContainer")?.querySelector("#primaryViewFind");
if (primaryViewFind) {
findButtonVisible ||= !primaryViewFind?.classList.contains("dummy-component");
}
const secondaryToolBar = this.pdfViewer.viewer.closest("#outerContainer")?.querySelector("#secondaryToolbarToggle");
if (!secondaryToolBar.hidden) {
const secondaryViewFind = this.pdfViewer.viewer.closest("#outerContainer")?.querySelector("#secondaryViewFind");
if (secondaryViewFind) {
findButtonVisible ||= !secondaryViewFind?.classList.contains("dummy-component");
}
}
if (findButtonVisible) {
this.findBar?.open();
}
}
handled = true;
}
break;
case 71:
if (!this.supportsIntegratedFind) {
const {
state
} = this.findController;
if (state) {
const newState = {
source: window,
type: "again",
findPrevious: cmd === 5 || cmd === 12
};
eventBus.dispatch("find", {
...state,
...newState
});
}
handled = true;
}
break;
case 61:
case 107:
case 187:
case 171:
this.zoomIn();
handled = true;
break;
case 173:
case 109:
case 189:
this.zoomOut();
handled = true;
break;
case 48:
case 96:
if (!isViewerInPresentationMode) {
setTimeout(() => {
this.zoomReset();
});
handled = false;
}
break;
case 38:
if (isViewerInPresentationMode || this.page > 1) {
this.page = 1;
handled = true;
ensureViewerFocused = true;
}
break;
case 40:
if (isViewerInPresentationMode || this.page < this.pagesCount) {
this.page = this.pagesCount;
handled = true;
ensureViewerFocused = true;
}
break;
}
}
if (cmd === 1 || cmd === 8) {
switch (evt.keyCode) {
case 83:
eventBus.dispatch("download", {
source: window
});
handled = true;
break;
case 79:
{
eventBus.dispatch("openfile", {
source: window
});
handled = true;
}
break;
}
}
if (cmd === 3 || cmd === 10) {
switch (evt.keyCode) {
case 80:
this.requestPresentationMode();
handled = true;
this.externalServices.reportTelemetry({
type: "buttons",
data: {
id: "presentationModeKeyboard"
}
});
break;
case 71:
if (this.appConfig.toolbar) {
this.appConfig.toolbar.pageNumber.select();
handled = true;
}
break;
}
}
if (handled) {
if (ensureViewerFocused && !isViewerInPresentationMode) {
pdfViewer.focus();
}
evt.preventDefault();
return;
}
const curElement = getActiveOrFocusedElement();
const curElementTagName = curElement?.tagName.toUpperCase();
if (curElementTagName === "INPUT" || curElementTagName === "TEXTAREA" || curElementTagName === "SELECT" || curElementTagName === "BUTTON" && (evt.keyCode === 13 || evt.keyCode === 32) || curElement?.isContentEditable) {
if (evt.keyCode !== 27) {
return;
}
}
if (cmd === 0) {
let turnPage = 0,
turnOnlyIfPageFit = false;
switch (evt.keyCode) {
case 38:
if (this.supportsCaretBrowsingMode) {
this.moveCaret(true, false);
handled = true;
break;
}
case 33:
if (pdfViewer.isVerticalScrollbarEnabled) {
turnOnlyIfPageFit = true;
}
turnPage = -1;
break;
case 8:
if (!isViewerInPresentationMode) {
turnOnlyIfPageFit = true;
}
turnPage = -1;
break;
case 37:
if (this.supportsCaretBrowsingMode) {
return;
}
if (pdfViewer.isHorizontalScrollbarEnabled) {
turnOnlyIfPageFit = true;
}
case 75:
case 80:
turnPage = -1;
break;
case 27:
if (this.secondaryToolbar?.isOpen) {
this.secondaryToolbar.close();
handled = true;
}
if (!this.supportsIntegratedFind && this.findBar?.opened) {
this.findBar.close();
handled = true;
}
break;
case 40:
if (this.supportsCaretBrowsingMode) {
this.moveCaret(false, false);
handled = true;
break;
}
case 34:
if (pdfViewer.isVerticalScrollbarEnabled) {
turnOnlyIfPageFit = true;
}
turnPage = 1;
break;
case 13:
case 32:
if (!isViewerInPresentationMode) {
turnOnlyIfPageFit = true;
}
turnPage = 1;
break;
case 39:
if (this.supportsCaretBrowsingMode) {
return;
}
if (pdfViewer.isHorizontalScrollbarEnabled) {
turnOnlyIfPageFit = true;
}
case 74:
case 78:
turnPage = 1;
break;
case 36:
if (isViewerInPresentationMode || this.page > 1) {
this.page = 1;
handled = true;
ensureViewerFocused = true;
}
break;
case 35:
if (isViewerInPresentationMode || this.page < this.pagesCount) {
this.page = this.pagesCount;
handled = true;
ensureViewerFocused = true;
}
break;
case 83:
this.pdfCursorTools?.switchTool(CursorTool.SELECT);
break;
case 72:
this.pdfCursorTools?.switchTool(CursorTool.HAND);
break;
case 82:
this.rotatePages(90);
break;
case 115:
this.pdfSidebar?.toggle();
break;
}
if (turnPage !== 0 && (!turnOnlyIfPageFit || pdfViewer.currentScaleValue === "page-fit")) {
const {
mainContainer
} = this.appConfig;
if (viewerIsAllowedToCaptureKeyEvent(mainContainer)) {
if (turnPage > 0) {
pdfViewer.nextPage();
} else {
pdfViewer.previousPage();
}
}
handled = true;
}
}
if (cmd === 4) {
switch (evt.keyCode) {
case 13:
case 32:
if (!isViewerInPresentationMode && pdfViewer.currentScaleValue !== "page-fit") {
break;
}
pdfViewer.previousPage();
handled = true;
break;
case 38:
this.moveCaret(true, true);
handled = true;
break;
case 40:
this.moveCaret(false, true);
handled = true;
break;
case 82:
this.rotatePages(-90);
break;
}
}
if (ensureViewerFocused && !pdfViewer.containsElement(curElement)) {
pdfViewer.focus();
}
if (handled) {
evt.preventDefault();
}
}
function viewerIsAllowedToCaptureKeyEvent(mainContainer) {
let elementInFocus = document.activeElement;
const ngxExtendedPdfViewer = mainContainer.closest('ngx-extended-pdf-viewer');
while (elementInFocus && elementInFocus !== document.body) {
if (elementInFocus === ngxExtendedPdfViewer) {
return true;
}
if (elementInFocus.tabIndex !== -1) {
return false;
}
elementInFocus = elementInFocus.parentElement;
}
return true;
}
function beforeUnload(evt) {
evt.preventDefault();
evt.returnValue = "";
return false;
}
PDFViewerApplication.printPdf = printPdf;
PDFViewerApplication.PDFPrintServiceFactory = PDFPrintServiceFactory;
PDFViewerApplication.ngxConsole = new ngx_console.NgxConsole();
const ServiceWorkerOptions = {
showUnverifiedSignatures: false
};
PDFViewerApplication.serviceWorkerOptions = ServiceWorkerOptions;
;// ./web/viewer.js
const pdfjsVersion = "4.10.708";
const pdfjsBuild = "f47106c3e";
const AppConstants = {
LinkTarget: LinkTarget,
RenderingStates: RenderingStates,
ScrollMode: ScrollMode,
SpreadMode: SpreadMode
};
if (!HTMLCollection.prototype[Symbol.iterator]) {
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
}
(function () {
if (typeof window.CustomEvent === "function") {
return;
}
function CustomEvent(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: null
};
const evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
window.CustomEvent = CustomEvent;
})();
function getViewerConfiguration() {
return {
appContainer: document.getElementsByClassName("body")[0],
principalContainer: document.getElementById("mainContainer"),
mainContainer: document.getElementById("viewerContainer"),
viewerContainer: document.getElementById("viewer"),
toolbar: {
container: document.getElementById("toolbarContainer"),
numPages: document.getElementById("numPages"),
pageNumber: document.getElementById("pageNumber"),
scaleSelect: document.getElementById("scaleSelect"),
customScaleOption: document.getElementById("customScaleOption"),
previous: document.getElementById("previous"),
next: document.getElementById("next"),
zoomIn: document.getElementById("primaryZoomIn") ?? document.getElementById("zoomInButton"),
zoomOut: document.getElementById("primaryZoomOut") ?? document.getElementById("zoomOutButton"),
print: document.getElementById("printButton"),
editorFreeTextButton: document.getElementById("primaryEditorFreeText") ?? document.getElementById("editorFreeTextButton"),
editorFreeTextParamsToolbar: document.getElementById("editorFreeTextParamsToolbar"),
editorHighlightButton: document.getElementById("primaryEditorHighlight") ?? document.getElementById("editorHighlightButton"),
editorHighlightParamsToolbar: document.getElementById("editorHighlightParamsToolbar"),
editorHighlightColorPicker: document.getElementById("editorHighlightColorPicker"),
editorInkButton: document.getElementById("primaryEditorInk") ?? document.getElementById("editorInkButton"),
editorInkParamsToolbar: document.getElementById("editorInkParamsToolbar"),
editorStampButton: document.getElementById("primaryEditorStamp") ?? document.getElementById("editorStampButton"),
presentationModeButton: document.getElementById("presentationMode"),
editorStampParamsToolbar: document.getElementById("editorStampParamsToolbar"),
download: document.getElementById("downloadButton")
},
secondaryToolbar: {
toolbar: document.getElementById("secondaryToolbar"),
toggleButton: document.getElementById("secondaryToolbarToggle"),
presentationModeButton: document.getElementById("secondaryPresentationMode"),
openFileButton: document.getElementById("secondaryOpenFile"),
printButton: document.getElementById("secondaryPrintButton"),
downloadButton: document.getElementById("secondaryDownload"),
viewBookmarkButton: document.getElementById("viewBookmark"),
firstPageButton: document.getElementById("firstPage"),
lastPageButton: document.getElementById("lastPage"),
pageRotateCwButton: document.getElementById("pageRotateCw"),
pageRotateCcwButton: document.getElementById("pageRotateCcw"),
cursorSelectToolButton: document.getElementById("cursorSelectTool"),
cursorHandToolButton: document.getElementById("cursorHandTool"),
scrollPageButton: document.getElementById("scrollPage"),
scrollVerticalButton: document.getElementById("scrollVertical"),
scrollHorizontalButton: document.getElementById("scrollHorizontal"),
scrollWrappedButton: document.getElementById("scrollWrapped"),
spreadNoneButton: document.getElementById("spreadNone"),
spreadOddButton: document.getElementById("spreadOdd"),
spreadEvenButton: document.getElementById("spreadEven"),
imageAltTextSettingsButton: document.getElementById("imageAltTextSettings"),
imageAltTextSettingsSeparator: document.getElementById("imageAltTextSettingsSeparator"),
documentPropertiesButton: document.getElementById("documentProperties")
},
sidebar: {
outerContainer: document.getElementById("outerContainer"),
sidebarContainer: document.getElementById("sidebarContainer"),
toggleButton: document.getElementById("primarySidebarToggle") ?? document.getElementById("sidebarToggleButton"),
resizer: document.getElementById("sidebarResizer"),
thumbnailButton: document.getElementById("viewThumbnail"),
outlineButton: document.getElementById("viewOutline"),
attachmentsButton: document.getElementById("viewAttachments"),
layersButton: document.getElementById("viewLayers"),
thumbnailView: document.getElementById("thumbnailView"),
outlineView: document.getElementById("outlineView"),
attachmentsView: document.getElementById("attachmentsView"),
layersView: document.getElementById("layersView"),
currentOutlineItemButton: document.getElementById("currentOutlineItem")
},
findBar: {
bar: document.getElementById("findbar"),
toggleButton: document.getElementById("primaryViewFind") ?? document.getElementById("viewFindButton"),
findField: document.getElementById("findInput"),
highlightAllCheckbox: document.getElementById("findHighlightAll"),
caseSensitiveCheckbox: document.getElementById("findMatchCase"),
findMultipleCheckbox: document.getElementById("findMultiple"),
matchRegExpCheckbox: document.getElementById("matchRegExp"),
matchDiacriticsCheckbox: document.getElementById("findMatchDiacritics"),
entireWordCheckbox: document.getElementById("findEntireWord"),
findMsg: document.getElementById("findMsg"),
findResultsCount: document.getElementById("findResultsCount"),
findPreviousButton: document.getElementById("findPreviousButton") ?? document.getElementById("findPrevious"),
findNextButton: document.getElementById("findNextButton") ?? document.getElementById("findNext")
},
passwordOverlay: {
dialog: document.getElementById("passwordDialog"),
label: document.getElementById("passwordText"),
input: document.getElementById("password"),
submitButton: document.getElementById("passwordSubmit"),
cancelButton: document.getElementById("passwordCancel")
},
documentProperties: {
dialog: document.getElementById("documentPropertiesDialog"),
closeButton: document.getElementById("documentPropertiesClose"),
fields: {
fileName: document.getElementById("fileNameField"),
fileSize: document.getElementById("fileSizeField"),
title: document.getElementById("titleField"),
author: document.getElementById("authorField"),
subject: document.getElementById("subjectField"),
keywords: document.getElementById("keywordsField"),
creationDate: document.getElementById("creationDateField"),
modificationDate: document.getElementById("modificationDateField"),
creator: document.getElementById("creatorField"),
producer: document.getElementById("producerField"),
version: document.getElementById("versionField"),
pageCount: document.getElementById("pageCountField"),
pageSize: document.getElementById("pageSizeField"),
linearized: document.getElementById("linearizedField")
}
},
altTextDialog: {
dialog: document.getElementById("altTextDialog"),
optionDescription: document.getElementById("descriptionButton"),
optionDecorative: document.getElementById("decorativeButton"),
textarea: document.getElementById("descriptionTextarea"),
cancelButton: document.getElementById("altTextCancel"),
saveButton: document.getElementById("altTextSave")
},
newAltTextDialog: {
dialog: document.getElementById("newAltTextDialog"),
title: document.getElementById("newAltTextTitle"),
descriptionContainer: document.getElementById("newAltTextDescriptionContainer"),
textarea: document.getElementById("newAltTextDescriptionTextarea"),
disclaimer: document.getElementById("newAltTextDisclaimer"),
learnMore: document.getElementById("newAltTextLearnMore"),
imagePreview: document.getElementById("newAltTextImagePreview"),
createAutomatically: document.getElementById("newAltTextCreateAutomatically"),
createAutomaticallyButton: document.getElementById("newAltTextCreateAutomaticallyButton"),
downloadModel: document.getElementById("newAltTextDownloadModel"),
downloadModelDescription: document.getElementById("newAltTextDownloadModelDescription"),
error: document.getElementById("newAltTextError"),
errorCloseButton: document.getElementById("newAltTextCloseButton"),
cancelButton: document.getElementById("newAltTextCancel"),
notNowButton: document.getElementById("newAltTextNotNow"),
saveButton: document.getElementById("newAltTextSave")
},
altTextSettingsDialog: {
dialog: document.getElementById("altTextSettingsDialog"),
createModelButton: document.getElementById("createModelButton"),
aiModelSettings: document.getElementById("aiModelSettings"),
learnMore: document.getElementById("altTextSettingsLearnMore"),
deleteModelButton: document.getElementById("deleteModelButton"),
downloadModelButton: document.getElementById("downloadModelButton"),
showAltTextDialogButton: document.getElementById("showAltTextDialogButton"),
altTextSettingsCloseButton: document.getElementById("altTextSettingsCloseButton"),
closeButton: document.getElementById("altTextSettingsCloseButton")
},
annotationEditorParams: {
editorFreeTextFontSize: document.getElementById("editorFreeTextFontSize"),
editorFreeTextColor: document.getElementById("editorFreeTextColor"),
editorInkColor: document.getElementById("editorInkColor"),
editorInkThickness: document.getElementById("editorInkThickness"),
editorInkOpacity: document.getElementById("editorInkOpacity"),
editorStampAddImage: document.getElementById("editorStampAddImage"),
editorFreeHighlightThickness: document.getElementById("editorFreeHighlightThickness"),
editorHighlightShowAll: document.getElementById("editorHighlightShowAll")
},
editorUndoBar: {
container: document.getElementById("editorUndoBar"),
message: document.getElementById("editorUndoBarMessage"),
undoButton: document.getElementById("editorUndoBarUndoButton"),
closeButton: document.getElementById("editorUndoBarCloseButton")
}
};
}
function webViewerLoad(cspPolicyService) {
const config = getViewerConfiguration();
const event = new CustomEvent("webviewerloaded", {
bubbles: true,
cancelable: true,
detail: {
source: window
}
});
try {
parent.document.dispatchEvent(event);
} catch (ex) {
NgxConsole.error("webviewerloaded:", ex);
document.dispatchEvent(event);
}
config.cspPolicyService = cspPolicyService;
PDFViewerApplication.run(config);
}
document.blockUnblockOnload?.(true);
if (globalThis.STANDALONE_VIEWER) {
if (document.readyState === "interactive" || document.readyState === "complete") {
webViewerLoad();
} else {
document.addEventListener("DOMContentLoaded", webViewerLoad, true);
}
}
const viewer_event = new CustomEvent("ngxViewerFileHasBeenLoaded", {
detail: {
PDFViewerApplication: PDFViewerApplication,
PDFViewerApplicationConstants: AppConstants,
PDFViewerApplicationOptions: AppOptions,
webViewerLoad
}
});
document.dispatchEvent(viewer_event);
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ __webpack_require__(276);
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __webpack_require__(909);
/******/ var __webpack_exports__AbortException = __webpack_exports__.AbortException;
/******/ var __webpack_exports__AnnotationEditorLayer = __webpack_exports__.AnnotationEditorLayer;
/******/ var __webpack_exports__AnnotationEditorParamsType = __webpack_exports__.AnnotationEditorParamsType;
/******/ var __webpack_exports__AnnotationEditorType = __webpack_exports__.AnnotationEditorType;
/******/ var __webpack_exports__AnnotationEditorUIManager = __webpack_exports__.AnnotationEditorUIManager;
/******/ var __webpack_exports__AnnotationLayer = __webpack_exports__.AnnotationLayer;
/******/ var __webpack_exports__AnnotationMode = __webpack_exports__.AnnotationMode;
/******/ var __webpack_exports__ColorPicker = __webpack_exports__.ColorPicker;
/******/ var __webpack_exports__DOMSVGFactory = __webpack_exports__.DOMSVGFactory;
/******/ var __webpack_exports__DrawLayer = __webpack_exports__.DrawLayer;
/******/ var __webpack_exports__FeatureTest = __webpack_exports__.FeatureTest;
/******/ var __webpack_exports__GlobalWorkerOptions = __webpack_exports__.GlobalWorkerOptions;
/******/ var __webpack_exports__ImageKind = __webpack_exports__.ImageKind;
/******/ var __webpack_exports__InvalidPDFException = __webpack_exports__.InvalidPDFException;
/******/ var __webpack_exports__MissingPDFException = __webpack_exports__.MissingPDFException;
/******/ var __webpack_exports__OPS = __webpack_exports__.OPS;
/******/ var __webpack_exports__OutputScale = __webpack_exports__.OutputScale;
/******/ var __webpack_exports__PDFDataRangeTransport = __webpack_exports__.PDFDataRangeTransport;
/******/ var __webpack_exports__PDFDateString = __webpack_exports__.PDFDateString;
/******/ var __webpack_exports__PDFWorker = __webpack_exports__.PDFWorker;
/******/ var __webpack_exports__PasswordResponses = __webpack_exports__.PasswordResponses;
/******/ var __webpack_exports__PermissionFlag = __webpack_exports__.PermissionFlag;
/******/ var __webpack_exports__PixelsPerInch = __webpack_exports__.PixelsPerInch;
/******/ var __webpack_exports__RenderingCancelledException = __webpack_exports__.RenderingCancelledException;
/******/ var __webpack_exports__TextLayer = __webpack_exports__.TextLayer;
/******/ var __webpack_exports__TouchManager = __webpack_exports__.TouchManager;
/******/ var __webpack_exports__UnexpectedResponseException = __webpack_exports__.UnexpectedResponseException;
/******/ var __webpack_exports__Util = __webpack_exports__.Util;
/******/ var __webpack_exports__VerbosityLevel = __webpack_exports__.VerbosityLevel;
/******/ var __webpack_exports__XfaLayer = __webpack_exports__.XfaLayer;
/******/ var __webpack_exports__build = __webpack_exports__.build;
/******/ var __webpack_exports__createValidAbsoluteUrl = __webpack_exports__.createValidAbsoluteUrl;
/******/ var __webpack_exports__fetchData = __webpack_exports__.fetchData;
/******/ var __webpack_exports__getDocument = __webpack_exports__.getDocument;
/******/ var __webpack_exports__getFilenameFromUrl = __webpack_exports__.getFilenameFromUrl;
/******/ var __webpack_exports__getPdfFilenameFromUrl = __webpack_exports__.getPdfFilenameFromUrl;
/******/ var __webpack_exports__getXfaPageViewport = __webpack_exports__.getXfaPageViewport;
/******/ var __webpack_exports__isDataScheme = __webpack_exports__.isDataScheme;
/******/ var __webpack_exports__isPdfFile = __webpack_exports__.isPdfFile;
/******/ var __webpack_exports__noContextMenu = __webpack_exports__.noContextMenu;
/******/ var __webpack_exports__normalizeUnicode = __webpack_exports__.normalizeUnicode;
/******/ var __webpack_exports__setLayerDimensions = __webpack_exports__.setLayerDimensions;
/******/ var __webpack_exports__shadow = __webpack_exports__.shadow;
/******/ var __webpack_exports__stopEvent = __webpack_exports__.stopEvent;
/******/ var __webpack_exports__version = __webpack_exports__.version;
/******/ export { __webpack_exports__AbortException as AbortException, __webpack_exports__AnnotationEditorLayer as AnnotationEditorLayer, __webpack_exports__AnnotationEditorParamsType as AnnotationEditorParamsType, __webpack_exports__AnnotationEditorType as AnnotationEditorType, __webpack_exports__AnnotationEditorUIManager as AnnotationEditorUIManager, __webpack_exports__AnnotationLayer as AnnotationLayer, __webpack_exports__AnnotationMode as AnnotationMode, __webpack_exports__ColorPicker as ColorPicker, __webpack_exports__DOMSVGFactory as DOMSVGFactory, __webpack_exports__DrawLayer as DrawLayer, __webpack_exports__FeatureTest as FeatureTest, __webpack_exports__GlobalWorkerOptions as GlobalWorkerOptions, __webpack_exports__ImageKind as ImageKind, __webpack_exports__InvalidPDFException as InvalidPDFException, __webpack_exports__MissingPDFException as MissingPDFException, __webpack_exports__OPS as OPS, __webpack_exports__OutputScale as OutputScale, __webpack_exports__PDFDataRangeTransport as PDFDataRangeTransport, __webpack_exports__PDFDateString as PDFDateString, __webpack_exports__PDFWorker as PDFWorker, __webpack_exports__PasswordResponses as PasswordResponses, __webpack_exports__PermissionFlag as PermissionFlag, __webpack_exports__PixelsPerInch as PixelsPerInch, __webpack_exports__RenderingCancelledException as RenderingCancelledException, __webpack_exports__TextLayer as TextLayer, __webpack_exports__TouchManager as TouchManager, __webpack_exports__UnexpectedResponseException as UnexpectedResponseException, __webpack_exports__Util as Util, __webpack_exports__VerbosityLevel as VerbosityLevel, __webpack_exports__XfaLayer as XfaLayer, __webpack_exports__build as build, __webpack_exports__createValidAbsoluteUrl as createValidAbsoluteUrl, __webpack_exports__fetchData as fetchData, __webpack_exports__getDocument as getDocument, __webpack_exports__getFilenameFromUrl as getFilenameFromUrl, __webpack_exports__getPdfFilenameFromUrl as getPdfFilenameFromUrl, __webpack_exports__getXfaPageViewport as getXfaPageViewport, __webpack_exports__isDataScheme as isDataScheme, __webpack_exports__isPdfFile as isPdfFile, __webpack_exports__noContextMenu as noContextMenu, __webpack_exports__normalizeUnicode as normalizeUnicode, __webpack_exports__setLayerDimensions as setLayerDimensions, __webpack_exports__shadow as shadow, __webpack_exports__stopEvent as stopEvent, __webpack_exports__version as version };
/******/