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
JavaScript
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