/** * @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 */ /******/ // The require scope /******/ var __webpack_require__ = {}; /******/ /************************************************************************/ /******/ /* 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)) /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = globalThis.pdfjsViewer = {}; // EXPORTS __webpack_require__.d(__webpack_exports__, { AnnotationLayerBuilder: () => (/* reexport */ AnnotationLayerBuilder), DownloadManager: () => (/* reexport */ DownloadManager), EventBus: () => (/* reexport */ EventBus), FindState: () => (/* reexport */ FindState), GenericL10n: () => (/* reexport */ genericl10n_GenericL10n), LinkTarget: () => (/* reexport */ LinkTarget), PDFFindController: () => (/* reexport */ PDFFindController), PDFHistory: () => (/* reexport */ PDFHistory), PDFLinkService: () => (/* reexport */ PDFLinkService), PDFPageView: () => (/* reexport */ PDFPageView), PDFScriptingManager: () => (/* reexport */ PDFScriptingManagerComponents), PDFSinglePageViewer: () => (/* reexport */ PDFSinglePageViewer), PDFViewer: () => (/* reexport */ PDFViewer), ProgressBar: () => (/* reexport */ ProgressBar), RenderingStates: () => (/* reexport */ RenderingStates), ScrollMode: () => (/* reexport */ ScrollMode), SimpleLinkService: () => (/* reexport */ SimpleLinkService), SpreadMode: () => (/* reexport */ SpreadMode), StructTreeLayerBuilder: () => (/* reexport */ StructTreeLayerBuilder), TextLayerBuilder: () => (/* reexport */ TextLayerBuilder), XfaLayerBuilder: () => (/* reexport */ XfaLayerBuilder), parseQueryString: () => (/* reexport */ parseQueryString) }); ;// ./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) { let parent = element.offsetParent; if (!parent) { console.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) { return; } } if (spot) { if (spot.top !== undefined) { offsetY += spot.top; } if (spot.left !== undefined) { offsetX += spot.left; parent.scrollLeft = offsetX; } } parent.scrollTop = offsetY; } 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); } 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; }(); ;// ./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 }) { this._linkService = linkService; this._eventBus = eventBus; this.#updateMatchesCountOnProgress = updateMatchesCountOnProgress; this.onIsPageVisible = null; this.#reset(); 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(); } #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); } 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._scrollMatches || !element) { return; } else if (matchIndex === -1 || matchIndex !== this._selected.matchIdx) { return; } else if (pageIndex === -1 || pageIndex !== this._selected.pageIdx) { return; } this._scrollMatches = false; const spot = { top: MATCH_SCROLL_OFFSET_TOP, left: selectedLeft + MATCH_SCROLL_OFFSET_LEFT }; scrollIntoView(element, spot, true); } #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._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(); } } else if (++this.#visitedPagesCount === this._linkService.pagesCount) { this.#updateUIResultsCount(); } } match(query, pageContent, pageIndex) { const hasDiacritics = this._hasDiacritics[pageIndex]; let isUnicode = false; 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 { caseSensitive, 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 => { console.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) { 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) { console.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() { this._eventBus.dispatch("updatefindmatchescount", { source: this, matchesCount: this.#requestMatchesCount() }); } #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_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 { console.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) { console.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)) { console.error(`PDFLinkService.goToPage: "${val}" is not a valid page.`); return; } if (this.pdfHistory) { this.pdfHistory.pushCurrentPosition(); this.pdfHistory.pushPage(pageNumber); } 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) { console.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 { console.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) {} } ;// ./web/pdfjs.js const { AbortException, AnnotationEditorLayer, AnnotationEditorParamsType, AnnotationEditorType, AnnotationEditorUIManager, AnnotationLayer, AnnotationMode, build, ColorPicker, createValidAbsoluteUrl, DOMSVGFactory, DrawLayer, FeatureTest, fetchData, getDocument, getFilenameFromUrl, getPdfFilenameFromUrl, getXfaPageViewport, GlobalWorkerOptions, ImageKind, InvalidPDFException, isDataScheme, isPdfFile, MissingPDFException, noContextMenu, normalizeUnicode, OPS, OutputScale, PasswordResponses, PDFDataRangeTransport, PDFDateString, PDFWorker, PermissionFlag, PixelsPerInch, RenderingCancelledException, setLayerDimensions, shadow, stopEvent, TextLayer, TouchManager, UnexpectedResponseException, Util, VerbosityLevel, version, XfaLayer } = globalThis.pdfjsLib; ;// ./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 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; } 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/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 = isPdfFile(filename); const contentType = isPdfData ? "application/pdf" : ""; 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 (!createValidAbsoluteUrl(url, "http://example.com")) { console.error(`download - not a valid URL: ${url}`); return; } blobUrl = url + "#pdfjs.action=download"; } download(blobUrl, filename); } } ;// ./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; } } } } 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"); } } ;// ./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); await this.#l10n.translateRoots(); } catch {} } 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 fetchData(url, "text"); return createBundle(lang, text); } static async #getPaths() { try { const { href } = document.querySelector(`link[type="application/l10n"]`); const paths = await 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/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") { console.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") { console.error("PDFHistory.push: " + `"${namedDest}" is not a valid namedDest parameter.`); return; } else if (!Array.isArray(explicitDest)) { console.error("PDFHistory.push: " + `"${explicitDest}" is not a valid explicitDest parameter.`); return; } else if (!this.#isValidPage(pageNumber)) { if (pageNumber !== null || this._destination) { console.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)) { console.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) { 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/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.#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 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 }); 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/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 = { 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: "", 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 }, 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 }, 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: "../build/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], userOpt = options[name]; if (!defaultOpt || !(typeof userOpt === typeof defaultOpt.value || Type[(typeof userOpt).toUpperCase()] & defaultOpt.type)) { continue; } const { kind } = defaultOpt; if (prefs && !(kind & OptionKind.BROWSER || kind & OptionKind.PREFERENCE)) { 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/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 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, eventBus, pageIndex }) { this.findController = findController; 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) { throw new Error("TextHighlighter is already enabled."); } 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) { 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) { console.error("Could not find a matching mapping"); } const match = { begin: { divIdx: i, offset: matchIdx - iIndex } }; 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) { if (matches.length === 0) { return; } const { findController, pageIdx } = this; const { textContentItemsStr, textDivs } = this; const isSelectedPage = pageIdx === findController.selected.pageIdx; const selectedMatchIdx = findController.selected.matchIdx; const highlightAll = findController.state.highlightAll; 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 i0 = selectedMatchIdx, i1 = i0 + 1; if (highlightAll) { i0 = 0; i1 = matches.length; } else if (!isSelectedPage) { return; } let lastDivIdx = -1; let lastOffset = -1; for (let i = i0; i < i1; i++) { const match = matches[i]; const begin = match.begin; if (begin.divIdx === lastDivIdx && begin.offset === lastOffset) { continue; } lastDivIdx = begin.divIdx; lastOffset = begin.offset; const end = match.end; const isSelected = isSelectedPage && i === selectedMatchIdx; const highlightSuffix = isSelected ? " 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, "highlight" + highlightSuffix); } else { selectedLeft = appendTextToDiv(begin.divIdx, begin.offset, infinity.offset, "highlight begin" + highlightSuffix); for (let n0 = begin.divIdx + 1, n1 = end.divIdx; n0 < n1; n0++) { textDivs[n0].className = "highlight middle" + highlightSuffix; } beginText(end, "highlight end" + highlightSuffix); } prevEnd = end; if (isSelected) { findController.scrollMatchIntoView({ element: textDivs[begin.divIdx], selectedLeft, pageIndex: pageIdx, matchIndex: selectedMatchIdx }); } } if (prevEnd) { appendTextToDiv(prevEnd.divIdx, prevEnd.offset, infinity.offset); } } _updateMatches(reset = false) { if (!this.enabled && !reset) { return; } const { findController, 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 (!findController?.highlightMatches || reset) { return; } const pageMatches = findController.pageMatches[pageIdx] || null; const pageMatchesLength = findController.pageMatchesLength[pageIdx] || null; this.matches = this._convertMatches(pageMatches, pageMatchesLength); this._renderMatches(this.matches); } } ;// ./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 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(normalizeUnicode(selection.toString()))); } 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; } 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/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 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 XfaLayer.update(parameters); } this.div = document.createElement("div"); parameters.div = this.div; return XfaLayer.render(parameters); } cancel() { this._cancelled = true; } hide() { if (!this.div) { return; } this.div.hidden = true; } } ;// ./web/pdf_page_view.js const DEFAULT_LAYER_PROPERTIES = { annotationEditorUIManager: null, annotationStorage: null, downloadManager: null, enableScripting: false, fieldObjectsPromise: null, findController: null, hasJSActionsPromise: null, get linkService() { return new SimpleLinkService(); } }; const LAYERS_ORDER = new Map([["canvasWrapper", 0], ["textLayer", 1], ["annotationLayer", 2], ["annotationEditorLayer", 3], ["xfaLayer", 3]]); class PDFPageView { #annotationMode = 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 ?? 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 * 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; } 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 * 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 shadow(this, "_textHighlighter", new TextHighlighter({ pageIndex: this.id - 1, eventBus: this.eventBus, findController: this.#layerProperties.findController })); } #dispatchLayerRendered(name, error) { this.eventBus.dispatch(name, { source: this, pageNumber: this.id, error }); } 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); } } 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); } } 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); } } async #renderTextLayer() { if (!this.textLayer) { return; } let error = null; try { await this.textLayer.render(this.viewport); } catch (ex) { if (ex instanceof AbortException) { return; } console.error("#renderTextLayer:", ex); error = ex; } this.#dispatchLayerRendered("textlayerrendered", error); 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 * 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 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) { console.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 !== 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 === 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(); }; const { 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 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); const canvasWidth = canvas.width = floorToDivide(calcRound(width * outputScale.sx), sfx[0]); const canvasHeight = canvas.height = floorToDivide(calcRound(height * outputScale.sy), sfy[0]); 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"); } }); this.#renderAnnotationEditorLayer(); }, error => { if (!(error instanceof 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/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) { 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/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) { console.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) { console.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 shadow(this, "_pageOpenPending", new Set()); } get _visitedPages() { return 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": console.clear(); break; case "error": console.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": console.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_scripting_manager.component.js class PDFScriptingManagerComponents extends PDFScriptingManager { constructor(options) { if (!options.externalServices) { window.addEventListener("updatefromsandbox", event => { options.eventBus.dispatch("updatefromsandbox", { source: window, detail: event.detail }); }); } options.externalServices ||= { createScripting: () => new GenericScripting(options.sandboxBundleSrc) }; options.docProperties ||= pdfDocument => docProperties(pdfDocument); super(options); } } ;// ./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 RenderingCancelledException) { return; } console.error("renderView:", reason); }); break; } return true; } } ;// ./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(AnnotationEditorType).includes(mode) && mode !== 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 = AnnotationEditorType.NONE; #annotationEditorUIManager = null; #annotationMode = 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; constructor(options) { const viewerVersion = "4.10.38"; if (version !== viewerVersion) { throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`); } this.container = options.container; this.viewer = options.viewer || options.container.firstElementChild; 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.eventBus = options.eventBus; this.linkService = options.linkService || new SimpleLinkService(); this.downloadManager = options.downloadManager || null; this.findController = options.findController || 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 ?? AnnotationMode.ENABLE_FORMS; this.#annotationEditorMode = options.annotationEditorMode ?? 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); } } 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 === 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; } if (!this._setCurrentPageNumber(val, true)) { console.error(`currentPageNumber: "${val}" is not a valid page.`); } } _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.eventBus.dispatch("pagechanging", { source: this, pageNumber: val, pageLabel: this._pageLabels?.[val - 1] ?? null, previous }); if (resetCurrentPageView) { this.#resetCurrentPageView(); } return true; } 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)) { console.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 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 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(PermissionFlag.COPY) && this.#textLayerMode === TextLayerMode.ENABLE) { params.textLayerMode = TextLayerMode.ENABLE_PERMISSIONS; } if (!permissions.includes(PermissionFlag.MODIFY_CONTENTS)) { params.annotationEditorMode = AnnotationEditorType.DISABLE; } if (!permissions.includes(PermissionFlag.MODIFY_ANNOTATIONS) && !permissions.includes(PermissionFlag.FILL_INTERACTIVE_FORMS) && this.#annotationMode === AnnotationMode.ENABLE_FORMS) { params.annotationMode = 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) { 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 => { console.warn(`Something goes wrong when extracting the text: ${reason.message}`); }).finally(() => { this.#getAllTextInProgress = false; this.#interruptCopyCondition = false; ac.abort(); classList.remove("copyAll"); }); stopEvent(event); } } setDocument(pdfDocument) { if (this.pdfDocument) { this.eventBus.dispatch("pagesdestroy", { source: this }); this._cancelRendering(); this._resetView(); this.findController?.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) { console.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 !== AnnotationEditorType.DISABLE) { const mode = annotationEditorMode; if (pdfDocument.isPureXfa) { console.warn("Warning: XFA-editing is not implemented."); } else if (isValidAnnotationEditorMode(mode)) { this.#annotationEditorUIManager = new 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 !== AnnotationEditorType.NONE) { if (mode === AnnotationEditorType.STAMP) { this.#mlManager?.loadModel("altText"); } this.#annotationEditorUIManager.updateMode(mode); } } else { console.error(`Invalid AnnotationEditor mode: ${mode}`); } } const viewerElement = this._scrollMode === ScrollMode.PAGE ? null : viewer; const scale = this.currentScale; const viewport = firstPdfPage.getViewport({ scale: scale * 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._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; } 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 => { console.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; } } }); 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 => { console.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; console.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; this.#buffer = new PDFPageViewBuffer(DEFAULT_CACHE_SIZE); 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(); } #scrollIntoView(pageView, pageSpot = null) { 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); 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 }) { this._currentScaleValue = newValue.toString(); if (this.#isSameScale(newScale)) { if (preset) { this.eventBus.dispatch("scalechanging", { source: this, scale: newScale, presetValue: newValue }); } return; } this.viewer.style.setProperty("--scale-factor", newScale * 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); } const previousScale = this._currentScale; 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 }); if (this.defaultRenderingQueue) { this.update(); } } get #pageWidthScaleFactor() { if (this._spreadMode !== SpreadMode.NONE && this._scrollMode !== ScrollMode.HORIZONTAL) { return 2; } return 1; } #setScale(value, options) { let scale = parseFloat(value); 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: console.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) { console.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 / PixelsPerInch.PDF_TO_CSS_UNITS; const pageHeight = (changeOrientation ? pageView.width : pageView.height) / pageView.scale / 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 / PixelsPerInch.PDF_TO_CSS_UNITS; heightScale = (this.container.clientHeight - vPadding) / height / PixelsPerInch.PDF_TO_CSS_UNITS; scale = Math.min(Math.abs(widthScale), Math.abs(heightScale)); break; default: console.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; } } 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() { const visible = this._getVisiblePages(); const visiblePages = visible.views, numVisiblePages = visiblePages.length; if (numVisiblePages === 0) { return; } const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 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; } } this._setCurrentPageNumber(stillFullyVisible ? currentId : visiblePages[0].id); this._updateLocation(visible.first); this.eventBus.dispatch("updateviewarea", { source: this, location: this._location }); } #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) { console.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) { console.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); } } } 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) { 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); } newScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, 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 : 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 === 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 === AnnotationEditorType.NONE || this.#annotationEditorMode === AnnotationEditorType.NONE) { const isEditing = mode !== 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(); } } } ;// ./web/pdf_single_page_viewer.js class PDFSinglePageViewer extends PDFViewer { _resetView() { super._resetView(); this._scrollMode = ScrollMode.PAGE; this._spreadMode = SpreadMode.NONE; } set scrollMode(mode) {} _updateScrollMode() {} set spreadMode(mode) {} _updateSpreadMode() {} } ;// ./web/pdf_viewer.component.js const pdfjsVersion = "4.10.38"; const pdfjsBuild = "f9bea397f"; var __webpack_exports__AnnotationLayerBuilder = __webpack_exports__.AnnotationLayerBuilder; var __webpack_exports__DownloadManager = __webpack_exports__.DownloadManager; var __webpack_exports__EventBus = __webpack_exports__.EventBus; var __webpack_exports__FindState = __webpack_exports__.FindState; var __webpack_exports__GenericL10n = __webpack_exports__.GenericL10n; var __webpack_exports__LinkTarget = __webpack_exports__.LinkTarget; var __webpack_exports__PDFFindController = __webpack_exports__.PDFFindController; var __webpack_exports__PDFHistory = __webpack_exports__.PDFHistory; var __webpack_exports__PDFLinkService = __webpack_exports__.PDFLinkService; var __webpack_exports__PDFPageView = __webpack_exports__.PDFPageView; var __webpack_exports__PDFScriptingManager = __webpack_exports__.PDFScriptingManager; var __webpack_exports__PDFSinglePageViewer = __webpack_exports__.PDFSinglePageViewer; var __webpack_exports__PDFViewer = __webpack_exports__.PDFViewer; var __webpack_exports__ProgressBar = __webpack_exports__.ProgressBar; var __webpack_exports__RenderingStates = __webpack_exports__.RenderingStates; var __webpack_exports__ScrollMode = __webpack_exports__.ScrollMode; var __webpack_exports__SimpleLinkService = __webpack_exports__.SimpleLinkService; var __webpack_exports__SpreadMode = __webpack_exports__.SpreadMode; var __webpack_exports__StructTreeLayerBuilder = __webpack_exports__.StructTreeLayerBuilder; var __webpack_exports__TextLayerBuilder = __webpack_exports__.TextLayerBuilder; var __webpack_exports__XfaLayerBuilder = __webpack_exports__.XfaLayerBuilder; var __webpack_exports__parseQueryString = __webpack_exports__.parseQueryString; export { __webpack_exports__AnnotationLayerBuilder as AnnotationLayerBuilder, __webpack_exports__DownloadManager as DownloadManager, __webpack_exports__EventBus as EventBus, __webpack_exports__FindState as FindState, __webpack_exports__GenericL10n as GenericL10n, __webpack_exports__LinkTarget as LinkTarget, __webpack_exports__PDFFindController as PDFFindController, __webpack_exports__PDFHistory as PDFHistory, __webpack_exports__PDFLinkService as PDFLinkService, __webpack_exports__PDFPageView as PDFPageView, __webpack_exports__PDFScriptingManager as PDFScriptingManager, __webpack_exports__PDFSinglePageViewer as PDFSinglePageViewer, __webpack_exports__PDFViewer as PDFViewer, __webpack_exports__ProgressBar as ProgressBar, __webpack_exports__RenderingStates as RenderingStates, __webpack_exports__ScrollMode as ScrollMode, __webpack_exports__SimpleLinkService as SimpleLinkService, __webpack_exports__SpreadMode as SpreadMode, __webpack_exports__StructTreeLayerBuilder as StructTreeLayerBuilder, __webpack_exports__TextLayerBuilder as TextLayerBuilder, __webpack_exports__XfaLayerBuilder as XfaLayerBuilder, __webpack_exports__parseQueryString as parseQueryString }; //# sourceMappingURL=pdf_viewer.mjs.map