12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041 |
- /* Copyright 2012 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.
- */
- import {
- animationStarted,
- apiPageLayoutToViewerModes,
- apiPageModeToSidebarView,
- AutoPrintRegExp,
- DEFAULT_SCALE_VALUE,
- getActiveOrFocusedElement,
- isValidRotation,
- isValidScrollMode,
- isValidSpreadMode,
- normalizeWheelEventDirection,
- parseQueryString,
- ProgressBar,
- RendererType,
- RenderingStates,
- ScrollMode,
- SidebarView,
- SpreadMode,
- TextLayerMode,
- } from "./ui_utils.js";
- import {
- AnnotationEditorType,
- build,
- createPromiseCapability,
- getDocument,
- getFilenameFromUrl,
- getPdfFilenameFromUrl,
- GlobalWorkerOptions,
- InvalidPDFException,
- isDataScheme,
- isPdfFile,
- loadScript,
- MissingPDFException,
- OPS,
- PDFWorker,
- shadow,
- UnexpectedResponseException,
- version,
- } from "pdfjs-lib";
- import { AppOptions, OptionKind } from "./app_options.js";
- import { AutomationEventBus, EventBus } from "./event_utils.js";
- import { CursorTool, PDFCursorTools } from "./pdf_cursor_tools.js";
- import { LinkTarget, PDFLinkService } from "./pdf_link_service.js";
- import { AnnotationEditorParams } from "./annotation_editor_params.js";
- import { OverlayManager } from "./overlay_manager.js";
- import { PasswordPrompt } from "./password_prompt.js";
- import { PDFAttachmentViewer } from "./pdf_attachment_viewer.js";
- import { PDFDocumentProperties } from "./pdf_document_properties.js";
- import { PDFFindBar } from "./pdf_find_bar.js";
- import { PDFFindController } from "./pdf_find_controller.js";
- import { PDFHistory } from "./pdf_history.js";
- import { PDFLayerViewer } from "./pdf_layer_viewer.js";
- import { PDFOutlineViewer } from "./pdf_outline_viewer.js";
- import { PDFPresentationMode } from "./pdf_presentation_mode.js";
- import { PDFRenderingQueue } from "./pdf_rendering_queue.js";
- import { PDFScriptingManager } from "./pdf_scripting_manager.js";
- import { PDFSidebar } from "./pdf_sidebar.js";
- import { PDFSidebarResizer } from "./pdf_sidebar_resizer.js";
- import { PDFThumbnailViewer } from "./pdf_thumbnail_viewer.js";
- import { PDFViewer } from "./pdf_viewer.js";
- import { SecondaryToolbar } from "./secondary_toolbar.js";
- import { Toolbar } from "./toolbar.js";
- import { ViewHistory } from "./view_history.js";
- const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000; // ms
- const FORCE_PAGES_LOADED_TIMEOUT = 10000; // ms
- const WHEEL_ZOOM_DISABLED_TIMEOUT = 1000; // ms
- const ViewOnLoad = {
- UNKNOWN: -1,
- PREVIOUS: 0, // Default value.
- INITIAL: 1,
- };
- const ViewerCssTheme = {
- AUTOMATIC: 0, // Default value.
- LIGHT: 1,
- DARK: 2,
- };
- class DefaultExternalServices {
- constructor() {
- throw new Error("Cannot initialize DefaultExternalServices.");
- }
- static updateFindControlState(data) {}
- static updateFindMatchesCount(data) {}
- static initPassiveLoading(callbacks) {}
- static reportTelemetry(data) {}
- static createDownloadManager() {
- throw new Error("Not implemented: createDownloadManager");
- }
- static createPreferences() {
- throw new Error("Not implemented: createPreferences");
- }
- static createL10n(options) {
- throw new Error("Not implemented: createL10n");
- }
- static createScripting(options) {
- throw new Error("Not implemented: createScripting");
- }
- static get supportsIntegratedFind() {
- return shadow(this, "supportsIntegratedFind", false);
- }
- static get supportsDocumentFonts() {
- return shadow(this, "supportsDocumentFonts", true);
- }
- static get supportedMouseWheelZoomModifierKeys() {
- return shadow(this, "supportedMouseWheelZoomModifierKeys", {
- ctrlKey: true,
- metaKey: true,
- });
- }
- static get isInAutomation() {
- return shadow(this, "isInAutomation", false);
- }
- static updateEditorStates(data) {
- throw new Error("Not implemented: updateEditorStates");
- }
- }
- const PDFViewerApplication = {
- initialBookmark: document.location.hash.substring(1),
- _initializedCapability: createPromiseCapability(),
- appConfig: null,
- pdfDocument: null,
- pdfLoadingTask: null,
- printService: null,
- /** @type {PDFViewer} */
- pdfViewer: null,
- /** @type {PDFThumbnailViewer} */
- pdfThumbnailViewer: null,
- /** @type {PDFRenderingQueue} */
- pdfRenderingQueue: null,
- /** @type {PDFPresentationMode} */
- pdfPresentationMode: null,
- /** @type {PDFDocumentProperties} */
- pdfDocumentProperties: null,
- /** @type {PDFLinkService} */
- pdfLinkService: null,
- /** @type {PDFHistory} */
- pdfHistory: null,
- /** @type {PDFSidebar} */
- pdfSidebar: null,
- /** @type {PDFSidebarResizer} */
- pdfSidebarResizer: null,
- /** @type {PDFOutlineViewer} */
- pdfOutlineViewer: null,
- /** @type {PDFAttachmentViewer} */
- pdfAttachmentViewer: null,
- /** @type {PDFLayerViewer} */
- pdfLayerViewer: null,
- /** @type {PDFCursorTools} */
- pdfCursorTools: null,
- /** @type {PDFScriptingManager} */
- pdfScriptingManager: null,
- /** @type {ViewHistory} */
- store: null,
- /** @type {DownloadManager} */
- downloadManager: null,
- /** @type {OverlayManager} */
- overlayManager: null,
- /** @type {Preferences} */
- preferences: null,
- /** @type {Toolbar} */
- toolbar: null,
- /** @type {SecondaryToolbar} */
- secondaryToolbar: null,
- /** @type {EventBus} */
- eventBus: null,
- /** @type {IL10n} */
- l10n: null,
- /** @type {AnnotationEditorParams} */
- annotationEditorParams: null,
- isInitialViewSet: false,
- downloadComplete: false,
- isViewerEmbedded: window.parent !== window,
- url: "",
- baseUrl: "",
- _downloadUrl: "",
- externalServices: DefaultExternalServices,
- _boundEvents: Object.create(null),
- documentInfo: null,
- metadata: null,
- _contentDispositionFilename: null,
- _contentLength: null,
- _saveInProgress: false,
- _wheelUnusedTicks: 0,
- _PDFBug: null,
- _hasAnnotationEditors: false,
- _title: document.title,
- _printAnnotationStoragePromise: null,
- // Called once when the document is loaded.
- async initialize(appConfig) {
- this.preferences = this.externalServices.createPreferences();
- this.appConfig = appConfig;
- await this._readPreferences();
- await this._parseHashParameters();
- this._forceCssTheme();
- await this._initializeL10n();
- if (
- this.isViewerEmbedded &&
- AppOptions.get("externalLinkTarget") === LinkTarget.NONE
- ) {
- // Prevent external links from "replacing" the viewer,
- // when it's embedded in e.g. an <iframe> or an <object>.
- AppOptions.set("externalLinkTarget", LinkTarget.TOP);
- }
- await this._initializeViewerComponents();
- // Bind the various event handlers *after* the viewer has been
- // initialized, to prevent errors if an event arrives too soon.
- this.bindEvents();
- this.bindWindowEvents();
- // We can start UI localization now.
- const appContainer = appConfig.appContainer || document.documentElement;
- this.l10n.translate(appContainer).then(() => {
- // Dispatch the 'localized' event on the `eventBus` once the viewer
- // has been fully initialized and translated.
- this.eventBus.dispatch("localized", { source: this });
- });
- this._initializedCapability.resolve();
- },
- /**
- * @private
- */
- async _readPreferences() {
- if (
- typeof PDFJSDev === "undefined" ||
- PDFJSDev.test("!PRODUCTION || GENERIC")
- ) {
- if (AppOptions.get("disablePreferences")) {
- // Give custom implementations of the default viewer a simpler way to
- // opt-out of having the `Preferences` override existing `AppOptions`.
- return;
- }
- if (AppOptions._hasUserOptions()) {
- console.warn(
- "_readPreferences: The Preferences may override manually set AppOptions; " +
- 'please use the "disablePreferences"-option in order to prevent that.'
- );
- }
- }
- try {
- AppOptions.setAll(await this.preferences.getAll());
- } catch (reason) {
- console.error(`_readPreferences: "${reason?.message}".`);
- }
- },
- /**
- * Potentially parse special debugging flags in the hash section of the URL.
- * @private
- */
- async _parseHashParameters() {
- if (!AppOptions.get("pdfBugEnabled")) {
- return;
- }
- const hash = document.location.hash.substring(1);
- if (!hash) {
- return;
- }
- const { mainContainer, viewerContainer } = this.appConfig,
- params = parseQueryString(hash);
- if (params.get("disableworker") === "true") {
- try {
- await loadFakeWorker();
- } catch (ex) {
- console.error(`_parseHashParameters: "${ex.message}".`);
- }
- }
- if (params.has("disablerange")) {
- AppOptions.set("disableRange", params.get("disablerange") === "true");
- }
- if (params.has("disablestream")) {
- AppOptions.set("disableStream", params.get("disablestream") === "true");
- }
- if (params.has("disableautofetch")) {
- AppOptions.set(
- "disableAutoFetch",
- params.get("disableautofetch") === "true"
- );
- }
- if (params.has("disablefontface")) {
- AppOptions.set(
- "disableFontFace",
- params.get("disablefontface") === "true"
- );
- }
- if (params.has("disablehistory")) {
- AppOptions.set("disableHistory", params.get("disablehistory") === "true");
- }
- if (params.has("verbosity")) {
- AppOptions.set("verbosity", params.get("verbosity") | 0);
- }
- if (params.has("textlayer")) {
- switch (params.get("textlayer")) {
- case "off":
- AppOptions.set("textLayerMode", TextLayerMode.DISABLE);
- break;
- case "visible":
- case "shadow":
- case "hover":
- viewerContainer.classList.add(`textLayer-${params.get("textlayer")}`);
- try {
- await loadPDFBug(this);
- this._PDFBug.loadCSS();
- } catch (ex) {
- console.error(`_parseHashParameters: "${ex.message}".`);
- }
- break;
- }
- }
- if (params.has("pdfbug")) {
- AppOptions.set("pdfBug", true);
- AppOptions.set("fontExtraProperties", true);
- const enabled = params.get("pdfbug").split(",");
- try {
- await loadPDFBug(this);
- this._PDFBug.init({ OPS }, mainContainer, enabled);
- } catch (ex) {
- console.error(`_parseHashParameters: "${ex.message}".`);
- }
- }
- // It is not possible to change locale for the (various) extension builds.
- if (
- (typeof PDFJSDev === "undefined" ||
- PDFJSDev.test("!PRODUCTION || GENERIC")) &&
- params.has("locale")
- ) {
- AppOptions.set("locale", params.get("locale"));
- }
- },
- /**
- * @private
- */
- async _initializeL10n() {
- this.l10n = this.externalServices.createL10n(
- typeof PDFJSDev === "undefined" || PDFJSDev.test("!PRODUCTION || GENERIC")
- ? { locale: AppOptions.get("locale") }
- : null
- );
- const dir = await this.l10n.getDirection();
- document.getElementsByTagName("html")[0].dir = dir;
- },
- /**
- * @private
- */
- _forceCssTheme() {
- const cssTheme = AppOptions.get("viewerCssTheme");
- if (
- cssTheme === ViewerCssTheme.AUTOMATIC ||
- !Object.values(ViewerCssTheme).includes(cssTheme)
- ) {
- return;
- }
- try {
- const styleSheet = document.styleSheets[0];
- const cssRules = styleSheet?.cssRules || [];
- for (let i = 0, ii = cssRules.length; i < ii; i++) {
- const rule = cssRules[i];
- if (
- rule instanceof CSSMediaRule &&
- rule.media?.[0] === "(prefers-color-scheme: dark)"
- ) {
- if (cssTheme === ViewerCssTheme.LIGHT) {
- styleSheet.deleteRule(i);
- return;
- }
- // cssTheme === ViewerCssTheme.DARK
- const darkRules =
- /^@media \(prefers-color-scheme: dark\) {\n\s*([\w\s-.,:;/\\{}()]+)\n}$/.exec(
- rule.cssText
- );
- if (darkRules?.[1]) {
- styleSheet.deleteRule(i);
- styleSheet.insertRule(darkRules[1], i);
- }
- return;
- }
- }
- } catch (reason) {
- console.error(`_forceCssTheme: "${reason?.message}".`);
- }
- },
- /**
- * @private
- */
- async _initializeViewerComponents() {
- const { appConfig, externalServices } = this;
- const eventBus = externalServices.isInAutomation
- ? new AutomationEventBus()
- : new EventBus();
- this.eventBus = eventBus;
- this.overlayManager = new OverlayManager();
- const pdfRenderingQueue = new PDFRenderingQueue();
- pdfRenderingQueue.onIdle = this._cleanup.bind(this);
- this.pdfRenderingQueue = pdfRenderingQueue;
- const pdfLinkService = new PDFLinkService({
- eventBus,
- externalLinkTarget: AppOptions.get("externalLinkTarget"),
- externalLinkRel: AppOptions.get("externalLinkRel"),
- ignoreDestinationZoom: AppOptions.get("ignoreDestinationZoom"),
- });
- this.pdfLinkService = pdfLinkService;
- const downloadManager = externalServices.createDownloadManager();
- this.downloadManager = downloadManager;
- const findController = new PDFFindController({
- linkService: pdfLinkService,
- eventBus,
- });
- this.findController = findController;
- const pdfScriptingManager = new PDFScriptingManager({
- eventBus,
- sandboxBundleSrc:
- typeof PDFJSDev === "undefined" ||
- PDFJSDev.test("!PRODUCTION || GENERIC || CHROME")
- ? AppOptions.get("sandboxBundleSrc")
- : null,
- scriptingFactory: externalServices,
- docPropertiesLookup: this._scriptingDocProperties.bind(this),
- });
- this.pdfScriptingManager = pdfScriptingManager;
- const container = appConfig.mainContainer,
- viewer = appConfig.viewerContainer;
- const annotationEditorMode = AppOptions.get("annotationEditorMode");
- const pageColors =
- AppOptions.get("forcePageColors") ||
- window.matchMedia("(forced-colors: active)").matches
- ? {
- background: AppOptions.get("pageColorsBackground"),
- foreground: AppOptions.get("pageColorsForeground"),
- }
- : null;
- this.pdfViewer = new PDFViewer({
- container,
- viewer,
- eventBus,
- renderingQueue: pdfRenderingQueue,
- linkService: pdfLinkService,
- downloadManager,
- findController,
- scriptingManager:
- AppOptions.get("enableScripting") && pdfScriptingManager,
- renderer:
- typeof PDFJSDev === "undefined" ||
- PDFJSDev.test("!PRODUCTION || GENERIC")
- ? AppOptions.get("renderer")
- : null,
- l10n: this.l10n,
- textLayerMode: AppOptions.get("textLayerMode"),
- annotationMode: AppOptions.get("annotationMode"),
- annotationEditorMode,
- imageResourcesPath: AppOptions.get("imageResourcesPath"),
- enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
- useOnlyCssZoom: AppOptions.get("useOnlyCssZoom"),
- isOffscreenCanvasSupported: AppOptions.get("isOffscreenCanvasSupported"),
- maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
- enablePermissions: AppOptions.get("enablePermissions"),
- pageColors,
- });
- pdfRenderingQueue.setViewer(this.pdfViewer);
- pdfLinkService.setViewer(this.pdfViewer);
- pdfScriptingManager.setViewer(this.pdfViewer);
- if (appConfig.sidebar?.thumbnailView) {
- this.pdfThumbnailViewer = new PDFThumbnailViewer({
- container: appConfig.sidebar.thumbnailView,
- eventBus,
- renderingQueue: pdfRenderingQueue,
- linkService: pdfLinkService,
- l10n: this.l10n,
- pageColors,
- });
- pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
- }
- // The browsing history is only enabled when the viewer is standalone,
- // i.e. not when it is embedded in a web page.
- if (!this.isViewerEmbedded && !AppOptions.get("disableHistory")) {
- this.pdfHistory = new PDFHistory({
- linkService: pdfLinkService,
- eventBus,
- });
- pdfLinkService.setHistory(this.pdfHistory);
- }
- if (!this.supportsIntegratedFind && appConfig.findBar) {
- this.findBar = new PDFFindBar(appConfig.findBar, eventBus, this.l10n);
- }
- if (appConfig.annotationEditorParams) {
- if (annotationEditorMode !== AnnotationEditorType.DISABLE) {
- this.annotationEditorParams = new AnnotationEditorParams(
- appConfig.annotationEditorParams,
- eventBus
- );
- } else {
- for (const id of ["editorModeButtons", "editorModeSeparator"]) {
- document.getElementById(id)?.classList.add("hidden");
- }
- }
- }
- if (appConfig.documentProperties) {
- this.pdfDocumentProperties = new PDFDocumentProperties(
- appConfig.documentProperties,
- this.overlayManager,
- eventBus,
- this.l10n,
- /* fileNameLookup = */ () => {
- return this._docFilename;
- }
- );
- }
- this.pdfCursorTools = new PDFCursorTools({
- container,
- eventBus,
- cursorToolOnLoad: AppOptions.get("cursorToolOnLoad"),
- });
- if (appConfig.toolbar) {
- this.toolbar = new Toolbar(appConfig.toolbar, eventBus, this.l10n);
- }
- if (appConfig.secondaryToolbar) {
- this.secondaryToolbar = new SecondaryToolbar(
- appConfig.secondaryToolbar,
- eventBus,
- this.externalServices
- );
- }
- if (this.supportsFullscreen) {
- this.pdfPresentationMode = new PDFPresentationMode({
- container,
- pdfViewer: this.pdfViewer,
- eventBus,
- });
- }
- if (appConfig.passwordOverlay) {
- this.passwordPrompt = new PasswordPrompt(
- appConfig.passwordOverlay,
- this.overlayManager,
- this.l10n,
- this.isViewerEmbedded
- );
- }
- if (appConfig.sidebar?.outlineView) {
- this.pdfOutlineViewer = new PDFOutlineViewer({
- container: appConfig.sidebar.outlineView,
- eventBus,
- linkService: pdfLinkService,
- downloadManager,
- });
- }
- if (appConfig.sidebar?.attachmentsView) {
- this.pdfAttachmentViewer = new PDFAttachmentViewer({
- container: appConfig.sidebar.attachmentsView,
- eventBus,
- downloadManager,
- });
- }
- if (appConfig.sidebar?.layersView) {
- this.pdfLayerViewer = new PDFLayerViewer({
- container: appConfig.sidebar.layersView,
- eventBus,
- l10n: this.l10n,
- });
- }
- if (appConfig.sidebar) {
- this.pdfSidebar = new PDFSidebar({
- elements: appConfig.sidebar,
- pdfViewer: this.pdfViewer,
- pdfThumbnailViewer: this.pdfThumbnailViewer,
- eventBus,
- l10n: this.l10n,
- });
- this.pdfSidebar.onToggled = this.forceRendering.bind(this);
- this.pdfSidebarResizer = new PDFSidebarResizer(
- appConfig.sidebarResizer,
- eventBus,
- this.l10n
- );
- }
- },
- run(config) {
- this.initialize(config).then(webViewerInitialized);
- },
- get initialized() {
- return this._initializedCapability.settled;
- },
- get initializedPromise() {
- return this._initializedCapability.promise;
- },
- zoomIn(steps) {
- if (this.pdfViewer.isInPresentationMode) {
- return;
- }
- this.pdfViewer.increaseScale(steps, {
- delay: AppOptions.get("defaultZoomDelay"),
- });
- },
- zoomOut(steps) {
- if (this.pdfViewer.isInPresentationMode) {
- return;
- }
- this.pdfViewer.decreaseScale(steps, {
- delay: AppOptions.get("defaultZoomDelay"),
- });
- },
- zoomReset() {
- if (this.pdfViewer.isInPresentationMode) {
- return;
- }
- this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
- },
- get pagesCount() {
- return this.pdfDocument ? this.pdfDocument.numPages : 0;
- },
- get page() {
- return this.pdfViewer.currentPageNumber;
- },
- set page(val) {
- this.pdfViewer.currentPageNumber = val;
- },
- get supportsPrinting() {
- return PDFPrintServiceFactory.instance.supportsPrinting;
- },
- get supportsFullscreen() {
- return shadow(this, "supportsFullscreen", document.fullscreenEnabled);
- },
- get supportsIntegratedFind() {
- return this.externalServices.supportsIntegratedFind;
- },
- get supportsDocumentFonts() {
- return this.externalServices.supportsDocumentFonts;
- },
- get loadingBar() {
- const barElement = document.getElementById("loadingBar");
- const bar = barElement ? new ProgressBar(barElement) : null;
- return shadow(this, "loadingBar", bar);
- },
- get supportedMouseWheelZoomModifierKeys() {
- return this.externalServices.supportedMouseWheelZoomModifierKeys;
- },
- initPassiveLoading() {
- if (
- typeof PDFJSDev === "undefined" ||
- !PDFJSDev.test("MOZCENTRAL || CHROME")
- ) {
- throw new Error("Not implemented: initPassiveLoading");
- }
- this.externalServices.initPassiveLoading({
- onOpenWithTransport: (url, length, transport) => {
- this.open(url, { length, range: transport });
- },
- onOpenWithData: (data, contentDispositionFilename) => {
- if (isPdfFile(contentDispositionFilename)) {
- this._contentDispositionFilename = contentDispositionFilename;
- }
- this.open(data);
- },
- onOpenWithURL: (url, length, originalUrl) => {
- const file = originalUrl !== undefined ? { url, originalUrl } : url;
- const args = length !== undefined ? { length } : null;
- this.open(file, args);
- },
- onError: err => {
- this.l10n.get("loading_error").then(msg => {
- this._documentError(msg, err);
- });
- },
- onProgress: (loaded, total) => {
- this.progress(loaded / total);
- },
- });
- },
- setTitleUsingUrl(url = "", downloadUrl = null) {
- this.url = url;
- this.baseUrl = url.split("#")[0];
- if (downloadUrl) {
- this._downloadUrl =
- downloadUrl === url ? this.baseUrl : downloadUrl.split("#")[0];
- }
- if (isDataScheme(url)) {
- this._hideViewBookmark();
- }
- let title = getPdfFilenameFromUrl(url, "");
- if (!title) {
- try {
- title = decodeURIComponent(getFilenameFromUrl(url)) || url;
- } catch (ex) {
- // decodeURIComponent may throw URIError,
- // fall back to using the unprocessed url in that case
- title = url;
- }
- }
- this.setTitle(title);
- },
- setTitle(title = this._title) {
- this._title = title;
- if (this.isViewerEmbedded) {
- // Embedded PDF viewers should not be changing their parent page's title.
- return;
- }
- const editorIndicator =
- this._hasAnnotationEditors && !this.pdfRenderingQueue.printing;
- document.title = `${editorIndicator ? "* " : ""}${title}`;
- },
- get _docFilename() {
- // Use `this.url` instead of `this.baseUrl` to perform filename detection
- // based on the reference fragment as ultimate fallback if needed.
- return this._contentDispositionFilename || getPdfFilenameFromUrl(this.url);
- },
- /**
- * @private
- */
- _hideViewBookmark() {
- const { secondaryToolbar } = this.appConfig;
- // URL does not reflect proper document location - hiding some buttons.
- secondaryToolbar?.viewBookmarkButton.classList.add("hidden");
- // Avoid displaying multiple consecutive separators in the secondaryToolbar.
- if (secondaryToolbar?.presentationModeButton.classList.contains("hidden")) {
- document.getElementById("viewBookmarkSeparator")?.classList.add("hidden");
- }
- },
- /**
- * Closes opened PDF document.
- * @returns {Promise} - Returns the promise, which is resolved when all
- * destruction is completed.
- */
- async close() {
- this._unblockDocumentLoadEvent();
- this._hideViewBookmark();
- if (!this.pdfLoadingTask) {
- return;
- }
- if (
- (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
- this.pdfDocument?.annotationStorage.size > 0 &&
- this._annotationStorageModified
- ) {
- try {
- // Trigger saving, to prevent data loss in forms; see issue 12257.
- await this.save();
- } catch (reason) {
- // Ignoring errors, to ensure that document closing won't break.
- }
- }
- const promises = [];
- promises.push(this.pdfLoadingTask.destroy());
- this.pdfLoadingTask = null;
- if (this.pdfDocument) {
- this.pdfDocument = null;
- this.pdfThumbnailViewer?.setDocument(null);
- this.pdfViewer.setDocument(null);
- this.pdfLinkService.setDocument(null);
- this.pdfDocumentProperties?.setDocument(null);
- }
- this.pdfLinkService.externalLinkEnabled = true;
- this.store = null;
- this.isInitialViewSet = false;
- this.downloadComplete = false;
- this.url = "";
- this.baseUrl = "";
- this._downloadUrl = "";
- this.documentInfo = null;
- this.metadata = null;
- this._contentDispositionFilename = null;
- this._contentLength = null;
- this._saveInProgress = false;
- this._hasAnnotationEditors = false;
- promises.push(this.pdfScriptingManager.destroyPromise);
- this.setTitle();
- this.pdfSidebar?.reset();
- this.pdfOutlineViewer?.reset();
- this.pdfAttachmentViewer?.reset();
- this.pdfLayerViewer?.reset();
- this.pdfHistory?.reset();
- this.findBar?.reset();
- this.toolbar?.reset();
- this.secondaryToolbar?.reset();
- this._PDFBug?.cleanup();
- await Promise.all(promises);
- },
- /**
- * Opens PDF document specified by URL or array with additional arguments.
- * @param {string|TypedArray|ArrayBuffer} file - PDF location or binary data.
- * @param {Object} [args] - Additional arguments for the getDocument call,
- * e.g. HTTP headers ('httpHeaders') or alternative
- * data transport ('range').
- * @returns {Promise} - Returns the promise, which is resolved when document
- * is opened.
- */
- async open(file, args) {
- if (this.pdfLoadingTask) {
- // We need to destroy already opened document.
- await this.close();
- }
- // Set the necessary global worker parameters, using the available options.
- const workerParameters = AppOptions.getAll(OptionKind.WORKER);
- for (const key in workerParameters) {
- GlobalWorkerOptions[key] = workerParameters[key];
- }
- const parameters = Object.create(null);
- if (typeof file === "string") {
- // URL
- this.setTitleUsingUrl(file, /* downloadUrl = */ file);
- parameters.url = file;
- } else if (file && "byteLength" in file) {
- // ArrayBuffer
- parameters.data = file;
- } else if (file.url && file.originalUrl) {
- this.setTitleUsingUrl(file.originalUrl, /* downloadUrl = */ file.url);
- parameters.url = file.url;
- }
- // Set the necessary API parameters, using the available options.
- const apiParameters = AppOptions.getAll(OptionKind.API);
- for (const key in apiParameters) {
- let value = apiParameters[key];
- if (key === "docBaseUrl" && !value) {
- if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
- value = document.URL.split("#")[0];
- } else if (PDFJSDev.test("MOZCENTRAL || CHROME")) {
- value = this.baseUrl;
- }
- }
- parameters[key] = value;
- }
- // Finally, update the API parameters with the arguments (if they exist).
- if (args) {
- for (const key in args) {
- parameters[key] = args[key];
- }
- }
- const loadingTask = getDocument(parameters);
- this.pdfLoadingTask = loadingTask;
- loadingTask.onPassword = (updateCallback, reason) => {
- if (this.isViewerEmbedded) {
- // The load event can't be triggered until the password is entered, so
- // if the viewer is in an iframe and its visibility depends on the
- // onload callback then the viewer never shows (bug 1801341).
- this._unblockDocumentLoadEvent();
- }
- this.pdfLinkService.externalLinkEnabled = false;
- this.passwordPrompt.setUpdateCallback(updateCallback, reason);
- this.passwordPrompt.open();
- };
- loadingTask.onProgress = ({ loaded, total }) => {
- this.progress(loaded / total);
- };
- return loadingTask.promise.then(
- pdfDocument => {
- this.load(pdfDocument);
- },
- reason => {
- if (loadingTask !== this.pdfLoadingTask) {
- return undefined; // Ignore errors for previously opened PDF files.
- }
- let key = "loading_error";
- if (reason instanceof InvalidPDFException) {
- key = "invalid_file_error";
- } else if (reason instanceof MissingPDFException) {
- key = "missing_file_error";
- } else if (reason instanceof UnexpectedResponseException) {
- key = "unexpected_response_error";
- }
- return this.l10n.get(key).then(msg => {
- this._documentError(msg, { message: reason?.message });
- throw reason;
- });
- }
- );
- },
- /**
- * @private
- */
- _ensureDownloadComplete() {
- if (this.pdfDocument && this.downloadComplete) {
- return;
- }
- throw new Error("PDF document not downloaded.");
- },
- async download() {
- const url = this._downloadUrl,
- filename = this._docFilename;
- try {
- this._ensureDownloadComplete();
- const data = await this.pdfDocument.getData();
- const blob = new Blob([data], { type: "application/pdf" });
- await this.downloadManager.download(blob, url, filename);
- } catch (reason) {
- // When the PDF document isn't ready, or the PDF file is still
- // downloading, simply download using the URL.
- await this.downloadManager.downloadUrl(url, filename);
- }
- },
- async save() {
- if (this._saveInProgress) {
- return;
- }
- this._saveInProgress = true;
- await this.pdfScriptingManager.dispatchWillSave();
- const url = this._downloadUrl,
- filename = this._docFilename;
- try {
- this._ensureDownloadComplete();
- const data = await this.pdfDocument.saveDocument();
- const blob = new Blob([data], { type: "application/pdf" });
- await this.downloadManager.download(blob, url, filename);
- } catch (reason) {
- // When the PDF document isn't ready, or the PDF file is still
- // downloading, simply fallback to a "regular" download.
- console.error(`Error when saving the document: ${reason.message}`);
- await this.download();
- } finally {
- await this.pdfScriptingManager.dispatchDidSave();
- this._saveInProgress = false;
- }
- if (this._hasAnnotationEditors) {
- this.externalServices.reportTelemetry({
- type: "editing",
- data: { type: "save" },
- });
- }
- },
- downloadOrSave() {
- if (this.pdfDocument?.annotationStorage.size > 0) {
- this.save();
- } else {
- this.download();
- }
- },
- /**
- * Report the error; used for errors affecting loading and/or parsing of
- * the entire PDF document.
- */
- _documentError(message, moreInfo = null) {
- this._unblockDocumentLoadEvent();
- this._otherError(message, moreInfo);
- this.eventBus.dispatch("documenterror", {
- source: this,
- message,
- reason: moreInfo?.message ?? null,
- });
- },
- /**
- * Report the error; used for errors affecting e.g. only a single page.
- * @param {string} message - A message that is human readable.
- * @param {Object} [moreInfo] - Further information about the error that is
- * more technical. Should have a 'message' and
- * optionally a 'stack' property.
- */
- _otherError(message, moreInfo = null) {
- const moreInfoText = [`PDF.js v${version || "?"} (build: ${build || "?"})`];
- if (moreInfo) {
- moreInfoText.push(`Message: ${moreInfo.message}`);
- if (moreInfo.stack) {
- moreInfoText.push(`Stack: ${moreInfo.stack}`);
- } else {
- if (moreInfo.filename) {
- moreInfoText.push(`File: ${moreInfo.filename}`);
- }
- if (moreInfo.lineNumber) {
- moreInfoText.push(`Line: ${moreInfo.lineNumber}`);
- }
- }
- }
- console.error(`${message}\n\n${moreInfoText.join("\n")}`);
- },
- progress(level) {
- if (!this.loadingBar || this.downloadComplete) {
- // Don't accidentally show the loading bar again when the entire file has
- // already been fetched (only an issue when disableAutoFetch is enabled).
- return;
- }
- const percent = Math.round(level * 100);
- // When we transition from full request to range requests, it's possible
- // that we discard some of the loaded data. This can cause the loading
- // bar to move backwards. So prevent this by only updating the bar if it
- // increases.
- if (percent <= this.loadingBar.percent) {
- return;
- }
- this.loadingBar.percent = percent;
- // When disableAutoFetch is enabled, it's not uncommon for the entire file
- // to never be fetched (depends on e.g. the file structure). In this case
- // the loading bar will not be completely filled, nor will it be hidden.
- // To prevent displaying a partially filled loading bar permanently, we
- // hide it when no data has been loaded during a certain amount of time.
- const disableAutoFetch =
- this.pdfDocument?.loadingParams.disableAutoFetch ??
- AppOptions.get("disableAutoFetch");
- if (!disableAutoFetch || isNaN(percent)) {
- return;
- }
- if (this.disableAutoFetchLoadingBarTimeout) {
- clearTimeout(this.disableAutoFetchLoadingBarTimeout);
- this.disableAutoFetchLoadingBarTimeout = null;
- }
- this.loadingBar.show();
- this.disableAutoFetchLoadingBarTimeout = setTimeout(() => {
- this.loadingBar.hide();
- this.disableAutoFetchLoadingBarTimeout = null;
- }, DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT);
- },
- load(pdfDocument) {
- this.pdfDocument = pdfDocument;
- pdfDocument.getDownloadInfo().then(({ length }) => {
- this._contentLength = length; // Ensure that the correct length is used.
- this.downloadComplete = true;
- this.loadingBar?.hide();
- firstPagePromise.then(() => {
- this.eventBus.dispatch("documentloaded", { source: this });
- });
- });
- // Since the `setInitialView` call below depends on this being resolved,
- // fetch it early to avoid delaying initial rendering of the PDF document.
- const pageLayoutPromise = pdfDocument.getPageLayout().catch(function () {
- /* Avoid breaking initial rendering; ignoring errors. */
- });
- const pageModePromise = pdfDocument.getPageMode().catch(function () {
- /* Avoid breaking initial rendering; ignoring errors. */
- });
- const openActionPromise = pdfDocument.getOpenAction().catch(function () {
- /* Avoid breaking initial rendering; ignoring errors. */
- });
- this.toolbar?.setPagesCount(pdfDocument.numPages, false);
- this.secondaryToolbar?.setPagesCount(pdfDocument.numPages);
- let baseDocumentUrl;
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- baseDocumentUrl = null;
- } else if (PDFJSDev.test("MOZCENTRAL")) {
- baseDocumentUrl = this.baseUrl;
- } else if (PDFJSDev.test("CHROME")) {
- baseDocumentUrl = location.href.split("#")[0];
- }
- if (baseDocumentUrl && isDataScheme(baseDocumentUrl)) {
- // Ignore "data:"-URLs for performance reasons, even though it may cause
- // internal links to not work perfectly in all cases (see bug 1803050).
- baseDocumentUrl = null;
- }
- this.pdfLinkService.setDocument(pdfDocument, baseDocumentUrl);
- this.pdfDocumentProperties?.setDocument(pdfDocument);
- const pdfViewer = this.pdfViewer;
- pdfViewer.setDocument(pdfDocument);
- const { firstPagePromise, onePageRendered, pagesPromise } = pdfViewer;
- this.pdfThumbnailViewer?.setDocument(pdfDocument);
- const storedPromise = (this.store = new ViewHistory(
- pdfDocument.fingerprints[0]
- ))
- .getMultiple({
- page: null,
- zoom: DEFAULT_SCALE_VALUE,
- scrollLeft: "0",
- scrollTop: "0",
- rotation: null,
- sidebarView: SidebarView.UNKNOWN,
- scrollMode: ScrollMode.UNKNOWN,
- spreadMode: SpreadMode.UNKNOWN,
- })
- .catch(() => {
- /* Unable to read from storage; ignoring errors. */
- return Object.create(null);
- });
- firstPagePromise.then(pdfPage => {
- this.loadingBar?.setWidth(this.appConfig.viewerContainer);
- this._initializeAnnotationStorageCallbacks(pdfDocument);
- Promise.all([
- animationStarted,
- storedPromise,
- pageLayoutPromise,
- pageModePromise,
- openActionPromise,
- ])
- .then(async ([timeStamp, stored, pageLayout, pageMode, openAction]) => {
- const viewOnLoad = AppOptions.get("viewOnLoad");
- this._initializePdfHistory({
- fingerprint: pdfDocument.fingerprints[0],
- viewOnLoad,
- initialDest: openAction?.dest,
- });
- const initialBookmark = this.initialBookmark;
- // Initialize the default values, from user preferences.
- const zoom = AppOptions.get("defaultZoomValue");
- let hash = zoom ? `zoom=${zoom}` : null;
- let rotation = null;
- let sidebarView = AppOptions.get("sidebarViewOnLoad");
- let scrollMode = AppOptions.get("scrollModeOnLoad");
- let spreadMode = AppOptions.get("spreadModeOnLoad");
- if (stored.page && viewOnLoad !== ViewOnLoad.INITIAL) {
- hash =
- `page=${stored.page}&zoom=${zoom || stored.zoom},` +
- `${stored.scrollLeft},${stored.scrollTop}`;
- rotation = parseInt(stored.rotation, 10);
- // Always let user preference take precedence over the view history.
- if (sidebarView === SidebarView.UNKNOWN) {
- sidebarView = stored.sidebarView | 0;
- }
- if (scrollMode === ScrollMode.UNKNOWN) {
- scrollMode = stored.scrollMode | 0;
- }
- if (spreadMode === SpreadMode.UNKNOWN) {
- spreadMode = stored.spreadMode | 0;
- }
- }
- // Always let the user preference/view history take precedence.
- if (pageMode && sidebarView === SidebarView.UNKNOWN) {
- sidebarView = apiPageModeToSidebarView(pageMode);
- }
- // NOTE: Always ignore the pageLayout in GeckoView since there's
- // no UI available to change Scroll/Spread modes for the user.
- if (
- (typeof PDFJSDev === "undefined" || !PDFJSDev.test("GECKOVIEW")) &&
- pageLayout &&
- scrollMode === ScrollMode.UNKNOWN &&
- spreadMode === SpreadMode.UNKNOWN
- ) {
- const modes = apiPageLayoutToViewerModes(pageLayout);
- // TODO: Try to improve page-switching when using the mouse-wheel
- // and/or arrow-keys before allowing the document to control this.
- // scrollMode = modes.scrollMode;
- spreadMode = modes.spreadMode;
- }
- this.setInitialView(hash, {
- rotation,
- sidebarView,
- scrollMode,
- spreadMode,
- });
- this.eventBus.dispatch("documentinit", { source: this });
- // Make all navigation keys work on document load,
- // unless the viewer is embedded in a web page.
- if (!this.isViewerEmbedded) {
- pdfViewer.focus();
- }
- // For documents with different page sizes, once all pages are
- // resolved, ensure that the correct location becomes visible on load.
- // (To reduce the risk, in very large and/or slow loading documents,
- // that the location changes *after* the user has started interacting
- // with the viewer, wait for either `pagesPromise` or a timeout.)
- await Promise.race([
- pagesPromise,
- new Promise(resolve => {
- setTimeout(resolve, FORCE_PAGES_LOADED_TIMEOUT);
- }),
- ]);
- if (!initialBookmark && !hash) {
- return;
- }
- if (pdfViewer.hasEqualPageSizes) {
- return;
- }
- this.initialBookmark = initialBookmark;
- // eslint-disable-next-line no-self-assign
- pdfViewer.currentScaleValue = pdfViewer.currentScaleValue;
- // Re-apply the initial document location.
- this.setInitialView(hash);
- })
- .catch(() => {
- // Ensure that the document is always completely initialized,
- // even if there are any errors thrown above.
- this.setInitialView();
- })
- .then(function () {
- // At this point, rendering of the initial page(s) should always have
- // started (and may even have completed).
- // To prevent any future issues, e.g. the document being completely
- // blank on load, always trigger rendering here.
- pdfViewer.update();
- });
- });
- pagesPromise.then(
- () => {
- this._unblockDocumentLoadEvent();
- this._initializeAutoPrint(pdfDocument, openActionPromise);
- },
- reason => {
- this.l10n.get("loading_error").then(msg => {
- this._documentError(msg, { message: reason?.message });
- });
- }
- );
- onePageRendered.then(data => {
- this.externalServices.reportTelemetry({
- type: "pageInfo",
- timestamp: data.timestamp,
- });
- pdfDocument.getOutline().then(outline => {
- if (pdfDocument !== this.pdfDocument) {
- return; // The document was closed while the outline resolved.
- }
- this.pdfOutlineViewer?.render({ outline, pdfDocument });
- });
- pdfDocument.getAttachments().then(attachments => {
- if (pdfDocument !== this.pdfDocument) {
- return; // The document was closed while the attachments resolved.
- }
- this.pdfAttachmentViewer?.render({ attachments });
- });
- // Ensure that the layers accurately reflects the current state in the
- // viewer itself, rather than the default state provided by the API.
- pdfViewer.optionalContentConfigPromise.then(optionalContentConfig => {
- if (pdfDocument !== this.pdfDocument) {
- return; // The document was closed while the layers resolved.
- }
- this.pdfLayerViewer?.render({ optionalContentConfig, pdfDocument });
- });
- });
- this._initializePageLabels(pdfDocument);
- this._initializeMetadata(pdfDocument);
- },
- /**
- * @private
- */
- async _scriptingDocProperties(pdfDocument) {
- if (!this.documentInfo) {
- // It should be *extremely* rare for metadata to not have been resolved
- // when this code runs, but ensure that we handle that case here.
- await new Promise(resolve => {
- this.eventBus._on("metadataloaded", resolve, { once: true });
- });
- if (pdfDocument !== this.pdfDocument) {
- return null; // The document was closed while the metadata resolved.
- }
- }
- if (!this._contentLength) {
- // Always waiting for the entire PDF document to be loaded will, most
- // likely, delay sandbox-creation too much in the general case for all
- // PDF documents which are not provided as binary data to the API.
- // Hence we'll simply have to trust that the `contentLength` (as provided
- // by the server), when it exists, is accurate enough here.
- await new Promise(resolve => {
- this.eventBus._on("documentloaded", resolve, { once: true });
- });
- if (pdfDocument !== this.pdfDocument) {
- return null; // The document was closed while the downloadInfo resolved.
- }
- }
- return {
- ...this.documentInfo,
- baseURL: this.baseUrl,
- filesize: this._contentLength,
- filename: this._docFilename,
- metadata: this.metadata?.getRaw(),
- authors: this.metadata?.get("dc:creator"),
- numPages: this.pagesCount,
- URL: this.url,
- };
- },
- /**
- * @private
- */
- async _initializeAutoPrint(pdfDocument, openActionPromise) {
- const [openAction, javaScript] = await Promise.all([
- openActionPromise,
- !this.pdfViewer.enableScripting ? pdfDocument.getJavaScript() : null,
- ]);
- if (pdfDocument !== this.pdfDocument) {
- return; // The document was closed while the auto print data resolved.
- }
- let triggerAutoPrint = false;
- if (openAction?.action === "Print") {
- triggerAutoPrint = true;
- }
- if (javaScript) {
- javaScript.some(js => {
- if (!js) {
- // Don't warn/fallback for empty JavaScript actions.
- return false;
- }
- console.warn("Warning: JavaScript support is not enabled");
- return true;
- });
- if (!triggerAutoPrint) {
- // Hack to support auto printing.
- for (const js of javaScript) {
- if (js && AutoPrintRegExp.test(js)) {
- triggerAutoPrint = true;
- break;
- }
- }
- }
- }
- if (triggerAutoPrint) {
- this.triggerPrinting();
- }
- },
- /**
- * @private
- */
- async _initializeMetadata(pdfDocument) {
- const { info, metadata, contentDispositionFilename, contentLength } =
- await pdfDocument.getMetadata();
- if (pdfDocument !== this.pdfDocument) {
- return; // The document was closed while the metadata resolved.
- }
- this.documentInfo = info;
- this.metadata = metadata;
- this._contentDispositionFilename ??= contentDispositionFilename;
- this._contentLength ??= contentLength; // See `getDownloadInfo`-call above.
- // Provides some basic debug information
- console.log(
- `PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` +
- `${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` +
- `(PDF.js: ${version || "?"} [${build || "?"}])`
- );
- let pdfTitle = info.Title;
- const metadataTitle = metadata?.get("dc:title");
- if (metadataTitle) {
- // Ghostscript can produce invalid 'dc:title' Metadata entries:
- // - The title may be "Untitled" (fixes bug 1031612).
- // - The title may contain incorrectly encoded characters, which thus
- // looks broken, hence we ignore the Metadata entry when it contains
- // characters from the Specials Unicode block (fixes bug 1605526).
- if (
- metadataTitle !== "Untitled" &&
- !/[\uFFF0-\uFFFF]/g.test(metadataTitle)
- ) {
- pdfTitle = metadataTitle;
- }
- }
- if (pdfTitle) {
- this.setTitle(
- `${pdfTitle} - ${this._contentDispositionFilename || this._title}`
- );
- } else if (this._contentDispositionFilename) {
- this.setTitle(this._contentDispositionFilename);
- }
- if (
- info.IsXFAPresent &&
- !info.IsAcroFormPresent &&
- !pdfDocument.isPureXfa
- ) {
- if (pdfDocument.loadingParams.enableXfa) {
- console.warn("Warning: XFA Foreground documents are not supported");
- } else {
- console.warn("Warning: XFA support is not enabled");
- }
- } else if (
- (info.IsAcroFormPresent || info.IsXFAPresent) &&
- !this.pdfViewer.renderForms
- ) {
- console.warn("Warning: Interactive form support is not enabled");
- }
- if (info.IsSignaturesPresent) {
- console.warn("Warning: Digital signatures validation is not supported");
- }
- this.eventBus.dispatch("metadataloaded", { source: this });
- },
- /**
- * @private
- */
- async _initializePageLabels(pdfDocument) {
- const labels = await pdfDocument.getPageLabels();
- if (pdfDocument !== this.pdfDocument) {
- return; // The document was closed while the page labels resolved.
- }
- if (!labels || AppOptions.get("disablePageLabels")) {
- return;
- }
- const numLabels = labels.length;
- // Ignore page labels that correspond to standard page numbering,
- // or page labels that are all empty.
- let standardLabels = 0,
- emptyLabels = 0;
- for (let i = 0; i < numLabels; i++) {
- const label = labels[i];
- if (label === (i + 1).toString()) {
- standardLabels++;
- } else if (label === "") {
- emptyLabels++;
- } else {
- break;
- }
- }
- if (standardLabels >= numLabels || emptyLabels >= numLabels) {
- return;
- }
- const { pdfViewer, pdfThumbnailViewer, toolbar } = this;
- pdfViewer.setPageLabels(labels);
- pdfThumbnailViewer?.setPageLabels(labels);
- // Changing toolbar page display to use labels and we need to set
- // the label of the current page.
- toolbar?.setPagesCount(numLabels, true);
- toolbar?.setPageNumber(
- pdfViewer.currentPageNumber,
- pdfViewer.currentPageLabel
- );
- },
- /**
- * @private
- */
- _initializePdfHistory({ fingerprint, viewOnLoad, initialDest = null }) {
- if (!this.pdfHistory) {
- return;
- }
- this.pdfHistory.initialize({
- fingerprint,
- resetHistory: viewOnLoad === ViewOnLoad.INITIAL,
- updateUrl: AppOptions.get("historyUpdateUrl"),
- });
- if (this.pdfHistory.initialBookmark) {
- this.initialBookmark = this.pdfHistory.initialBookmark;
- this.initialRotation = this.pdfHistory.initialRotation;
- }
- // Always let the browser history/document hash take precedence.
- if (
- initialDest &&
- !this.initialBookmark &&
- viewOnLoad === ViewOnLoad.UNKNOWN
- ) {
- this.initialBookmark = JSON.stringify(initialDest);
- // TODO: Re-factor the `PDFHistory` initialization to remove this hack
- // that's currently necessary to prevent weird initial history state.
- this.pdfHistory.push({ explicitDest: initialDest, pageNumber: null });
- }
- },
- /**
- * @private
- */
- _initializeAnnotationStorageCallbacks(pdfDocument) {
- if (pdfDocument !== this.pdfDocument) {
- return;
- }
- const { annotationStorage } = pdfDocument;
- annotationStorage.onSetModified = () => {
- window.addEventListener("beforeunload", beforeUnload);
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- this._annotationStorageModified = true;
- }
- };
- annotationStorage.onResetModified = () => {
- window.removeEventListener("beforeunload", beforeUnload);
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- delete this._annotationStorageModified;
- }
- };
- annotationStorage.onAnnotationEditor = typeStr => {
- this._hasAnnotationEditors = !!typeStr;
- this.setTitle();
- if (typeStr) {
- this.externalServices.reportTelemetry({
- type: "editing",
- data: { type: typeStr },
- });
- }
- };
- },
- setInitialView(
- storedHash,
- { rotation, sidebarView, scrollMode, spreadMode } = {}
- ) {
- const setRotation = angle => {
- if (isValidRotation(angle)) {
- this.pdfViewer.pagesRotation = angle;
- }
- };
- const setViewerModes = (scroll, spread) => {
- if (isValidScrollMode(scroll)) {
- this.pdfViewer.scrollMode = scroll;
- }
- if (isValidSpreadMode(spread)) {
- this.pdfViewer.spreadMode = spread;
- }
- };
- this.isInitialViewSet = true;
- this.pdfSidebar?.setInitialView(sidebarView);
- setViewerModes(scrollMode, spreadMode);
- if (this.initialBookmark) {
- setRotation(this.initialRotation);
- delete this.initialRotation;
- this.pdfLinkService.setHash(this.initialBookmark);
- this.initialBookmark = null;
- } else if (storedHash) {
- setRotation(rotation);
- this.pdfLinkService.setHash(storedHash);
- }
- // Ensure that the correct page number is displayed in the UI,
- // even if the active page didn't change during document load.
- this.toolbar?.setPageNumber(
- this.pdfViewer.currentPageNumber,
- this.pdfViewer.currentPageLabel
- );
- this.secondaryToolbar?.setPageNumber(this.pdfViewer.currentPageNumber);
- if (!this.pdfViewer.currentScaleValue) {
- // Scale was not initialized: invalid bookmark or scale was not specified.
- // Setting the default one.
- this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
- }
- },
- /**
- * @private
- */
- _cleanup() {
- if (!this.pdfDocument) {
- return; // run cleanup when document is loaded
- }
- this.pdfViewer.cleanup();
- this.pdfThumbnailViewer?.cleanup();
- if (
- typeof PDFJSDev === "undefined" ||
- PDFJSDev.test("!PRODUCTION || GENERIC")
- ) {
- // We don't want to remove fonts used by active page SVGs.
- this.pdfDocument.cleanup(
- /* keepLoadedFonts = */ this.pdfViewer.renderer === RendererType.SVG
- );
- } else {
- this.pdfDocument.cleanup();
- }
- },
- forceRendering() {
- this.pdfRenderingQueue.printing = !!this.printService;
- this.pdfRenderingQueue.isThumbnailViewEnabled =
- this.pdfSidebar?.visibleView === SidebarView.THUMBS;
- this.pdfRenderingQueue.renderHighestPriority();
- },
- beforePrint() {
- this._printAnnotationStoragePromise = this.pdfScriptingManager
- .dispatchWillPrint()
- .catch(() => {
- /* Avoid breaking printing; ignoring errors. */
- })
- .then(() => {
- return this.pdfDocument?.annotationStorage.print;
- });
- if (this.printService) {
- // There is no way to suppress beforePrint/afterPrint events,
- // but PDFPrintService may generate double events -- this will ignore
- // the second event that will be coming from native window.print().
- return;
- }
- if (!this.supportsPrinting) {
- this.l10n.get("printing_not_supported").then(msg => {
- this._otherError(msg);
- });
- return;
- }
- // The beforePrint is a sync method and we need to know layout before
- // returning from this method. Ensure that we can get sizes of the pages.
- if (!this.pdfViewer.pageViewsReady) {
- this.l10n.get("printing_not_ready").then(msg => {
- // eslint-disable-next-line no-alert
- window.alert(msg);
- });
- return;
- }
- const pagesOverview = this.pdfViewer.getPagesOverview();
- const printContainer = this.appConfig.printContainer;
- const printResolution = AppOptions.get("printResolution");
- const optionalContentConfigPromise =
- this.pdfViewer.optionalContentConfigPromise;
- const printService = PDFPrintServiceFactory.instance.createPrintService(
- this.pdfDocument,
- pagesOverview,
- printContainer,
- printResolution,
- optionalContentConfigPromise,
- this._printAnnotationStoragePromise,
- this.l10n
- );
- this.printService = printService;
- this.forceRendering();
- // Disable the editor-indicator during printing (fixes bug 1790552).
- this.setTitle();
- printService.layout();
- if (this._hasAnnotationEditors) {
- this.externalServices.reportTelemetry({
- type: "editing",
- data: { type: "print" },
- });
- }
- },
- afterPrint() {
- if (this._printAnnotationStoragePromise) {
- this._printAnnotationStoragePromise.then(() => {
- this.pdfScriptingManager.dispatchDidPrint();
- });
- this._printAnnotationStoragePromise = null;
- }
- if (this.printService) {
- this.printService.destroy();
- this.printService = null;
- this.pdfDocument?.annotationStorage.resetModified();
- }
- this.forceRendering();
- // Re-enable the editor-indicator after printing (fixes bug 1790552).
- this.setTitle();
- },
- rotatePages(delta) {
- this.pdfViewer.pagesRotation += delta;
- // Note that the thumbnail viewer is updated, and rendering is triggered,
- // in the 'rotationchanging' event handler.
- },
- requestPresentationMode() {
- this.pdfPresentationMode?.request();
- },
- triggerPrinting() {
- if (!this.supportsPrinting) {
- return;
- }
- window.print();
- },
- bindEvents() {
- const { eventBus, _boundEvents } = this;
- _boundEvents.beforePrint = this.beforePrint.bind(this);
- _boundEvents.afterPrint = this.afterPrint.bind(this);
- eventBus._on("resize", webViewerResize);
- eventBus._on("hashchange", webViewerHashchange);
- eventBus._on("beforeprint", _boundEvents.beforePrint);
- eventBus._on("afterprint", _boundEvents.afterPrint);
- eventBus._on("pagerender", webViewerPageRender);
- eventBus._on("pagerendered", webViewerPageRendered);
- eventBus._on("updateviewarea", webViewerUpdateViewarea);
- eventBus._on("pagechanging", webViewerPageChanging);
- eventBus._on("scalechanging", webViewerScaleChanging);
- eventBus._on("rotationchanging", webViewerRotationChanging);
- eventBus._on("sidebarviewchanged", webViewerSidebarViewChanged);
- eventBus._on("pagemode", webViewerPageMode);
- eventBus._on("namedaction", webViewerNamedAction);
- eventBus._on("presentationmodechanged", webViewerPresentationModeChanged);
- eventBus._on("presentationmode", webViewerPresentationMode);
- eventBus._on(
- "switchannotationeditormode",
- webViewerSwitchAnnotationEditorMode
- );
- eventBus._on(
- "switchannotationeditorparams",
- webViewerSwitchAnnotationEditorParams
- );
- eventBus._on("print", webViewerPrint);
- eventBus._on("download", webViewerDownload);
- eventBus._on("firstpage", webViewerFirstPage);
- eventBus._on("lastpage", webViewerLastPage);
- eventBus._on("nextpage", webViewerNextPage);
- eventBus._on("previouspage", webViewerPreviousPage);
- eventBus._on("zoomin", webViewerZoomIn);
- eventBus._on("zoomout", webViewerZoomOut);
- eventBus._on("zoomreset", webViewerZoomReset);
- eventBus._on("pagenumberchanged", webViewerPageNumberChanged);
- eventBus._on("scalechanged", webViewerScaleChanged);
- eventBus._on("rotatecw", webViewerRotateCw);
- eventBus._on("rotateccw", webViewerRotateCcw);
- eventBus._on("optionalcontentconfig", webViewerOptionalContentConfig);
- eventBus._on("switchscrollmode", webViewerSwitchScrollMode);
- eventBus._on("scrollmodechanged", webViewerScrollModeChanged);
- eventBus._on("switchspreadmode", webViewerSwitchSpreadMode);
- eventBus._on("spreadmodechanged", webViewerSpreadModeChanged);
- eventBus._on("documentproperties", webViewerDocumentProperties);
- eventBus._on("findfromurlhash", webViewerFindFromUrlHash);
- eventBus._on("updatefindmatchescount", webViewerUpdateFindMatchesCount);
- eventBus._on("updatefindcontrolstate", webViewerUpdateFindControlState);
- if (AppOptions.get("pdfBug")) {
- _boundEvents.reportPageStatsPDFBug = reportPageStatsPDFBug;
- eventBus._on("pagerendered", _boundEvents.reportPageStatsPDFBug);
- eventBus._on("pagechanging", _boundEvents.reportPageStatsPDFBug);
- }
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- eventBus._on("fileinputchange", webViewerFileInputChange);
- eventBus._on("openfile", webViewerOpenFile);
- }
- if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
- eventBus._on(
- "annotationeditorstateschanged",
- webViewerAnnotationEditorStatesChanged
- );
- }
- },
- bindWindowEvents() {
- const { eventBus, _boundEvents } = this;
- function addWindowResolutionChange(evt = null) {
- if (evt) {
- webViewerResolutionChange(evt);
- }
- const mediaQueryList = window.matchMedia(
- `(resolution: ${window.devicePixelRatio || 1}dppx)`
- );
- mediaQueryList.addEventListener("change", addWindowResolutionChange, {
- once: true,
- });
- if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
- return;
- }
- _boundEvents.removeWindowResolutionChange ||= function () {
- mediaQueryList.removeEventListener("change", addWindowResolutionChange);
- _boundEvents.removeWindowResolutionChange = null;
- };
- }
- addWindowResolutionChange();
- _boundEvents.windowResize = () => {
- eventBus.dispatch("resize", { source: window });
- };
- _boundEvents.windowHashChange = () => {
- eventBus.dispatch("hashchange", {
- source: window,
- hash: document.location.hash.substring(1),
- });
- };
- _boundEvents.windowBeforePrint = () => {
- eventBus.dispatch("beforeprint", { source: window });
- };
- _boundEvents.windowAfterPrint = () => {
- eventBus.dispatch("afterprint", { source: window });
- };
- _boundEvents.windowUpdateFromSandbox = event => {
- eventBus.dispatch("updatefromsandbox", {
- source: window,
- detail: event.detail,
- });
- };
- window.addEventListener("visibilitychange", webViewerVisibilityChange);
- window.addEventListener("wheel", webViewerWheel, { passive: false });
- window.addEventListener("touchstart", webViewerTouchStart, {
- passive: false,
- });
- window.addEventListener("click", webViewerClick);
- window.addEventListener("keydown", webViewerKeyDown);
- window.addEventListener("resize", _boundEvents.windowResize);
- window.addEventListener("hashchange", _boundEvents.windowHashChange);
- window.addEventListener("beforeprint", _boundEvents.windowBeforePrint);
- window.addEventListener("afterprint", _boundEvents.windowAfterPrint);
- window.addEventListener(
- "updatefromsandbox",
- _boundEvents.windowUpdateFromSandbox
- );
- },
- unbindEvents() {
- if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
- throw new Error("Not implemented: unbindEvents");
- }
- const { eventBus, _boundEvents } = this;
- eventBus._off("resize", webViewerResize);
- eventBus._off("hashchange", webViewerHashchange);
- eventBus._off("beforeprint", _boundEvents.beforePrint);
- eventBus._off("afterprint", _boundEvents.afterPrint);
- eventBus._off("pagerender", webViewerPageRender);
- eventBus._off("pagerendered", webViewerPageRendered);
- eventBus._off("updateviewarea", webViewerUpdateViewarea);
- eventBus._off("pagechanging", webViewerPageChanging);
- eventBus._off("scalechanging", webViewerScaleChanging);
- eventBus._off("rotationchanging", webViewerRotationChanging);
- eventBus._off("sidebarviewchanged", webViewerSidebarViewChanged);
- eventBus._off("pagemode", webViewerPageMode);
- eventBus._off("namedaction", webViewerNamedAction);
- eventBus._off("presentationmodechanged", webViewerPresentationModeChanged);
- eventBus._off("presentationmode", webViewerPresentationMode);
- eventBus._off("print", webViewerPrint);
- eventBus._off("download", webViewerDownload);
- eventBus._off("firstpage", webViewerFirstPage);
- eventBus._off("lastpage", webViewerLastPage);
- eventBus._off("nextpage", webViewerNextPage);
- eventBus._off("previouspage", webViewerPreviousPage);
- eventBus._off("zoomin", webViewerZoomIn);
- eventBus._off("zoomout", webViewerZoomOut);
- eventBus._off("zoomreset", webViewerZoomReset);
- eventBus._off("pagenumberchanged", webViewerPageNumberChanged);
- eventBus._off("scalechanged", webViewerScaleChanged);
- eventBus._off("rotatecw", webViewerRotateCw);
- eventBus._off("rotateccw", webViewerRotateCcw);
- eventBus._off("optionalcontentconfig", webViewerOptionalContentConfig);
- eventBus._off("switchscrollmode", webViewerSwitchScrollMode);
- eventBus._off("scrollmodechanged", webViewerScrollModeChanged);
- eventBus._off("switchspreadmode", webViewerSwitchSpreadMode);
- eventBus._off("spreadmodechanged", webViewerSpreadModeChanged);
- eventBus._off("documentproperties", webViewerDocumentProperties);
- eventBus._off("findfromurlhash", webViewerFindFromUrlHash);
- eventBus._off("updatefindmatchescount", webViewerUpdateFindMatchesCount);
- eventBus._off("updatefindcontrolstate", webViewerUpdateFindControlState);
- if (_boundEvents.reportPageStatsPDFBug) {
- eventBus._off("pagerendered", _boundEvents.reportPageStatsPDFBug);
- eventBus._off("pagechanging", _boundEvents.reportPageStatsPDFBug);
- _boundEvents.reportPageStatsPDFBug = null;
- }
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- eventBus._off("fileinputchange", webViewerFileInputChange);
- eventBus._off("openfile", webViewerOpenFile);
- }
- _boundEvents.beforePrint = null;
- _boundEvents.afterPrint = null;
- },
- unbindWindowEvents() {
- if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
- throw new Error("Not implemented: unbindWindowEvents");
- }
- const { _boundEvents } = this;
- window.removeEventListener("visibilitychange", webViewerVisibilityChange);
- window.removeEventListener("wheel", webViewerWheel, { passive: false });
- window.removeEventListener("touchstart", webViewerTouchStart, {
- passive: false,
- });
- window.removeEventListener("click", webViewerClick);
- window.removeEventListener("keydown", webViewerKeyDown);
- window.removeEventListener("resize", _boundEvents.windowResize);
- window.removeEventListener("hashchange", _boundEvents.windowHashChange);
- window.removeEventListener("beforeprint", _boundEvents.windowBeforePrint);
- window.removeEventListener("afterprint", _boundEvents.windowAfterPrint);
- window.removeEventListener(
- "updatefromsandbox",
- _boundEvents.windowUpdateFromSandbox
- );
- _boundEvents.removeWindowResolutionChange?.();
- _boundEvents.windowResize = null;
- _boundEvents.windowHashChange = null;
- _boundEvents.windowBeforePrint = null;
- _boundEvents.windowAfterPrint = null;
- _boundEvents.windowUpdateFromSandbox = null;
- },
- accumulateWheelTicks(ticks) {
- // If the scroll direction changed, reset the accumulated wheel ticks.
- if (
- (this._wheelUnusedTicks > 0 && ticks < 0) ||
- (this._wheelUnusedTicks < 0 && ticks > 0)
- ) {
- this._wheelUnusedTicks = 0;
- }
- this._wheelUnusedTicks += ticks;
- const wholeTicks = Math.trunc(this._wheelUnusedTicks);
- this._wheelUnusedTicks -= wholeTicks;
- return wholeTicks;
- },
- /**
- * Should be called *after* all pages have loaded, or if an error occurred,
- * to unblock the "load" event; see https://bugzilla.mozilla.org/show_bug.cgi?id=1618553
- * @private
- */
- _unblockDocumentLoadEvent() {
- document.blockUnblockOnload?.(false);
- // Ensure that this method is only ever run once.
- this._unblockDocumentLoadEvent = () => {};
- },
- /**
- * Used together with the integration-tests, to enable awaiting full
- * initialization of the scripting/sandbox.
- */
- get scriptingReady() {
- return this.pdfScriptingManager.ready;
- },
- };
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- const HOSTED_VIEWER_ORIGINS = [
- "null",
- "http://mozilla.github.io",
- "https://mozilla.github.io",
- ];
- // eslint-disable-next-line no-var
- var validateFileURL = function (file) {
- if (!file) {
- return;
- }
- try {
- const viewerOrigin = new URL(window.location.href).origin || "null";
- if (HOSTED_VIEWER_ORIGINS.includes(viewerOrigin)) {
- // Hosted or local viewer, allow for any file locations
- return;
- }
- const fileOrigin = new URL(file, window.location.href).origin;
- // Removing of the following line will not guarantee that the viewer will
- // start accepting URLs from foreign origin -- CORS headers on the remote
- // server must be properly configured.
- // if (fileOrigin !== viewerOrigin) {
- // throw new Error("file origin does not match viewer's");
- // }
- } catch (ex) {
- PDFViewerApplication.l10n.get("loading_error").then(msg => {
- PDFViewerApplication._documentError(msg, { message: ex?.message });
- });
- throw ex;
- }
- };
- }
- async function loadFakeWorker() {
- GlobalWorkerOptions.workerSrc ||= AppOptions.get("workerSrc");
- if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
- window.pdfjsWorker = await import("pdfjs/core/worker.js");
- return;
- }
- await loadScript(PDFWorker.workerSrc);
- }
- async function loadPDFBug(self) {
- const { debuggerScriptPath } = self.appConfig;
- const { PDFBug } =
- typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
- ? await import(debuggerScriptPath) // eslint-disable-line no-unsanitized/method
- : await __non_webpack_import__(debuggerScriptPath); // eslint-disable-line no-undef
- self._PDFBug = PDFBug;
- }
- function reportPageStatsPDFBug({ pageNumber }) {
- if (!globalThis.Stats?.enabled) {
- return;
- }
- const pageView = PDFViewerApplication.pdfViewer.getPageView(
- /* index = */ pageNumber - 1
- );
- globalThis.Stats.add(pageNumber, pageView?.pdfPage?.stats);
- }
- function webViewerInitialized() {
- const { appConfig, eventBus } = PDFViewerApplication;
- let file;
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- const queryString = document.location.search.substring(1);
- const params = parseQueryString(queryString);
- file = params.get("file") ?? AppOptions.get("defaultUrl");
- validateFileURL(file);
- } else if (PDFJSDev.test("MOZCENTRAL")) {
- file = window.location.href;
- } else if (PDFJSDev.test("CHROME")) {
- file = AppOptions.get("defaultUrl");
- }
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- const fileInput = appConfig.openFileInput;
- fileInput.value = null;
- fileInput.addEventListener("change", function (evt) {
- const { files } = evt.target;
- if (!files || files.length === 0) {
- return;
- }
- eventBus.dispatch("fileinputchange", {
- source: this,
- fileInput: evt.target,
- });
- });
- // Enable dragging-and-dropping a new PDF file onto the viewerContainer.
- appConfig.mainContainer.addEventListener("dragover", function (evt) {
- evt.preventDefault();
- evt.dataTransfer.dropEffect =
- evt.dataTransfer.effectAllowed === "copy" ? "copy" : "move";
- });
- appConfig.mainContainer.addEventListener("drop", function (evt) {
- evt.preventDefault();
- const { files } = evt.dataTransfer;
- if (!files || files.length === 0) {
- return;
- }
- eventBus.dispatch("fileinputchange", {
- source: this,
- fileInput: evt.dataTransfer,
- });
- });
- }
- if (!PDFViewerApplication.supportsDocumentFonts) {
- AppOptions.set("disableFontFace", true);
- PDFViewerApplication.l10n.get("web_fonts_disabled").then(msg => {
- console.warn(msg);
- });
- }
- if (!PDFViewerApplication.supportsPrinting) {
- appConfig.toolbar?.print.classList.add("hidden");
- appConfig.secondaryToolbar?.printButton.classList.add("hidden");
- }
- if (!PDFViewerApplication.supportsFullscreen) {
- appConfig.secondaryToolbar?.presentationModeButton.classList.add("hidden");
- }
- if (PDFViewerApplication.supportsIntegratedFind) {
- appConfig.toolbar?.viewFind.classList.add("hidden");
- }
- appConfig.mainContainer.addEventListener(
- "transitionend",
- function (evt) {
- if (evt.target === /* mainContainer */ this) {
- eventBus.dispatch("resize", { source: this });
- }
- },
- true
- );
- try {
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- if (file) {
- PDFViewerApplication.open(file);
- } else {
- PDFViewerApplication._hideViewBookmark();
- }
- } else if (PDFJSDev.test("MOZCENTRAL || CHROME")) {
- PDFViewerApplication.setTitleUsingUrl(file, /* downloadUrl = */ file);
- PDFViewerApplication.initPassiveLoading();
- } else {
- throw new Error("Not implemented: webViewerInitialized");
- }
- } catch (reason) {
- PDFViewerApplication.l10n.get("loading_error").then(msg => {
- PDFViewerApplication._documentError(msg, reason);
- });
- }
- }
- function webViewerPageRender({ pageNumber }) {
- // If the page is (the most) visible when it starts rendering,
- // ensure that the page number input loading indicator is displayed.
- if (pageNumber === PDFViewerApplication.page) {
- PDFViewerApplication.toolbar?.updateLoadingIndicatorState(true);
- }
- }
- function webViewerPageRendered({ pageNumber, error }) {
- // If the page is still visible when it has finished rendering,
- // ensure that the page number input loading indicator is hidden.
- if (pageNumber === PDFViewerApplication.page) {
- PDFViewerApplication.toolbar?.updateLoadingIndicatorState(false);
- }
- // Use the rendered page to set the corresponding thumbnail image.
- if (PDFViewerApplication.pdfSidebar?.visibleView === SidebarView.THUMBS) {
- const pageView = PDFViewerApplication.pdfViewer.getPageView(
- /* index = */ pageNumber - 1
- );
- const thumbnailView = PDFViewerApplication.pdfThumbnailViewer?.getThumbnail(
- /* index = */ pageNumber - 1
- );
- if (pageView && thumbnailView) {
- thumbnailView.setImage(pageView);
- }
- }
- if (error) {
- PDFViewerApplication.l10n.get("rendering_error").then(msg => {
- PDFViewerApplication._otherError(msg, error);
- });
- }
- }
- function webViewerPageMode({ mode }) {
- // Handle the 'pagemode' hash parameter, see also `PDFLinkService_setHash`.
- let view;
- switch (mode) {
- case "thumbs":
- view = SidebarView.THUMBS;
- break;
- case "bookmarks":
- case "outline": // non-standard
- view = SidebarView.OUTLINE;
- break;
- case "attachments": // non-standard
- view = SidebarView.ATTACHMENTS;
- break;
- case "layers": // non-standard
- view = SidebarView.LAYERS;
- break;
- case "none":
- view = SidebarView.NONE;
- break;
- default:
- console.error('Invalid "pagemode" hash parameter: ' + mode);
- return;
- }
- PDFViewerApplication.pdfSidebar?.switchView(view, /* forceOpen = */ true);
- }
- function webViewerNamedAction(evt) {
- // Processing a couple of named actions that might be useful, see also
- // `PDFLinkService.executeNamedAction`.
- switch (evt.action) {
- case "GoToPage":
- PDFViewerApplication.appConfig.toolbar?.pageNumber.select();
- break;
- case "Find":
- if (!PDFViewerApplication.supportsIntegratedFind) {
- PDFViewerApplication?.findBar.toggle();
- }
- break;
- case "Print":
- PDFViewerApplication.triggerPrinting();
- break;
- case "SaveAs":
- PDFViewerApplication.downloadOrSave();
- break;
- }
- }
- function webViewerPresentationModeChanged(evt) {
- PDFViewerApplication.pdfViewer.presentationModeState = evt.state;
- }
- function webViewerSidebarViewChanged({ view }) {
- PDFViewerApplication.pdfRenderingQueue.isThumbnailViewEnabled =
- view === SidebarView.THUMBS;
- if (PDFViewerApplication.isInitialViewSet) {
- // Only update the storage when the document has been loaded *and* rendered.
- PDFViewerApplication.store?.set("sidebarView", view).catch(() => {
- // Unable to write to storage.
- });
- }
- }
- function webViewerUpdateViewarea({ location }) {
- if (PDFViewerApplication.isInitialViewSet) {
- // Only update the storage when the document has been loaded *and* rendered.
- PDFViewerApplication.store
- ?.setMultiple({
- page: location.pageNumber,
- zoom: location.scale,
- scrollLeft: location.left,
- scrollTop: location.top,
- rotation: location.rotation,
- })
- .catch(() => {
- // Unable to write to storage.
- });
- }
- if (PDFViewerApplication.appConfig.secondaryToolbar) {
- const href = PDFViewerApplication.pdfLinkService.getAnchorUrl(
- location.pdfOpenParams
- );
- PDFViewerApplication.appConfig.secondaryToolbar.viewBookmarkButton.href =
- href;
- }
- }
- function webViewerScrollModeChanged(evt) {
- if (
- PDFViewerApplication.isInitialViewSet &&
- !PDFViewerApplication.pdfViewer.isInPresentationMode
- ) {
- // Only update the storage when the document has been loaded *and* rendered.
- PDFViewerApplication.store?.set("scrollMode", evt.mode).catch(() => {
- // Unable to write to storage.
- });
- }
- }
- function webViewerSpreadModeChanged(evt) {
- if (
- PDFViewerApplication.isInitialViewSet &&
- !PDFViewerApplication.pdfViewer.isInPresentationMode
- ) {
- // Only update the storage when the document has been loaded *and* rendered.
- PDFViewerApplication.store?.set("spreadMode", evt.mode).catch(() => {
- // Unable to write to storage.
- });
- }
- }
- function webViewerResize() {
- const { pdfDocument, pdfViewer, pdfRenderingQueue } = PDFViewerApplication;
- if (pdfRenderingQueue.printing && window.matchMedia("print").matches) {
- // Work-around issue 15324 by ignoring "resize" events during printing.
- return;
- }
- if (!pdfDocument) {
- return;
- }
- const currentScaleValue = pdfViewer.currentScaleValue;
- if (
- currentScaleValue === "auto" ||
- currentScaleValue === "page-fit" ||
- currentScaleValue === "page-width"
- ) {
- // Note: the scale is constant for 'page-actual'.
- pdfViewer.currentScaleValue = currentScaleValue;
- }
- pdfViewer.update();
- }
- function webViewerHashchange(evt) {
- const hash = evt.hash;
- if (!hash) {
- return;
- }
- if (!PDFViewerApplication.isInitialViewSet) {
- PDFViewerApplication.initialBookmark = hash;
- } else if (!PDFViewerApplication.pdfHistory?.popStateInProgress) {
- PDFViewerApplication.pdfLinkService.setHash(hash);
- }
- }
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- // eslint-disable-next-line no-var
- var webViewerFileInputChange = function (evt) {
- if (PDFViewerApplication.pdfViewer?.isInPresentationMode) {
- return; // Opening a new PDF file isn't supported in Presentation Mode.
- }
- const file = evt.fileInput.files[0];
- let url = URL.createObjectURL(file);
- if (file.name) {
- url = { url, originalUrl: file.name };
- }
- PDFViewerApplication.open(url);
- };
- // eslint-disable-next-line no-var
- var webViewerOpenFile = function (evt) {
- const fileInput = PDFViewerApplication.appConfig.openFileInput;
- fileInput.click();
- };
- }
- function webViewerPresentationMode() {
- PDFViewerApplication.requestPresentationMode();
- }
- function webViewerSwitchAnnotationEditorMode(evt) {
- PDFViewerApplication.pdfViewer.annotationEditorMode = evt.mode;
- }
- function webViewerSwitchAnnotationEditorParams(evt) {
- PDFViewerApplication.pdfViewer.annotationEditorParams = evt;
- }
- function webViewerPrint() {
- PDFViewerApplication.triggerPrinting();
- }
- function webViewerDownload() {
- PDFViewerApplication.downloadOrSave();
- }
- function webViewerFirstPage() {
- if (PDFViewerApplication.pdfDocument) {
- PDFViewerApplication.page = 1;
- }
- }
- function webViewerLastPage() {
- if (PDFViewerApplication.pdfDocument) {
- PDFViewerApplication.page = PDFViewerApplication.pagesCount;
- }
- }
- function webViewerNextPage() {
- PDFViewerApplication.pdfViewer.nextPage();
- }
- function webViewerPreviousPage() {
- PDFViewerApplication.pdfViewer.previousPage();
- }
- function webViewerZoomIn() {
- PDFViewerApplication.zoomIn();
- }
- function webViewerZoomOut() {
- PDFViewerApplication.zoomOut();
- }
- function webViewerZoomReset() {
- PDFViewerApplication.zoomReset();
- }
- function webViewerPageNumberChanged(evt) {
- const pdfViewer = PDFViewerApplication.pdfViewer;
- // Note that for `<input type="number">` HTML elements, an empty string will
- // be returned for non-number inputs; hence we simply do nothing in that case.
- if (evt.value !== "") {
- PDFViewerApplication.pdfLinkService.goToPage(evt.value);
- }
- // Ensure that the page number input displays the correct value, even if the
- // value entered by the user was invalid (e.g. a floating point number).
- if (
- evt.value !== pdfViewer.currentPageNumber.toString() &&
- evt.value !== pdfViewer.currentPageLabel
- ) {
- PDFViewerApplication.toolbar?.setPageNumber(
- pdfViewer.currentPageNumber,
- pdfViewer.currentPageLabel
- );
- }
- }
- function webViewerScaleChanged(evt) {
- PDFViewerApplication.pdfViewer.currentScaleValue = evt.value;
- }
- function webViewerRotateCw() {
- PDFViewerApplication.rotatePages(90);
- }
- function webViewerRotateCcw() {
- PDFViewerApplication.rotatePages(-90);
- }
- function webViewerOptionalContentConfig(evt) {
- PDFViewerApplication.pdfViewer.optionalContentConfigPromise = evt.promise;
- }
- function webViewerSwitchScrollMode(evt) {
- PDFViewerApplication.pdfViewer.scrollMode = evt.mode;
- }
- function webViewerSwitchSpreadMode(evt) {
- PDFViewerApplication.pdfViewer.spreadMode = evt.mode;
- }
- function webViewerDocumentProperties() {
- PDFViewerApplication.pdfDocumentProperties?.open();
- }
- function webViewerFindFromUrlHash(evt) {
- PDFViewerApplication.eventBus.dispatch("find", {
- source: evt.source,
- type: "",
- query: evt.query,
- phraseSearch: evt.phraseSearch,
- caseSensitive: false,
- entireWord: false,
- highlightAll: true,
- findPrevious: false,
- matchDiacritics: true,
- });
- }
- function webViewerUpdateFindMatchesCount({ matchesCount }) {
- if (PDFViewerApplication.supportsIntegratedFind) {
- PDFViewerApplication.externalServices.updateFindMatchesCount(matchesCount);
- } else {
- PDFViewerApplication.findBar.updateResultsCount(matchesCount);
- }
- }
- function webViewerUpdateFindControlState({
- state,
- previous,
- matchesCount,
- rawQuery,
- }) {
- if (PDFViewerApplication.supportsIntegratedFind) {
- PDFViewerApplication.externalServices.updateFindControlState({
- result: state,
- findPrevious: previous,
- matchesCount,
- rawQuery,
- });
- } else {
- PDFViewerApplication.findBar?.updateUIState(state, previous, matchesCount);
- }
- }
- function webViewerScaleChanging(evt) {
- PDFViewerApplication.toolbar?.setPageScale(evt.presetValue, evt.scale);
- PDFViewerApplication.pdfViewer.update();
- }
- function webViewerRotationChanging(evt) {
- if (PDFViewerApplication.pdfThumbnailViewer) {
- PDFViewerApplication.pdfThumbnailViewer.pagesRotation = evt.pagesRotation;
- }
- PDFViewerApplication.forceRendering();
- // Ensure that the active page doesn't change during rotation.
- PDFViewerApplication.pdfViewer.currentPageNumber = evt.pageNumber;
- }
- function webViewerPageChanging({ pageNumber, pageLabel }) {
- PDFViewerApplication.toolbar?.setPageNumber(pageNumber, pageLabel);
- PDFViewerApplication.secondaryToolbar?.setPageNumber(pageNumber);
- if (PDFViewerApplication.pdfSidebar?.visibleView === SidebarView.THUMBS) {
- PDFViewerApplication.pdfThumbnailViewer?.scrollThumbnailIntoView(
- pageNumber
- );
- }
- // Show/hide the loading indicator in the page number input element.
- const currentPage = PDFViewerApplication.pdfViewer.getPageView(
- /* index = */ pageNumber - 1
- );
- PDFViewerApplication.toolbar?.updateLoadingIndicatorState(
- currentPage?.renderingState === RenderingStates.RUNNING
- );
- }
- function webViewerResolutionChange(evt) {
- PDFViewerApplication.pdfViewer.refresh();
- }
- function webViewerVisibilityChange(evt) {
- if (document.visibilityState === "visible") {
- // Ignore mouse wheel zooming during tab switches (bug 1503412).
- setZoomDisabledTimeout();
- }
- }
- let zoomDisabledTimeout = null;
- function setZoomDisabledTimeout() {
- if (zoomDisabledTimeout) {
- clearTimeout(zoomDisabledTimeout);
- }
- zoomDisabledTimeout = setTimeout(function () {
- zoomDisabledTimeout = null;
- }, WHEEL_ZOOM_DISABLED_TIMEOUT);
- }
- function webViewerWheel(evt) {
- const { pdfViewer, supportedMouseWheelZoomModifierKeys } =
- PDFViewerApplication;
- if (pdfViewer.isInPresentationMode) {
- return;
- }
- if (
- (evt.ctrlKey && supportedMouseWheelZoomModifierKeys.ctrlKey) ||
- (evt.metaKey && supportedMouseWheelZoomModifierKeys.metaKey)
- ) {
- // Only zoom the pages, not the entire viewer.
- evt.preventDefault();
- // NOTE: this check must be placed *after* preventDefault.
- if (zoomDisabledTimeout || document.visibilityState === "hidden") {
- return;
- }
- // It is important that we query deltaMode before delta{X,Y}, so that
- // Firefox doesn't switch to DOM_DELTA_PIXEL mode for compat with other
- // browsers, see https://bugzilla.mozilla.org/show_bug.cgi?id=1392460.
- const deltaMode = evt.deltaMode;
- const delta = normalizeWheelEventDirection(evt);
- const previousScale = pdfViewer.currentScale;
- let ticks = 0;
- if (
- deltaMode === WheelEvent.DOM_DELTA_LINE ||
- deltaMode === WheelEvent.DOM_DELTA_PAGE
- ) {
- // For line-based devices, use one tick per event, because different
- // OSs have different defaults for the number lines. But we generally
- // want one "clicky" roll of the wheel (which produces one event) to
- // adjust the zoom by one step.
- if (Math.abs(delta) >= 1) {
- ticks = Math.sign(delta);
- } else {
- // If we're getting fractional lines (I can't think of a scenario
- // this might actually happen), be safe and use the accumulator.
- ticks = PDFViewerApplication.accumulateWheelTicks(delta);
- }
- } else {
- // pixel-based devices
- const PIXELS_PER_LINE_SCALE = 30;
- ticks = PDFViewerApplication.accumulateWheelTicks(
- delta / PIXELS_PER_LINE_SCALE
- );
- }
- if (ticks < 0) {
- PDFViewerApplication.zoomOut(-ticks);
- } else if (ticks > 0) {
- PDFViewerApplication.zoomIn(ticks);
- }
- const currentScale = pdfViewer.currentScale;
- if (previousScale !== currentScale) {
- // After scaling the page via zoomIn/zoomOut, the position of the upper-
- // left corner is restored. When the mouse wheel is used, the position
- // under the cursor should be restored instead.
- const scaleCorrectionFactor = currentScale / previousScale - 1;
- const [top, left] = pdfViewer.containerTopLeft;
- const dx = evt.clientX - left;
- const dy = evt.clientY - top;
- pdfViewer.container.scrollLeft += dx * scaleCorrectionFactor;
- pdfViewer.container.scrollTop += dy * scaleCorrectionFactor;
- }
- } else {
- setZoomDisabledTimeout();
- }
- }
- function webViewerTouchStart(evt) {
- if (evt.touches.length > 1) {
- // Disable touch-based zooming, because the entire UI bits gets zoomed and
- // that doesn't look great. If we do want to have a good touch-based
- // zooming experience, we need to implement smooth zoom capability (probably
- // using a CSS transform for faster visual response, followed by async
- // re-rendering at the final zoom level) and do gesture detection on the
- // touchmove events to drive it. Or if we want to settle for a less good
- // experience we can make the touchmove events drive the existing step-zoom
- // behaviour that the ctrl+mousewheel path takes.
- evt.preventDefault();
- }
- }
- function webViewerClick(evt) {
- if (!PDFViewerApplication.secondaryToolbar?.isOpen) {
- return;
- }
- const appConfig = PDFViewerApplication.appConfig;
- if (
- PDFViewerApplication.pdfViewer.containsElement(evt.target) ||
- (appConfig.toolbar?.container.contains(evt.target) &&
- evt.target !== appConfig.secondaryToolbar?.toggleButton)
- ) {
- PDFViewerApplication.secondaryToolbar.close();
- }
- }
- function webViewerKeyDown(evt) {
- if (PDFViewerApplication.overlayManager.active) {
- return;
- }
- const { eventBus, pdfViewer } = PDFViewerApplication;
- const isViewerInPresentationMode = pdfViewer.isInPresentationMode;
- let handled = false,
- ensureViewerFocused = false;
- const cmd =
- (evt.ctrlKey ? 1 : 0) |
- (evt.altKey ? 2 : 0) |
- (evt.shiftKey ? 4 : 0) |
- (evt.metaKey ? 8 : 0);
- // First, handle the key bindings that are independent whether an input
- // control is selected or not.
- if (cmd === 1 || cmd === 8 || cmd === 5 || cmd === 12) {
- // either CTRL or META key with optional SHIFT.
- switch (evt.keyCode) {
- case 70: // f
- if (!PDFViewerApplication.supportsIntegratedFind && !evt.shiftKey) {
- PDFViewerApplication.findBar?.open();
- handled = true;
- }
- break;
- case 71: // g
- if (!PDFViewerApplication.supportsIntegratedFind) {
- const { state } = PDFViewerApplication.findController;
- if (state) {
- const eventState = Object.assign(Object.create(null), state, {
- source: window,
- type: "again",
- findPrevious: cmd === 5 || cmd === 12,
- });
- eventBus.dispatch("find", eventState);
- }
- handled = true;
- }
- break;
- case 61: // FF/Mac '='
- case 107: // FF '+' and '='
- case 187: // Chrome '+'
- case 171: // FF with German keyboard
- if (!isViewerInPresentationMode) {
- PDFViewerApplication.zoomIn();
- }
- handled = true;
- break;
- case 173: // FF/Mac '-'
- case 109: // FF '-'
- case 189: // Chrome '-'
- if (!isViewerInPresentationMode) {
- PDFViewerApplication.zoomOut();
- }
- handled = true;
- break;
- case 48: // '0'
- case 96: // '0' on Numpad of Swedish keyboard
- if (!isViewerInPresentationMode) {
- // keeping it unhandled (to restore page zoom to 100%)
- setTimeout(function () {
- // ... and resetting the scale after browser adjusts its scale
- PDFViewerApplication.zoomReset();
- });
- handled = false;
- }
- break;
- case 38: // up arrow
- if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
- PDFViewerApplication.page = 1;
- handled = true;
- ensureViewerFocused = true;
- }
- break;
- case 40: // down arrow
- if (
- isViewerInPresentationMode ||
- PDFViewerApplication.page < PDFViewerApplication.pagesCount
- ) {
- PDFViewerApplication.page = PDFViewerApplication.pagesCount;
- handled = true;
- ensureViewerFocused = true;
- }
- break;
- }
- }
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC || CHROME")) {
- // CTRL or META without shift
- if (cmd === 1 || cmd === 8) {
- switch (evt.keyCode) {
- case 83: // s
- eventBus.dispatch("download", { source: window });
- handled = true;
- break;
- case 79: // o
- if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
- eventBus.dispatch("openfile", { source: window });
- handled = true;
- }
- break;
- }
- }
- }
- // CTRL+ALT or Option+Command
- if (cmd === 3 || cmd === 10) {
- switch (evt.keyCode) {
- case 80: // p
- PDFViewerApplication.requestPresentationMode();
- handled = true;
- PDFViewerApplication.externalServices.reportTelemetry({
- type: "buttons",
- data: { id: "presentationModeKeyboard" },
- });
- break;
- case 71: // g
- // focuses input#pageNumber field
- if (PDFViewerApplication.appConfig.toolbar) {
- PDFViewerApplication.appConfig.toolbar.pageNumber.select();
- handled = true;
- }
- break;
- }
- }
- if (handled) {
- if (ensureViewerFocused && !isViewerInPresentationMode) {
- pdfViewer.focus();
- }
- evt.preventDefault();
- return;
- }
- // Some shortcuts should not get handled if a control/input element
- // is selected.
- const curElement = getActiveOrFocusedElement();
- const curElementTagName = curElement?.tagName.toUpperCase();
- if (
- curElementTagName === "INPUT" ||
- curElementTagName === "TEXTAREA" ||
- curElementTagName === "SELECT" ||
- curElement?.isContentEditable
- ) {
- // Make sure that the secondary toolbar is closed when Escape is pressed.
- if (evt.keyCode !== /* Esc = */ 27) {
- return;
- }
- }
- // No control key pressed at all.
- if (cmd === 0) {
- let turnPage = 0,
- turnOnlyIfPageFit = false;
- switch (evt.keyCode) {
- case 38: // up arrow
- case 33: // pg up
- // vertical scrolling using arrow/pg keys
- if (pdfViewer.isVerticalScrollbarEnabled) {
- turnOnlyIfPageFit = true;
- }
- turnPage = -1;
- break;
- case 8: // backspace
- if (!isViewerInPresentationMode) {
- turnOnlyIfPageFit = true;
- }
- turnPage = -1;
- break;
- case 37: // left arrow
- // horizontal scrolling using arrow keys
- if (pdfViewer.isHorizontalScrollbarEnabled) {
- turnOnlyIfPageFit = true;
- }
- /* falls through */
- case 75: // 'k'
- case 80: // 'p'
- turnPage = -1;
- break;
- case 27: // esc key
- if (PDFViewerApplication.secondaryToolbar?.isOpen) {
- PDFViewerApplication.secondaryToolbar.close();
- handled = true;
- }
- if (
- !PDFViewerApplication.supportsIntegratedFind &&
- PDFViewerApplication.findBar?.opened
- ) {
- PDFViewerApplication.findBar.close();
- handled = true;
- }
- break;
- case 40: // down arrow
- case 34: // pg down
- // vertical scrolling using arrow/pg keys
- if (pdfViewer.isVerticalScrollbarEnabled) {
- turnOnlyIfPageFit = true;
- }
- turnPage = 1;
- break;
- case 13: // enter key
- case 32: // spacebar
- if (!isViewerInPresentationMode) {
- turnOnlyIfPageFit = true;
- }
- turnPage = 1;
- break;
- case 39: // right arrow
- // horizontal scrolling using arrow keys
- if (pdfViewer.isHorizontalScrollbarEnabled) {
- turnOnlyIfPageFit = true;
- }
- /* falls through */
- case 74: // 'j'
- case 78: // 'n'
- turnPage = 1;
- break;
- case 36: // home
- if (isViewerInPresentationMode || PDFViewerApplication.page > 1) {
- PDFViewerApplication.page = 1;
- handled = true;
- ensureViewerFocused = true;
- }
- break;
- case 35: // end
- if (
- isViewerInPresentationMode ||
- PDFViewerApplication.page < PDFViewerApplication.pagesCount
- ) {
- PDFViewerApplication.page = PDFViewerApplication.pagesCount;
- handled = true;
- ensureViewerFocused = true;
- }
- break;
- case 83: // 's'
- PDFViewerApplication.pdfCursorTools?.switchTool(CursorTool.SELECT);
- break;
- case 72: // 'h'
- PDFViewerApplication.pdfCursorTools?.switchTool(CursorTool.HAND);
- break;
- case 82: // 'r'
- PDFViewerApplication.rotatePages(90);
- break;
- case 115: // F4
- PDFViewerApplication.pdfSidebar?.toggle();
- break;
- }
- if (
- turnPage !== 0 &&
- (!turnOnlyIfPageFit || pdfViewer.currentScaleValue === "page-fit")
- ) {
- if (turnPage > 0) {
- pdfViewer.nextPage();
- } else {
- pdfViewer.previousPage();
- }
- handled = true;
- }
- }
- // shift-key
- if (cmd === 4) {
- switch (evt.keyCode) {
- case 13: // enter key
- case 32: // spacebar
- if (
- !isViewerInPresentationMode &&
- pdfViewer.currentScaleValue !== "page-fit"
- ) {
- break;
- }
- pdfViewer.previousPage();
- handled = true;
- break;
- case 82: // 'r'
- PDFViewerApplication.rotatePages(-90);
- break;
- }
- }
- if (!handled && !isViewerInPresentationMode) {
- // 33=Page Up 34=Page Down 35=End 36=Home
- // 37=Left 38=Up 39=Right 40=Down
- // 32=Spacebar
- if (
- (evt.keyCode >= 33 && evt.keyCode <= 40) ||
- (evt.keyCode === 32 && curElementTagName !== "BUTTON")
- ) {
- ensureViewerFocused = true;
- }
- }
- if (ensureViewerFocused && !pdfViewer.containsElement(curElement)) {
- // The page container is not focused, but a page navigation key has been
- // pressed. Change the focus to the viewer container to make sure that
- // navigation by keyboard works as expected.
- pdfViewer.focus();
- }
- if (handled) {
- evt.preventDefault();
- }
- }
- function beforeUnload(evt) {
- evt.preventDefault();
- evt.returnValue = "";
- return false;
- }
- function webViewerAnnotationEditorStatesChanged(data) {
- PDFViewerApplication.externalServices.updateEditorStates(data);
- }
- /* Abstract factory for the print service. */
- const PDFPrintServiceFactory = {
- instance: {
- supportsPrinting: false,
- createPrintService() {
- throw new Error("Not implemented: createPrintService");
- },
- },
- };
- export {
- DefaultExternalServices,
- PDFPrintServiceFactory,
- PDFViewerApplication,
- };
|