extract web viewer instance into a service

This commit is contained in:
Dan Percic 2022-03-08 12:36:25 +02:00
parent 98a25abd27
commit 7baa9b9f56
7 changed files with 174 additions and 136 deletions

View File

@ -2,18 +2,18 @@
<div #viewer [id]="(stateService.file$ | async).fileId" class="viewer"></div>
</div>
<input #compareFileInput (change)="uploadFile($event.target['files'])" class="file-upload-input" type="file" accept="application/pdf" />
<input #compareFileInput (change)="uploadFile($event.target['files'])" accept="application/pdf" class="file-upload-input" type="file" />
<div *ngIf="utils?.totalPages && utils?.currentPage" class="pagination noselect">
<div (click)="utils.previousPage()">
<div *ngIf="pdf?.totalPages && pdf?.currentPage" class="pagination noselect">
<div (click)="pdf.previousPage()">
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
</div>
<div>
<input
#pageInput
(change)="utils.navigateToPage(pageInput.value)"
[max]="utils.totalPages"
[value]="utils.currentPage"
(change)="pdf.navigateToPage(pageInput.value)"
[max]="pdf.totalPages"
[value]="pdf.currentPage"
class="page-number-input"
min="1"
type="number"
@ -21,9 +21,9 @@
</div>
<div class="separator">/</div>
<div>
{{ utils.totalPages }}
{{ pdf.totalPages }}
</div>
<div (click)="utils.nextPage()">
<div (click)="pdf.nextPage()">
<mat-icon class="chevron-icon" svgIcon="red:nav-next"></mat-icon>
</div>
</div>

View File

@ -12,7 +12,7 @@ import {
ViewChild,
} from '@angular/core';
import { Dossier, File, IHeaderElement, IManualRedactionEntry, RotationTypes } from '@red/domain';
import WebViewer, { Core, WebViewerInstance } from '@pdftron/webviewer';
import { Core, WebViewerInstance } from '@pdftron/webviewer';
import { TranslateService } from '@ngx-translate/core';
import {
ManualRedactionEntryType,
@ -30,7 +30,7 @@ import { ConfigService } from '@services/config.service';
import { AutoUnsubscribe, ConfirmationDialogInput, LoadingService } from '@iqser/common-ui';
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
import { loadCompareDocumentWrapper } from '../../../../utils/compare-mode.utils';
import { PdfViewerUtils } from '../../../../utils/pdf-viewer.utils';
import { PdfViewer } from '../../services/pdf-viewer.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ActivatedRoute } from '@angular/router';
import { toPosition } from '../../../../utils/pdf-calculation.utils';
@ -40,30 +40,11 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
import { tap, withLatestFrom } from 'rxjs/operators';
import { FileManagementService } from '../../../../../../services/entity-services/file-management.service';
import { PageRotationService } from '../../services/page-rotation.service';
import { ALLOWED_KEYBOARD_SHORTCUTS, HeaderElements, TextPopups } from '../../shared/constants';
import Tools = Core.Tools;
import TextTool = Tools.TextTool;
import Annotation = Core.Annotations.Annotation;
const ALLOWED_KEYBOARD_SHORTCUTS: readonly string[] = ['+', '-', 'p', 'r', 'Escape'] as const;
const headerElements = {
SHAPE_TOOL_GROUP_BUTTON: 'shapeToolGroupButton',
ROTATE_LEFT_BUTTON: 'rotateLeftButton',
ROTATE_RIGHT_BUTTON: 'rotateRightButton',
APPLY_ROTATION: 'applyRotation',
DISCARD_ROTATION: 'discardRotation',
TOGGLE_TOOLTIPS: 'toggle-tooltips',
ANNOTATION_POPUP: 'annotationPopup',
COMPARE_BUTTON: 'compareButton',
CLOSE_COMPARE_BUTTON: 'closeCompareButton',
} as const;
const textPopups = {
ADD_REDACTION: 'add-redaction',
ADD_DICTIONARY: 'add-dictionary',
ADD_RECTANGLE: 'add-rectangle',
ADD_FALSE_POSITIVE: 'add-false-positive',
} as const;
function getDivider(hiddenOn?: readonly ('desktop' | 'mobile' | 'tablet')[]) {
return {
type: 'divider',
@ -92,7 +73,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
instance: WebViewerInstance;
documentViewer: Core.DocumentViewer;
annotationManager: Core.AnnotationManager;
utils: PdfViewerUtils;
private _selectedText = '';
constructor(
@ -112,6 +92,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
readonly stateService: FilePreviewStateService,
readonly viewModeService: ViewModeService,
readonly multiSelectService: MultiSelectService,
readonly pdf: PdfViewer,
) {
super();
}
@ -173,7 +154,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
const loadCompareDocument = async () => {
this._loadingService.start();
this.utils.ready = false;
this.pdf.ready = false;
const mergedDocument = await pdfNet.PDFDoc.create();
const file = await this.stateService.file;
await loadCompareDocumentWrapper(
@ -186,7 +167,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.viewModeService.compareMode = true;
},
() => {
this.utils.navigateToPage(1);
this.pdf.navigateToPage(1);
},
this.instance.Core.PDFNet,
);
@ -229,30 +210,19 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
const filename = (await this.stateService.file).filename ?? 'document.pdf';
this.instance.UI.loadDocument(currentDocument, { filename });
this.instance.UI.disableElements([headerElements.CLOSE_COMPARE_BUTTON]);
this.instance.UI.enableElements([headerElements.COMPARE_BUTTON]);
this.utils.navigateToPage(1);
this.instance.UI.disableElements([HeaderElements.CLOSE_COMPARE_BUTTON]);
this.instance.UI.enableElements([HeaderElements.COMPARE_BUTTON]);
this.pdf.navigateToPage(1);
}
private async _loadViewer() {
this.instance = await WebViewer(
{
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
fullAPI: true,
path: this._convertPath('/assets/wv-resources'),
css: this._convertPath('/assets/pdftron/stylesheet.css'),
backendType: 'ems',
},
this.viewer.nativeElement as HTMLElement,
);
this.documentViewer = this.instance.Core.documentViewer;
this.annotationManager = this.instance.Core.annotationManager;
this.utils = new PdfViewerUtils(this.instance, this.viewModeService);
this.instance = await this.pdf.loadViewer(this.viewer.nativeElement as HTMLElement);
this.documentViewer = this.pdf.documentViewer;
this.annotationManager = this.pdf.annotationManager;
this._setSelectionMode();
this._configureElements();
this.utils.disableHotkeys();
this.pdf.disableHotkeys();
await this._configureTextPopup();
this.annotationManager.addEventListener('annotationSelected', (annotations: Annotation[], action) => {
@ -262,7 +232,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this._toggleRectangleAnnotationAction(true);
} else {
if (!this.multiSelectService.isEnabled) {
this.utils.deselectAnnotations(this.annotations.filter(wrapper => !nextAnnotations.find(ann => ann.Id === wrapper.id)));
this.pdf.deselectAnnotations(this.annotations.filter(wrapper => !nextAnnotations.find(ann => ann.Id === wrapper.id)));
}
this._configureAnnotationSpecificActions(annotations);
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
@ -280,7 +250,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
});
this.documentViewer.addEventListener('pageNumberUpdated', (pageNumber: number) => {
this.utils.deselectAllAnnotations();
this.pdf.deselectAllAnnotations();
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
return this._handleCustomActions();
});
@ -305,7 +275,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.documentViewer.addEventListener('textSelected', async (quads, selectedText, pageNumber: number) => {
this._selectedText = selectedText;
const textActions = [textPopups.ADD_DICTIONARY, textPopups.ADD_FALSE_POSITIVE];
const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
const file = await this.stateService.file;
@ -315,7 +285,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.instance.UI.enableElements(['textPopup']);
}
if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded(file)) {
if (selectedText.length > 2 && this.canPerformActions && !this.pdf.isCurrentPageExcluded(file)) {
this.instance.UI.enableElements(textActions);
} else {
this.instance.UI.disableElements(textActions);
@ -353,9 +323,9 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
private _toggleRectangleAnnotationAction(readonly = false) {
if (!readonly) {
this.instance.UI.enableElements([textPopups.ADD_RECTANGLE]);
this.instance.UI.enableElements([TextPopups.ADD_RECTANGLE]);
} else {
this.instance.UI.disableElements([textPopups.ADD_RECTANGLE]);
this.instance.UI.disableElements([TextPopups.ADD_RECTANGLE]);
}
}
@ -385,7 +355,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
const applyRotation: IHeaderElement = {
type: 'customElement',
dataElement: headerElements.APPLY_ROTATION,
dataElement: HeaderElements.APPLY_ROTATION,
render: () => {
const paragraph = document.createElement('p');
paragraph.innerText = 'APPLY';
@ -398,14 +368,13 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
`;
paragraph.addEventListener('click', async () => {
await this._pageRotationService.applyRotation();
this._showRotationConfirmationButtons();
});
return paragraph;
},
};
const discardRotation: IHeaderElement = {
type: 'customElement',
dataElement: headerElements.DISCARD_ROTATION,
dataElement: HeaderElements.DISCARD_ROTATION,
render: () => {
const paragraph = document.createElement('p');
paragraph.innerText = 'DISCARD';
@ -417,16 +386,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
opacity: 0.7;
`;
paragraph.addEventListener('click', () => {
const rotations = this._pageRotationService.rotations$.value;
const oneRotationDegree = 90;
for (const page of Object.keys(rotations)) {
const times = rotations[page] / oneRotationDegree;
for (let i = 1; i <= times; i++) {
this.documentViewer.rotateCounterClockwise(Number(page));
}
}
this._pageRotationService.clearRotations();
this._showRotationConfirmationButtons();
this._pageRotationService.discardRotation();
});
return paragraph;
},
@ -438,7 +398,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
{
type: 'actionButton',
element: 'compare',
dataElement: headerElements.COMPARE_BUTTON,
dataElement: HeaderElements.COMPARE_BUTTON,
img: this._convertPath('/assets/icons/general/pdftron-action-compare.svg'),
title: 'Compare',
onClick: () => {
@ -448,7 +408,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
{
type: 'actionButton',
element: 'closeCompare',
dataElement: headerElements.CLOSE_COMPARE_BUTTON,
dataElement: HeaderElements.CLOSE_COMPARE_BUTTON,
img: this._convertPath('/assets/icons/general/pdftron-action-close-compare.svg'),
title: 'Leave Compare Mode',
onClick: async () => {
@ -459,13 +419,13 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
{
type: 'actionButton',
element: 'tooltips',
dataElement: headerElements.TOGGLE_TOOLTIPS,
dataElement: HeaderElements.TOGGLE_TOOLTIPS,
img: this._toggleTooltipsIcon,
title: this._toggleTooltipsBtnTitle,
onClick: async () => {
await this._userPreferenceService.toggleFilePreviewTooltipsPreference();
this._updateTooltipsVisibility();
this.instance.UI.updateElement(headerElements.TOGGLE_TOOLTIPS, {
this.instance.UI.updateElement(HeaderElements.TOGGLE_TOOLTIPS, {
title: this._toggleTooltipsBtnTitle,
img: this._toggleTooltipsIcon,
});
@ -475,7 +435,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
{
type: 'toolGroupButton',
toolGroup: 'rectangleTools',
dataElement: headerElements.SHAPE_TOOL_GROUP_BUTTON,
dataElement: HeaderElements.SHAPE_TOOL_GROUP_BUTTON,
img: this._convertPath('/assets/icons/general/rectangle.svg'),
title: 'annotation.rectangle',
},
@ -483,24 +443,16 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
{
type: 'actionButton',
element: 'tooltips',
dataElement: headerElements.ROTATE_LEFT_BUTTON,
dataElement: HeaderElements.ROTATE_LEFT_BUTTON,
img: this._convertPath('/assets/icons/general/rotate-left.svg'),
onClick: () => {
this._pageRotationService.addRotation(this.utils.currentPage, RotationTypes.LEFT);
this.documentViewer.rotateCounterClockwise(this.utils.currentPage);
this._showRotationConfirmationButtons();
},
onClick: () => this._pageRotationService.addRotation(RotationTypes.LEFT),
},
{
type: 'actionButton',
element: 'tooltips',
dataElement: headerElements.ROTATE_RIGHT_BUTTON,
dataElement: HeaderElements.ROTATE_RIGHT_BUTTON,
img: this._convertPath('/assets/icons/general/rotate-right.svg'),
onClick: () => {
this._pageRotationService.addRotation(this.utils.currentPage, RotationTypes.RIGHT);
this.documentViewer.rotateClockwise(this.utils.currentPage);
this._showRotationConfirmationButtons();
},
onClick: () => this._pageRotationService.addRotation(RotationTypes.RIGHT),
},
applyRotation,
discardRotation,
@ -512,9 +464,9 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
});
this.instance.UI.disableElements([
headerElements.CLOSE_COMPARE_BUTTON,
headerElements.APPLY_ROTATION,
headerElements.DISCARD_ROTATION,
HeaderElements.CLOSE_COMPARE_BUTTON,
HeaderElements.APPLY_ROTATION,
HeaderElements.DISCARD_ROTATION,
]);
const dossierTemplateId = this.dossier.dossierTemplateId;
@ -527,16 +479,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
});
}
private _showRotationConfirmationButtons() {
const rotationElements = [headerElements.APPLY_ROTATION, headerElements.DISCARD_ROTATION];
if (this._pageRotationService.hasRotations()) {
this.instance.UI.enableElements(rotationElements);
} else {
this.instance.UI.disableElements(rotationElements);
}
}
private _configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) {
if (!this.canPerformActions) {
return;
@ -591,7 +533,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.instance.UI.annotationPopup.add([
{
type: 'actionButton',
dataElement: textPopups.ADD_RECTANGLE,
dataElement: TextPopups.ADD_RECTANGLE,
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this.#getTitle(ManualRedactionEntryTypes.REDACTION),
onClick: () => this._addRectangleManualRedaction(),
@ -611,8 +553,8 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
}
private _cleanUpSelectionAndButtonState() {
const rectangleElements = [headerElements.SHAPE_TOOL_GROUP_BUTTON];
this.utils.deselectAllAnnotations();
const rectangleElements = [HeaderElements.SHAPE_TOOL_GROUP_BUTTON];
this.pdf.deselectAllAnnotations();
this.instance.UI.disableElements(rectangleElements);
this.instance.UI.enableElements(rectangleElements);
}
@ -642,7 +584,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
if (this._userPreferenceService.areDevFeaturesEnabled) {
popups.push({
type: 'actionButton',
dataElement: textPopups.ADD_FALSE_POSITIVE,
dataElement: TextPopups.ADD_FALSE_POSITIVE,
img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'),
title: this.#getTitle(ManualRedactionEntryTypes.FALSE_POSITIVE),
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.FALSE_POSITIVE),
@ -651,14 +593,14 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
popups.push({
type: 'actionButton',
dataElement: textPopups.ADD_REDACTION,
dataElement: TextPopups.ADD_REDACTION,
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this.#getTitle(ManualRedactionEntryTypes.REDACTION),
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION),
});
popups.push({
type: 'actionButton',
dataElement: textPopups.ADD_DICTIONARY,
dataElement: TextPopups.ADD_DICTIONARY,
img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'),
title: this.#getTitle(ManualRedactionEntryTypes.DICTIONARY),
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY),
@ -683,16 +625,16 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
private async _handleCustomActions() {
this.instance.UI.setToolMode('AnnotationEdit');
const elementsToToggle = [
textPopups.ADD_REDACTION,
textPopups.ADD_RECTANGLE,
textPopups.ADD_FALSE_POSITIVE,
headerElements.SHAPE_TOOL_GROUP_BUTTON,
headerElements.ANNOTATION_POPUP,
headerElements.ROTATE_LEFT_BUTTON,
headerElements.ROTATE_RIGHT_BUTTON,
TextPopups.ADD_REDACTION,
TextPopups.ADD_RECTANGLE,
TextPopups.ADD_FALSE_POSITIVE,
HeaderElements.SHAPE_TOOL_GROUP_BUTTON,
HeaderElements.ANNOTATION_POPUP,
HeaderElements.ROTATE_LEFT_BUTTON,
HeaderElements.ROTATE_RIGHT_BUTTON,
];
const isCurrentPageExcluded = this.utils.isCurrentPageExcluded(await this.stateService.file);
const isCurrentPageExcluded = this.pdf.isCurrentPageExcluded(await this.stateService.file);
if (this.canPerformActions && !isCurrentPageExcluded) {
try {
@ -703,20 +645,20 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.instance.UI.enableElements(elementsToToggle);
if (this._selectedText.length > 2) {
this.instance.UI.enableElements([textPopups.ADD_DICTIONARY, textPopups.ADD_FALSE_POSITIVE]);
this.instance.UI.enableElements([TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]);
}
return;
}
let elementsToDisable = [...elementsToToggle, textPopups.ADD_RECTANGLE];
let elementsToDisable = [...elementsToToggle, TextPopups.ADD_RECTANGLE];
if (isCurrentPageExcluded) {
const allowedActionsWhenPageExcluded: string[] = [
headerElements.ANNOTATION_POPUP,
textPopups.ADD_RECTANGLE,
textPopups.ADD_REDACTION,
headerElements.SHAPE_TOOL_GROUP_BUTTON,
HeaderElements.ANNOTATION_POPUP,
TextPopups.ADD_RECTANGLE,
TextPopups.ADD_REDACTION,
HeaderElements.SHAPE_TOOL_GROUP_BUTTON,
];
elementsToDisable = elementsToDisable.filter(element => !allowedActionsWhenPageExcluded.includes(element));
} else {
@ -737,7 +679,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
for (const quad of quads[key]) {
const page = parseInt(key, 10);
const pageHeight = this.documentViewer.getPageHeight(page);
entry.positions.push(toPosition(page, pageHeight, convertQuads ? this.utils.translateQuad(page, quad) : quad));
entry.positions.push(toPosition(page, pageHeight, convertQuads ? this.pdf.translateQuad(page, quad) : quad));
}
}
@ -752,7 +694,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
private _setReadyAndInitialState(): void {
this._ngZone.run(() => {
this.utils.ready = true;
this.pdf.ready = true;
this.viewerReady.emit(this.instance);
const routePageNumber: number = this._activatedRoute.snapshot.queryParams.page;
this.pageChanged.emit(routePageNumber || 1);

View File

@ -11,6 +11,7 @@ import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
import { AnnotationReferencesService } from './services/annotation-references.service';
import { FilterService } from '../../../../../../../../libs/common-ui/src';
import { PageRotationService } from './services/page-rotation.service';
import { PdfViewer } from './services/pdf-viewer.service';
export const filePreviewScreenProviders = [
FilterService,
@ -26,4 +27,5 @@ export const filePreviewScreenProviders = [
PdfViewerDataService,
AnnotationReferencesService,
PageRotationService,
PdfViewer,
];

View File

@ -297,18 +297,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
selectAnnotations(annotations?: AnnotationWrapper[]) {
if (annotations) {
const annotationsToSelect = this.multiSelectService.isActive ? [...this.selectedAnnotations, ...annotations] : annotations;
this.viewerComponent?.utils?.selectAnnotations(annotationsToSelect, this.multiSelectService.isActive);
this.viewerComponent?.pdf?.selectAnnotations(annotationsToSelect, this.multiSelectService.isActive);
} else {
this.viewerComponent?.utils?.deselectAllAnnotations();
this.viewerComponent?.pdf?.deselectAllAnnotations();
}
}
deselectAnnotations(annotations: AnnotationWrapper[]) {
this.viewerComponent.utils.deselectAnnotations(annotations);
this.viewerComponent.pdf.deselectAnnotations(annotations);
}
selectPage(pageNumber: number) {
this.viewerComponent.utils.navigateToPage(pageNumber);
this.viewerComponent.pdf.navigateToPage(pageNumber);
this._workloadComponent?.scrollAnnotationsToPage(pageNumber, 'always');
this._lastPage = pageNumber.toString();
}
@ -467,7 +467,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
#deactivateMultiSelect(): void {
this.multiSelectService.deactivate();
this.viewerComponent?.utils?.deselectAllAnnotations();
this.viewerComponent?.pdf?.deselectAllAnnotations();
this.handleAnnotationSelected([]);
}

View File

@ -1,11 +1,15 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { RotationType } from '@red/domain';
import { RotationType, RotationTypes } from '@red/domain';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { FilePreviewStateService } from './file-preview-state.service';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { log } from '@iqser/common-ui';
import { PdfViewer } from './pdf-viewer.service';
import { HeaderElements } from '../shared/constants';
const confirmationButtons = [HeaderElements.APPLY_ROTATION, HeaderElements.DISCARD_ROTATION];
const oneRotationDegree = 90;
@Injectable()
export class PageRotationService {
@ -15,6 +19,7 @@ export class PageRotationService {
private readonly _permissionsService: PermissionsService,
private readonly _fileManagementService: FileManagementService,
private readonly _screenState: FilePreviewStateService,
private readonly _pdf: PdfViewer,
) {}
get canRotate() {
@ -41,14 +46,49 @@ export class PageRotationService {
return firstValueFrom(request);
}
addRotation(pageNumber: number, rotation: RotationType): void {
discardRotation() {
const rotations = this.rotations$.value;
for (const page of Object.keys(rotations)) {
const times = rotations[page] / oneRotationDegree;
for (let i = 1; i <= times; i++) {
this._pdf.documentViewer.rotateCounterClockwise(Number(page));
}
}
this.clearRotations();
}
addRotation(rotation: RotationType): void {
const pageNumber = this._pdf.currentPage;
const pageRotation = this.rotations$.value[pageNumber];
const rotationValue = pageRotation ? (pageRotation + Number(rotation)) % 360 : rotation;
this.rotations$.next({ ...this.rotations$.value, [pageNumber]: rotationValue });
if (rotation === RotationTypes.LEFT) {
this._pdf.documentViewer.rotateCounterClockwise(pageNumber);
} else {
this._pdf.documentViewer.rotateClockwise(pageNumber);
}
if (this.hasRotations()) {
this.#showConfirmationButtons();
} else {
this.#hideConfirmationButtons();
}
}
clearRotations() {
this.rotations$.next({});
this.#hideConfirmationButtons();
}
#showConfirmationButtons() {
this._pdf.UI.enableElements(confirmationButtons);
}
#hideConfirmationButtons() {
this._pdf.UI.disableElements(confirmationButtons);
}
}

View File

@ -1,9 +1,14 @@
import { translateQuads } from '@utils/pdf-coordinates';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core, WebViewerInstance } from '@pdftron/webviewer';
import { ViewModeService } from '../screens/file-preview-screen/services/view-mode.service';
import { translateQuads } from '../../../../../utils/pdf-coordinates';
import { AnnotationWrapper } from '../../../../../models/file/annotation.wrapper';
import WebViewer, { Core, WebViewerInstance } from '@pdftron/webviewer';
import { ViewModeService } from './view-mode.service';
import { File } from '@red/domain';
import { Inject, Injectable } from '@angular/core';
import { BASE_HREF } from '../../../../../tokens';
import { environment } from '../../../../../../environments/environment';
import Annotation = Core.Annotations.Annotation;
import DocumentViewer = Core.DocumentViewer;
import AnnotationManager = Core.AnnotationManager;
const DISABLED_HOTKEYS = [
'CTRL+SHIFT+EQUAL',
@ -38,10 +43,18 @@ const DISABLED_HOTKEYS = [
'U',
] as const;
export class PdfViewerUtils {
@Injectable()
export class PdfViewer {
ready = false;
instance: WebViewerInstance;
documentViewer: DocumentViewer;
annotationManager: AnnotationManager;
constructor(readonly instance: WebViewerInstance, readonly viewModeService: ViewModeService) {}
constructor(@Inject(BASE_HREF) private readonly _baseHref: string, readonly viewModeService: ViewModeService) {}
get UI() {
return this.instance.UI;
}
get paginationOffset() {
return this.viewModeService.isCompare ? 2 : 1;
@ -83,6 +96,24 @@ export class PdfViewerUtils {
return this.instance?.Core.documentViewer?.getPageCount();
}
async loadViewer(htmlElement: HTMLElement) {
this.instance = await WebViewer(
{
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
fullAPI: true,
path: this.#convertPath('/assets/wv-resources'),
css: this.#convertPath('/assets/pdftron/stylesheet.css'),
backendType: 'ems',
},
htmlElement,
);
this.documentViewer = this.instance.Core.documentViewer;
this.annotationManager = this.instance.Core.annotationManager;
return this.instance;
}
isCurrentPageExcluded(file: File) {
const currentPage = this.currentPage;
return !!file?.excludedPages?.includes(currentPage);
@ -147,4 +178,8 @@ export class PdfViewerUtils {
private _getAnnotationById(id: string): Annotation {
return this._annotationManager.getAnnotationById(id);
}
#convertPath(path: string) {
return `${this._baseHref}${path}`;
}
}

View File

@ -0,0 +1,19 @@
export const ALLOWED_KEYBOARD_SHORTCUTS: readonly string[] = ['+', '-', 'p', 'r', 'Escape'] as const;
export const HeaderElements = {
SHAPE_TOOL_GROUP_BUTTON: 'shapeToolGroupButton',
ROTATE_LEFT_BUTTON: 'rotateLeftButton',
ROTATE_RIGHT_BUTTON: 'rotateRightButton',
APPLY_ROTATION: 'applyRotation',
DISCARD_ROTATION: 'discardRotation',
TOGGLE_TOOLTIPS: 'toggle-tooltips',
ANNOTATION_POPUP: 'annotationPopup',
COMPARE_BUTTON: 'compareButton',
CLOSE_COMPARE_BUTTON: 'closeCompareButton',
} as const;
export const TextPopups = {
ADD_REDACTION: 'add-redaction',
ADD_DICTIONARY: 'add-dictionary',
ADD_RECTANGLE: 'add-rectangle',
ADD_FALSE_POSITIVE: 'add-false-positive',
} as const;