UNPKG

ngx-extended-pdf-viewer

Version:

Embedding PDF files in your Angular application. Highly configurable viewer including the toolbar, sidebar, and all the features you're used to.

1,076 lines 313 kB
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, HostListener, Inject, PLATFORM_ID, ViewChild, } from '@angular/core'; import { getVersionSuffix, pdfDefaultOptions } from './options/pdf-default-options'; import { VerbosityLevel } from './options/verbosity-level'; import { FindState } from './events/find-result'; import { isPlatformBrowser } from '@angular/common'; import { PdfDummyComponentsComponent } from './pdf-dummy-components/pdf-dummy-components.component'; import { PdfCursorTools } from './options/pdf-cursor-tools'; import { PinchOnMobileSupport } from './pinch-on-mobile-support'; import { ScrollModeType } from './options/pdf-viewer'; import { UnitToPx } from './unit-to-px'; import * as i0 from "@angular/core"; import * as i1 from "./pdf-notification-service"; import * as i2 from "@angular/common"; import * as i3 from "./ngx-extended-pdf-viewer.service"; import * as i4 from "./theme/pdf-dark-theme/pdf-dark-theme.component"; import * as i5 from "./theme/pdf-light-theme/pdf-light-theme.component"; import * as i6 from "./dynamic-css/dynamic-css.component"; import * as i7 from "./sidebar/pdf-sidebar/pdf-sidebar.component"; import * as i8 from "./pdf-dummy-components/pdf-dummy-components.component"; import * as i9 from "./toolbar/pdf-toolbar/pdf-toolbar.component"; import * as i10 from "./secondary-toolbar/pdf-secondary-toolbar/pdf-secondary-toolbar.component"; import * as i11 from "./toolbar/pdf-findbar/pdf-findbar.component"; import * as i12 from "./toolbar/pdf-context-menu/pdf-context-menu.component"; import * as i13 from "./document-properties/pdf-document-properties-overlay/pdf-document-properties-overlay.component"; import * as i14 from "./translate.pipe"; export class NgxExtendedPdfViewerComponent { constructor(ngZone, platformId, notificationService, location, elementRef, platformLocation, cdr, service) { this.ngZone = ngZone; this.platformId = platformId; this.notificationService = notificationService; this.location = location; this.elementRef = elementRef; this.platformLocation = platformLocation; this.cdr = cdr; this.service = service; this.ngxExtendedPdfViewerIncompletelyInitialized = true; this.showFreeFloatingBar = true; this.enableDragAndDrop = true; this.formData = {}; /** Maps the internal ids of the annotations of pdf.js to their field name */ this.formIdToFieldName = {}; this.formRadioButtonValueToId = {}; this.formDataChange = new EventEmitter(); this._pageViewMode = 'multiple'; this.progress = new EventEmitter(); this.srcChange = new EventEmitter(); this.scrollMode = undefined; this.scrollModeChange = new EventEmitter(); this.authorization = undefined; this.httpHeaders = undefined; this.contextMenuAllowed = true; this.afterPrint = new EventEmitter(); this.beforePrint = new EventEmitter(); this.currentZoomFactor = new EventEmitter(); this.enablePrint = true; /** * Number of milliseconds to wait between initializing the PDF viewer and loading the PDF file. * Most users can let this parameter safely at it's default value of zero. * Set this to 1000 or higher if you run into timing problems (typically caused by loading the locale files * after the PDF files, so they are not available when the PDF viewer is initialized). */ this.delayFirstView = 0; /** How many log messages should be printed? * Legal values: VerbosityLevel.INFOS (= 5), VerbosityLevel.WARNINGS (= 1), VerbosityLevel.ERRORS (= 0) */ this.logLevel = VerbosityLevel.WARNINGS; this.enablePinchOnMobile = false; /** Use the minified (minifiedJSLibraries="true", which is the default) or the user-readable pdf.js library (minifiedJSLibraries="false") */ this.minifiedJSLibraries = true; this.primaryMenuVisible = true; /** option to increase (or reduce) print resolution. Default is 150 (dpi). Sensible values * are 300, 600, and 1200. Note the increase memory consumption, which may even result in a browser crash. */ this.printResolution = null; this.rotationChange = new EventEmitter(); /** * The combination of height, minHeight, and autoHeight ensures the PDF height of the PDF viewer is calculated correctly when the height is a percentage. * By default, many CSS frameworks make a div with 100% have a height or zero pixels. checkHeigth() fixes this. */ this.autoHeight = false; this.minHeight = undefined; this._height = '100%'; /** * If this flag is true, this components adds a link to the locale assets. The pdf viewer * sees this link and uses it to load the locale files automatically. * @param useBrowserLocale boolean */ this.useBrowserLocale = false; this.forceUsingLegacyES5 = false; this.backgroundColor = '#e8e8eb'; this.pdfBackground = undefined; this.pdfBackgroundColorToReplace = '#ffffff'; /** Allows the user to define the name of the file after clicking "download" */ this.filenameForDownload = undefined; /** Allows the user to disable the keyboard bindings completely */ this.ignoreKeyboard = false; /** Allows the user to disable a list of key bindings. */ this.ignoreKeys = []; /** Allows the user to enable a list of key bindings explicitly. If this property is set, every other key binding is ignored. */ this.acceptKeys = []; /** Allows the user to put the viewer's svg images into an arbitrary folder */ this.imageResourcesPath = './' + pdfDefaultOptions.assetsFolder + '/images/'; /** Allows the user to put their locale folder into an arbitrary folder */ this.localeFolderPath = './' + pdfDefaultOptions.assetsFolder + '/locale'; /** Override the default locale. This must be the complete locale name, such as "es-ES". The string is allowed to be all lowercase. */ this.language = undefined; /** By default, listening to the URL is deactivated because often the anchor tag is used for the Angular router */ this.listenToURL = false; /** Navigate to a certain "named destination" */ this.nameddest = undefined; /** allows you to pass a password to read password-protected files */ this.password = undefined; this._showSidebarButton = true; this.viewerPositionTop = '32px'; /** pdf.js can show signatures, but fails to verify them. So they are switched off by default. * Set "[showUnverifiedSignatures]"="true" to display e-signatures nonetheless. */ this.showUnverifiedSignatures = false; this.sidebarVisible = undefined; this.sidebarVisibleChange = new EventEmitter(); this.showFindButton = undefined; this.showFindHighlightAll = true; this.showFindMatchCase = true; this.showFindCurrentPageOnly = true; this.showFindPageRange = true; this.showFindEntireWord = true; this.showFindEntirePhrase = true; this.showFindIgnoreAccents = true; this.showFindFuzzySearch = true; this.showFindResultsCount = true; this.showFindMessages = true; this.showPagingButtons = true; this.showZoomButtons = true; this.showPresentationModeButton = false; this.showOpenFileButton = true; this.showPrintButton = true; this.showDownloadButton = true; this.showBookmarkButton = true; this.theme = 'light'; this.showToolbar = true; this.showSecondaryToolbarButton = true; /** Set by the event (secondaryMenuIsEmpty) */ this.hideKebabMenuForSecondaryToolbar = false; this.showRotateButton = true; this.handTool = true; this.handToolChange = new EventEmitter(); this.showHandToolButton = false; this._showScrollingButton = true; this._showSpreadButton = true; this.showPropertiesButton = true; this.showBorders = true; this.spreadChange = new EventEmitter(); this.thumbnailDrawn = new EventEmitter(); this._page = undefined; this.pageChange = new EventEmitter(); this.pageLabel = undefined; this.pageLabelChange = new EventEmitter(); this.pagesLoaded = new EventEmitter(); this.pageRender = new EventEmitter(); this.pageRendered = new EventEmitter(); this.pdfDownloaded = new EventEmitter(); this.pdfLoaded = new EventEmitter(); this.pdfLoadingStarts = new EventEmitter(); this.pdfLoadingFailed = new EventEmitter(); this.textLayer = undefined; this.textLayerRendered = new EventEmitter(); this.updateFindMatchesCount = new EventEmitter(); this.updateFindState = new EventEmitter(); /** Legal values: undefined, 'auto', 'page-actual', 'page-fit', 'page-width', or '50' (or any other percentage) */ this.zoom = undefined; this.zoomChange = new EventEmitter(); this.zoomLevels = ['auto', 'page-actual', 'page-fit', 'page-width', 0.5, 1, 1.25, 1.5, 2, 3, 4]; this.maxZoom = 10; this.minZoom = 0.1; /** This attribute allows you to increase the size of the UI elements so you can use them on small mobile devices. * This attribute is a string with a percent character at the end (e.g. "150%"). */ this._mobileFriendlyZoom = '100%'; this.mobileFriendlyZoomScale = 1; this.wheelAction = 'scroll'; this.toolbarMarginTop = '0px'; this.toolbarWidth = '100%'; this.toolbarWidthInPixels = 100; this.secondaryToolbarTop = undefined; this.sidebarPositionTop = undefined; // dirty IE11 hack - temporary solution this.findbarTop = undefined; // dirty IE11 hack - temporary solution this.findbarLeft = undefined; // Additional PDF Form Field Types #567: Used to store the exported values of radio and checkbox buttons this.buttonValues = {}; this.shuttingDown = false; this.baseHref = this.platformLocation.getBaseHrefFromDOM(); this.service.recalculateSize$.subscribe(() => this.onResize()); } get pageViewMode() { return this._pageViewMode; } set pageViewMode(viewMode) { this._pageViewMode = viewMode; if (viewMode === 'infinite-scroll') { this.scrollMode = ScrollModeType.vertical; this.spread = 'off'; } else if (viewMode !== 'multiple') { this.scrollMode = ScrollModeType.vertical; } if (viewMode === 'single') { // since pdf.js, our custom single-page-mode has been replaced by the standard scrollMode="page" this.scrollMode = ScrollModeType.page; this._pageViewMode = 'multiple'; } if (viewMode === 'book') { this.showBorders = false; } } set src(url) { if (url instanceof Uint8Array) { this._src = url.buffer; } else if (url instanceof URL) { this._src = url.toString(); } else if (typeof Blob !== 'undefined' && url instanceof Blob) { // additional check introduced to support server side rendering const reader = new FileReader(); reader.onloadend = () => { setTimeout(() => { this.src = new Uint8Array(reader.result); if (NgxExtendedPdfViewerComponent.ngxExtendedPdfViewerInitialized) { if (this.ngxExtendedPdfViewerIncompletelyInitialized) { this.openPDF(); } else { (async () => await this.openPDF2())(); } // else openPDF is called later, so we do nothing to prevent loading the PDF file twice } }); }; reader.readAsArrayBuffer(url); } else if (typeof url === 'string') { this._src = url; if (url.length > 980) { // minimal length of a base64 encoded PDF if (url.length % 4 === 0) { if (/^[a-zA-Z\d\/+]+={0,2}$/.test(url)) { console.error('The URL looks like a base64 encoded string. If so, please use the attribute [base64Src] instead of [src]'); } } } } else { this._src = url; } } set base64Src(base64) { if (base64) { const binary_string = window.atob(base64); const len = binary_string.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binary_string.charCodeAt(i); } this.src = bytes.buffer; } else { this._src = undefined; } } set height(h) { this.minHeight = undefined; this.autoHeight = false; if (h) { this._height = h; } else { this.height = '100%'; } setTimeout(() => { this.checkHeight(); }); } get height() { return this._height; } get showSidebarButton() { return this._showSidebarButton; } set showSidebarButton(show) { this._showSidebarButton = show; const isIE = /msie\s|trident\//i.test(window.navigator.userAgent); let factor = 1; if (isIE) { factor = Number((this._mobileFriendlyZoom || '100').replace('%', '')) / 100; } if (this._showSidebarButton) { this.findbarLeft = (68 * factor).toString() + 'px'; } else { this.findbarLeft = '0px'; } } get showScrollingButton() { if (this.pageViewMode === 'multiple') { return this._showScrollingButton; } return false; } set showScrollingButton(val) { this._showScrollingButton = val; } get showSpreadButton() { if (this.pageViewMode !== 'infinite-scroll') { return this._showSpreadButton; } return false; } set showSpreadButton(val) { this._showSpreadButton = val; } get page() { return this._page; } set page(p) { if (p) { // silently cope with strings this._page = Number(p); } else { this._page = undefined; } } get mobileFriendlyZoom() { return this._mobileFriendlyZoom; } /** * This attributes allows you to increase the size of the UI elements so you can use them on small mobile devices. * This attribute is a string with a percent character at the end (e.g. "150%"). */ set mobileFriendlyZoom(zoom) { // tslint:disable-next-line:triple-equals - the type conversion is intended if (zoom == 'true') { zoom = '150%'; // tslint:disable-next-line:triple-equals - the type conversion is intended } else if (zoom == 'false' || zoom === undefined || zoom === null) { zoom = '100%'; } this._mobileFriendlyZoom = zoom; let factor = 1; if (!String(zoom).includes('%')) { zoom = 100 * Number(zoom) + '%'; } factor = Number((zoom || '100').replace('%', '')) / 100; this.mobileFriendlyZoomScale = factor; this.toolbarWidth = (100 / factor).toString() + '%'; this.toolbarMarginTop = (factor - 1) * 16 + 'px'; setTimeout(() => this.calcViewerPositionTop()); } calcViewerPositionTop() { const toolbar = document.getElementsByClassName('toolbar')[0]; if (toolbar === undefined) { return; } let top = toolbar.getBoundingClientRect().height; this.viewerPositionTop = top + 'px'; const factor = top / 33; this.sidebarPositionTop = (33 + 33 * (factor - 1)).toString() + 'px'; this.secondaryToolbarTop = (33 + 38 * (factor - 1)).toString() + 'px'; this.findbarTop = (34 + 54 * (factor - 1)).toString() + 'px'; const findButton = document.getElementById('viewFind'); if (findButton) { const containerPositionLeft = toolbar.getBoundingClientRect().left; const findButtonPosition = findButton.getBoundingClientRect(); const left = findButtonPosition.left - containerPositionLeft; this.findbarLeft = left + 'px'; } else if (this.showSidebarButton) { this.findbarLeft = 34 + (32 * factor).toString() + 'px'; } else { this.findbarLeft = '0px'; } } iOSVersionRequiresES5() { const match = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/); if (match !== undefined && match !== null) { return parseInt(match[1], 10) < 14; } return false; } async needsES5() { const isIE = !!window.MSInputMethodContext && !!document.documentMode; const isEdge = /Edge\/\d./i.test(navigator.userAgent); const isIOs13OrBelow = this.iOSVersionRequiresES5(); let needsES5 = typeof ReadableStream === 'undefined' || typeof Promise['allSettled'] === 'undefined'; if (needsES5 || isIE || isEdge || isIOs13OrBelow || this.forceUsingLegacyES5) { return true; } return !(await this.supportsOptionalChaining()); } supportsOptionalChaining() { return new Promise((resolve) => { const support = window.supportsOptionalChaining; support !== undefined ? resolve(support) : resolve(this.addScriptOpChainingSupport()); }); } addScriptOpChainingSupport() { return new Promise((resolve) => { const script = this.createScriptElement(pdfDefaultOptions.assetsFolder + '/op-chaining-support.js'); script.onload = () => { script.remove(); resolve(window.supportsOptionalChaining); }; script.onerror = () => { script.remove(); window.supportsOptionalChaining = false; resolve(false); }; document.body.appendChild(script); }); } createScriptElement(sourcePath) { const script = document.createElement('script'); script.async = true; script.type = 'text/javascript'; script.src = this.location.normalize(sourcePath); return script; } getPdfJsPath(artifact, needsES5) { const suffix = this.minifiedJSLibraries ? '.min.js' : '.js'; const assets = pdfDefaultOptions.assetsFolder; const versionSuffix = getVersionSuffix(assets); const artifactPath = `/${artifact}-`; const es5 = needsES5 ? '-es5' : ''; return assets + artifactPath + versionSuffix + es5 + suffix; } loadViewer() { window['ngxZone'] = this.ngZone; this.ngZone.runOutsideAngular(() => { if (!window['pdfjs-dist/build/pdf']) { setTimeout(() => this.loadViewer(), 25); } else { this.needsES5().then((needsES5) => { const viewerPath = this.getPdfJsPath('viewer', needsES5); const script = this.createScriptElement(viewerPath); // script.onload = async () => await this.addFeatures(); // DEBUG CODE!!! document.getElementsByTagName('head')[0].appendChild(script); }); } }); } addFeatures() { return new Promise((resolve) => { const script = this.createScriptElement(pdfDefaultOptions.assetsFolder + '/additional-features.js'); script.onload = () => { script.remove(); }; script.onerror = () => { script.remove(); resolve(); }; document.body.appendChild(script); }); } ngOnInit() { if (isPlatformBrowser(this.platformId)) { const link = document.createElement('link'); link.href = this.localeFolderPath + '/locale.properties'; link.setAttribute('origin', 'ngx-extended-pdf-viewer'); link.rel = 'resource'; link.type = 'application/l10n'; const widget = this.elementRef.nativeElement; widget.appendChild(link); window.getFormValue = (key) => this.getFormValue(key); window.setFormValue = (key, value) => this.setFormValue(key, value); window.registerAcroformAnnotations = (sortedAnnotations) => this.registerAcroformAnnotations(sortedAnnotations); window.assignFormIdAndFieldName = (key, fieldName, radioButtonField) => this.assignFormIdAndFieldName(key, fieldName, radioButtonField); this.loadPdfJs(); } } loadPdfJs() { window['ngxZone'] = this.ngZone; this.ngZone.runOutsideAngular(() => { if (!window['pdfjs-dist/build/pdf']) { this.needsES5().then((needsES5) => { if (needsES5) { if (!pdfDefaultOptions.needsES5) { console.log("If you see the error message \"expected expression, got '='\" above: you can safely ignore it as long as you know what you're doing. It means your browser is out-of-date. Please update your browser to benefit from the latest security updates and to enjoy a faster PDF viewer."); } pdfDefaultOptions.needsES5 = true; console.log('Using the ES5 version of the PDF viewer. Your PDF files show faster if you update your browser.'); } window['ngxZone'] = this.ngZone; if (this.minifiedJSLibraries) { if (!pdfDefaultOptions.workerSrc().endsWith('.min.js')) { const src = pdfDefaultOptions.workerSrc(); pdfDefaultOptions.workerSrc = () => src.replace('.js', '.min.js'); } } const pdfJsPath = this.getPdfJsPath('pdf', needsES5); const script = this.createScriptElement(pdfJsPath); document.getElementsByTagName('head')[0].appendChild(script); }); } if (!window.webViewerLoad) { this.loadViewer(); } }); } ngAfterViewInit() { if (typeof window !== 'undefined') { if (!this.shuttingDown) { // hurried users sometimes reload the PDF before it has finished initializing if (window.webViewerLoad) { this.ngZone.runOutsideAngular(() => this.doInitPDFViewer()); } else { setTimeout(() => this.ngAfterViewInit(), 50); } } } } assignTabindexes() { if (this.startTabindex) { const r = this.root.nativeElement.cloneNode(true); r.classList.add('offscreen'); this.showElementsRecursively(r); document.body.appendChild(r); const elements = this.collectElementPositions(r, this.root.nativeElement, []); document.body.removeChild(r); const sorted = elements.sort((a, b) => { if (a.y - b.y > 15) { return 1; } if (b.y - a.y > 15) { return -1; } return a.x - b.x; }); for (let i = 0; i < sorted.length; i++) { sorted[i].element.tabIndex = this.startTabindex + i; } } } showElementsRecursively(root) { root.classList.remove('hidden'); root.classList.remove('invisible'); root.classList.remove('hiddenXXLView'); root.classList.remove('hiddenXLView'); root.classList.remove('hiddenLargeView'); root.classList.remove('hiddenMediumView'); root.classList.remove('hiddenSmallView'); root.classList.remove('hiddenTinyView'); root.classList.remove('visibleXXLView'); root.classList.remove('visibleXLView'); root.classList.remove('visibleLargeView'); root.classList.remove('visibleMediumView'); root.classList.remove('visibleSmallView'); root.classList.remove('visibleTinyView'); if (root instanceof HTMLButtonElement || root instanceof HTMLAnchorElement || root instanceof HTMLInputElement || root instanceof HTMLSelectElement) { return; } else if (root.childElementCount > 0) { for (let i = 0; i < root.childElementCount; i++) { const c = root.children.item(i); if (c) { this.showElementsRecursively(c); } } } } collectElementPositions(copy, original, elements) { if (copy instanceof HTMLButtonElement || copy instanceof HTMLAnchorElement || copy instanceof HTMLInputElement || copy instanceof HTMLSelectElement) { const rect = copy.getBoundingClientRect(); const elementAndPos = { element: original, x: Math.round(rect.left), y: Math.round(rect.top), }; elements.push(elementAndPos); } else if (copy.childElementCount > 0) { for (let i = 0; i < copy.childElementCount; i++) { const c = copy.children.item(i); const o = original.children.item(i); if (c && o) { elements = this.collectElementPositions(c, o, elements); } } } return elements; } doInitPDFViewer() { if (typeof window === 'undefined') { return; } const langLinks = document.querySelectorAll('link[type="application/l10n"]'); const langCount = langLinks.length; if (langCount === 0) { const dict = document.querySelector('script[type="application/l10n"]'); if (!dict) { if (!this.useBrowserLocale) { console.error( // tslint:disable-next-line:quotemark "If you set the attribute 'useBrowserLocale' to false, you must provide the translations yourself in a script or link tag."); console.error('The easiest way to do this is to add them to the index.html.'); } } else if (this.useBrowserLocale) { console.error( // tslint:disable-next-line:quotemark "Please set the attribute 'useBrowserLocale' to false if you provide the translations yourself in a script or link tag."); } } else if (this.useBrowserLocale) { const o = langLinks[0].attributes['origin']; if (o && o.value !== 'ngx-extended-pdf-viewer') { console.error( // tslint:disable-next-line:quotemark "Please set the attribute 'useBrowserLocale' to false if you provide the translations yourself in a script or link tag."); } } const callback = (e) => { document.removeEventListener('localized', callback); this.initTimeout = setTimeout(() => { if (!this.shuttingDown) { // hurried users sometimes reload the PDF before it has finished initializing this.calcViewerPositionTop(); this.afterLibraryInit(); this.openPDF(); this.assignTabindexes(); } }, this.delayFirstView); }; window.addEventListener('afterprint', (event) => { this.afterPrint.emit(); }); window.addEventListener('beforeprint', (event) => { this.beforePrint.emit(); }); document.addEventListener('localized', callback); if (NgxExtendedPdfViewerComponent.ngxExtendedPdfViewerInitialized) { // tslint:disable-next-line:quotemark console.error("You're trying to open two instances of the PDF viewer. Most likely, this will result in errors."); } const onLoaded = () => { this.overrideDefaultSettings(); document.removeEventListener('webviewerloaded', onLoaded); if (this.enablePinchOnMobile) { this.pinchOnMobileSupport = new PinchOnMobileSupport(this.ngZone); } }; document.addEventListener('webviewerloaded', onLoaded); this.activateTextlayerIfNecessary(null); setTimeout(() => { if (!this.shuttingDown) { // hurried users sometimes reload the PDF before it has finished initializing // This initializes the webviewer, the file may be passed in to it to initialize the viewer with a pdf directly this.onResize(); this.primaryMenuVisible = this.showToolbar; const hideSecondaryMenu = this.hideKebabMenuForSecondaryToolbar && !this.showSecondaryToolbarButton; if (hideSecondaryMenu) { if (!this.isPrimaryMenuVisible()) { this.primaryMenuVisible = false; } } this.dummyComponents.addMissingStandardWidgets(); this.ngZone.runOutsideAngular(() => window.webViewerLoad()); const PDFViewerApplication = window.PDFViewerApplication; PDFViewerApplication.appConfig.defaultUrl = ''; // IE bugfix if (this.filenameForDownload) { PDFViewerApplication.appConfig.filenameForDownload = this.filenameForDownload; } const PDFViewerApplicationOptions = window.PDFViewerApplicationOptions; PDFViewerApplicationOptions.set('enableDragAndDrop', this.enableDragAndDrop); let language = this.language === '' ? undefined : this.language; if (!language) { language = navigator.language; } PDFViewerApplicationOptions.set('locale', language); PDFViewerApplicationOptions.set('imageResourcesPath', this.imageResourcesPath); PDFViewerApplicationOptions.set('minZoom', this.minZoom); PDFViewerApplicationOptions.set('maxZoom', this.maxZoom); PDFViewerApplicationOptions.set('pageViewMode', this.pageViewMode); PDFViewerApplicationOptions.set('verbosity', this.logLevel); PDFViewerApplicationOptions.set('initialZoom', this.zoom); PDFViewerApplicationOptions.set('pdfBackgroundColor', this.pdfBackground); PDFViewerApplicationOptions.set('pdfBackgroundColorToReplace', this.pdfBackgroundColorToReplace); PDFViewerApplication.isViewerEmbedded = true; if (PDFViewerApplication.printKeyDownListener) { window.addEventListener('keydown', PDFViewerApplication.printKeyDownListener, true); } const pc = document.getElementById('printContainer'); if (pc) { document.getElementsByTagName('body')[0].appendChild(pc); } } }, 0); } /** Notifies every widget that implements onLibraryInit() that the PDF viewer objects are available */ afterLibraryInit() { this.notificationService.onPDFJSInit.next(); } checkHeight() { if (typeof document !== 'undefined') { const container = document.getElementsByClassName('zoom')[0]; if (container) { if (container.clientHeight === 0) { if (!this.autoHeight) { console.warn("The height of the PDF viewer widget is zero pixels. Please check the height attribute. Is there a syntax error? Or are you using a percentage with a CSS framework that doesn't support this? The height is adjusted automatedly."); this.autoHeight = true; } } if (this.autoHeight) { const available = window.innerHeight; const rect = container.getBoundingClientRect(); const top = rect.top; let maximumHeight = available - top; // take the margins and paddings of the parent containers into account const padding = this.calculateBorderMarging(container); maximumHeight -= padding; const factor = Number(this._height.replace('%', '')); maximumHeight = (maximumHeight * factor) / 100; if (maximumHeight > 100) { this.minHeight = `${maximumHeight}px`; } else { this.minHeight = '100px'; } this.cdr.markForCheck(); } } } } calculateBorderMarging(container) { if (container) { const computedStyle = window.getComputedStyle(container); const padding = UnitToPx.toPx(computedStyle.paddingBottom); const margin = UnitToPx.toPx(computedStyle.marginBottom); if (container.style.zIndex) { return padding + margin; } return padding + margin + this.calculateBorderMarging(container.parentElement); } return 0; } onSpreadChange(newSpread) { this.spreadChange.emit(newSpread); } activateTextlayerIfNecessary(options) { if (this.textLayer === undefined) { if (!this.handTool) { if (options) { options.set('textLayerMode', pdfDefaultOptions.textLayerMode); } this.textLayer = true; if (this.showFindButton === undefined) { this.showFindButton = true; setTimeout(() => { // todo remove this hack: const viewFind = document.getElementById('viewFind'); if (viewFind) { viewFind.classList.remove('invisible'); } const findbar = document.getElementById('findbar'); if (findbar) { findbar.classList.remove('invisible'); } }); } } else { if (options) { options.set('textLayerMode', this.showHandToolButton ? pdfDefaultOptions.textLayerMode : 0); } if (!this.showHandToolButton) { if (this.showFindButton || this.showFindButton === undefined) { this.ngZone.run(() => { this.showFindButton = false; }); if (this.logLevel >= VerbosityLevel.WARNINGS) { console.warn( // tslint:disable-next-line:max-line-length 'Hiding the "find" button because the text layer of the PDF file is not rendered. Use [textLayer]="true" to enable the find button.'); } } if (this.showHandToolButton) { if (this.logLevel >= VerbosityLevel.WARNINGS) { console.warn( // tslint:disable-next-line:max-line-length 'Hiding the "hand tool / selection mode" menu because the text layer of the PDF file is not rendered. Use [textLayer]="true" to enable the the menu items.'); this.showHandToolButton = false; } } } } } else { if (this.textLayer) { // todo: is this a redundant check? if (options) { options.set('textLayerMode', pdfDefaultOptions.textLayerMode); } this.textLayer = true; if (this.showFindButton === undefined) { this.showFindButton = true; setTimeout(() => { // todo remove this hack: const viewFind = document.getElementById('viewFind'); if (viewFind) { viewFind.classList.remove('invisible'); } const findbar = document.getElementById('findbar'); if (findbar) { findbar.classList.remove('invisible'); } }); } } else { // todo: is the else branch dead code? if (options) { options.set('textLayerMode', 0); } this.textLayer = false; if (this.showFindButton) { if (this.logLevel >= VerbosityLevel.WARNINGS) { // tslint:disable-next-line:max-line-length console.warn('Hiding the "find" button because the text layer of the PDF file is not rendered. Use [textLayer]="true" to enable the find button.'); this.ngZone.run(() => { this.showFindButton = false; }); } } if (this.showHandToolButton) { if (this.logLevel >= VerbosityLevel.WARNINGS) { console.warn( // tslint:disable-next-line:max-line-length 'Hiding the "hand tool / selection mode" menu because the text layer of the PDF file is not rendered. Use [textLayer]="true" to enable the the menu items.'); this.showHandToolButton = false; } } } } } async overrideDefaultSettings() { const options = window.PDFViewerApplicationOptions; // tslint:disable-next-line:forin for (const key in pdfDefaultOptions) { options.set(key, pdfDefaultOptions[key]); } options.set('disablePreferences', true); await this.setZoom(); options.set('ignoreKeyboard', this.ignoreKeyboard); options.set('ignoreKeys', this.ignoreKeys); options.set('acceptKeys', this.acceptKeys); options.set('wheelAction', this.wheelAction); this.activateTextlayerIfNecessary(options); if (this.scrollMode || this.scrollMode === ScrollModeType.vertical) { options.set('scrollModeOnLoad', this.scrollMode); } const sidebarVisible = this.sidebarVisible; const PDFViewerApplication = window.PDFViewerApplication; if (sidebarVisible !== undefined) { PDFViewerApplication.sidebarViewOnLoad = sidebarVisible ? 1 : 0; if (PDFViewerApplication.appConfig) { PDFViewerApplication.appConfig.sidebarViewOnLoad = sidebarVisible ? 1 : 0; } options.set('sidebarViewOnLoad', this.sidebarVisible ? 1 : 0); } if (this.spread === 'even') { options.set('spreadModeOnLoad', 2); if (PDFViewerApplication.pdfViewer) { PDFViewerApplication.pdfViewer.spreadMode = 2; } this.onSpreadChange('even'); } else if (this.spread === 'odd') { options.set('spreadModeOnLoad', 1); if (PDFViewerApplication.pdfViewer) { PDFViewerApplication.pdfViewer.spreadMode = 1; } this.onSpreadChange('odd'); } else { options.set('spreadModeOnLoad', 0); if (PDFViewerApplication.pdfViewer) { PDFViewerApplication.pdfViewer.spreadMode = 0; } this.onSpreadChange('off'); } if (this.printResolution) { options.set('printResolution', this.printResolution); } if (this.showBorders === false) { options.set('removePageBorders', !this.showBorders); } } openPDF() { ServiceWorkerOptions.showUnverifiedSignatures = this.showUnverifiedSignatures; const PDFViewerApplication = window.PDFViewerApplication; PDFViewerApplication.enablePrint = this.enablePrint; NgxExtendedPdfViewerComponent.ngxExtendedPdfViewerInitialized = true; if (this._src) { this.ngxExtendedPdfViewerIncompletelyInitialized = false; if (!this.listenToURL) { PDFViewerApplication.pdfLinkService.setHash = function () { }; } this.initTimeout = null; this.selectCursorTool(); PDFViewerApplication.eventBus.on('textlayerrendered', (x) => { this.textLayerRendered.emit(x); }); PDFViewerApplication.eventBus.on('scrollmodechanged', (x) => { this.ngZone.run(() => this.scrollModeChange.emit(x.mode)); }); PDFViewerApplication.eventBus.on('progress', (x) => { this.progress.emit(x); }); PDFViewerApplication.eventBus.on('pagesloaded', async (x) => { this.pagesLoaded.emit(x); this.removeScrollbarInInititeScrollMode(); if (this.rotation !== undefined && this.rotation !== null) { const r = Number(this.rotation); if (r === 0 || r === 90 || r === 180 || r === 270) { PDFViewerApplication.pdfViewer.pagesRotation = r; } } setTimeout(() => { if (!this.shuttingDown) { // hurried users sometimes reload the PDF before it has finished initializing if (this.nameddest) { PDFViewerApplication.pdfLinkService.navigateTo(this.nameddest); } else if (this.page) { PDFViewerApplication.page = Number(this.page); } else if (this.pageLabel) { PDFViewerApplication.pdfViewer.currentPageLabel = this.pageLabel; } } }); await this.setZoom(); }); PDFViewerApplication.eventBus.on('pagerendered', (x) => { this.ngZone.run(() => { this.pageRendered.emit(x); this.removeScrollbarInInititeScrollMode(); }); }); PDFViewerApplication.eventBus.on('pagerender', (x) => { this.ngZone.run(() => { this.pageRender.emit(x); }); }); PDFViewerApplication.eventBus.on('download', (x) => { this.ngZone.run(() => { this.pdfDownloaded.emit(x); }); }); PDFViewerApplication.eventBus.on('scalechanging', (x) => { { const scale = this.root.nativeElement.querySelector('#scaleSelect'); let userZoomFactor = ''; if (scale) { userZoomFactor = scale.value; } } setTimeout(() => { this.currentZoomFactor.emit(x.scale); this.cdr.markForCheck(); }); const scale = this.root.nativeElement.querySelector('#scaleSelect'); let userZoomFactor = this.zoom; if (scale) { userZoomFactor = scale.value; } if (userZoomFactor !== 'auto' && userZoomFactor !== 'page-fit' && userZoomFactor !== 'page-actual' && userZoomFactor !== 'page-width') { const before = this.zoom; const after = x.scale * 100; // ignore rounding differences if (Math.abs(before - after) > 0.000001) { this.zoom = after; this.zoomChange.emit(x.scale * 100); } } else if (this.zoom !== userZoomFactor) { // called when the user selects one of the text values of the zoom select dropdown this.zoomChange.emit(userZoomFactor); } }); PDFViewerApplication.eventBus.on('rotationchanging', (x) => { this.ngZone.run(() => { this.rotationChange.emit(x.pagesRotation); }); }); PDFViewerApplication.eventBus.on('fileinputchange', (x) => { this.ngZone.run(() => { if (x.fileInput.files && x.fileInput.files.length >= 1) { // drag and drop this.srcChange.emit(x.fileInput.files[0].name); } else { // regular file open dialog const path = x.fileInput?.value?.replace('C:\\fakepath\\', ''); this.srcChange.emit(path); } }); }); PDFViewerApplication.eventBus.on('cursortoolchanged', (x) => { this.ngZone.run(() => { this.handToolChange.emit(x.tool === PdfCursorTools.HAND); }); }); PDFViewerApplication.eventBus.on('sidebarviewchanged', (x) => { this.ngZone.run(() => { this.sidebarVisibleChange.emit(x.view > 0); if (this.sidebarComponent) { this.sidebarComponent.showToolbarWhenNecessary(); } }); }); PDFViewerApplication.eventBus.on('documentloaded', (pdfLoadedEvent) => { this.ngZone.run(() => { this.loadComplete(pdfLoadedEvent.source.pdfDocument); }); }); const hideSidebarToolbar = () => { this.ngZone.run(() => { if (this.sidebarComponent) { this.sidebarComponent.showToolbarWhenNecessary(); } }); }; PDFViewerApplication.eventBus.on('outlineloaded', hideSidebarToolbar); PDFViewerApplication.eventBus.on('attachmentsloaded', hideSidebarToolbar); PDFViewerApplication.eventBus.on('layersloaded', hideSidebarToolbar); PDFViewerApplication.eventBus.on('updatefindcontrolstate', (x) => { if (x.state === FindState.NOT_FOUND) { this.updateFindMatchesCount.emit({ current: 0, total: 0 }); } else if (x.matchesCount.total) { x.matchesCount.matches = PDFViewerApplication.findController._pageMatches; x.matchesCount.matchesLength = PDFViewerApplication.findController._pageMatchesLength; x.matchesCount.matchesColor = PDFViewerApplication.findController._pageMatchesColor; this.updateFindMatchesCount.emit(x.matchesCount); } if (this.updateFindState) { this.updateFindState.emit(x.state); } }); PDFViewerApplication.eventBus.on('updatefindmatchescount', (x) => { x.matchesCount.matches = PDFViewerApplication.findController._pageMatches; x.matchesCount.matchesLength = PDFViewerApplication.findController._pageMatchesLength; x.matchesCount.matchesColor = PDFViewerApplication.findController._pageMatchesColor; this.updateFindMatchesCount.emit(x.matchesCount); }); PDFViewerApplication.eventBus.on('pagechanging', (x) => { if (!this.shuttingDown) { // hurried users sometimes reload the PDF before it has finished initializing this.ngZone.run(() => { const currentPage = PDFViewerApplication.pdfViewer.currentPageNumber; const currentPageLabel = PDFViewerApplication.pdfViewer.currentPageLabel; if (currentPage !== this.page) { this.pageChange.emit(currentPage); } if (currentPageLabel !== this.pageLabel) { this.pageLabelChange.emit(currentPageLabel); } }); } }); setTimeout(async () => await this.c