From a3e2f1d4989fd0b94eefbea0bb77d458656db70c Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Sat, 6 Nov 2021 02:04:46 +0200 Subject: [PATCH] little refactor for pdf viewer --- .../file/manual-redaction-entry.wrapper.ts | 10 +- .../pdf-viewer/pdf-viewer.component.ts | 488 ++++++++++-------- .../dossier/utils/compare-mode.utils.ts | 50 +- 3 files changed, 301 insertions(+), 247 deletions(-) diff --git a/apps/red-ui/src/app/models/file/manual-redaction-entry.wrapper.ts b/apps/red-ui/src/app/models/file/manual-redaction-entry.wrapper.ts index 39714983c..79504dea3 100644 --- a/apps/red-ui/src/app/models/file/manual-redaction-entry.wrapper.ts +++ b/apps/red-ui/src/app/models/file/manual-redaction-entry.wrapper.ts @@ -1,10 +1,18 @@ import { IManualRedactionEntry } from '@red/domain'; +export const ManualRedactionEntryTypes = { + DICTIONARY: 'DICTIONARY', + REDACTION: 'REDACTION', + FALSE_POSITIVE: 'FALSE_POSITIVE', +} as const; + +export type ManualRedactionEntryType = keyof typeof ManualRedactionEntryTypes; + export class ManualRedactionEntryWrapper { constructor( readonly quads: any, readonly manualRedactionEntry: IManualRedactionEntry, - readonly type: 'DICTIONARY' | 'REDACTION' | 'FALSE_POSITIVE', + readonly type: ManualRedactionEntryType, readonly annotationType: 'TEXT' | 'RECTANGLE' = 'TEXT', readonly rectId?: string, ) {} diff --git a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts index fadb6ffa1..a5cbb42ed 100644 --- a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts @@ -14,7 +14,11 @@ import { import { File, IManualRedactionEntry, ViewMode } from '@red/domain'; import WebViewer, { Core, WebViewerInstance } from '@pdftron/webviewer'; import { TranslateService } from '@ngx-translate/core'; -import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; +import { + ManualRedactionEntryType, + ManualRedactionEntryTypes, + ManualRedactionEntryWrapper, +} from '@models/file/manual-redaction-entry.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; import { environment } from '@environments/environment'; @@ -33,7 +37,19 @@ import Tools = Core.Tools; import TextTool = Tools.TextTool; import Annotation = Core.Annotations.Annotation; -const allowedKeyboardShortcuts = ['+', '-', 'p', 'r', 'Escape']; +const ALLOWED_KEYBOARD_SHORTCUTS = ['+', '-', 'p', 'r', 'Escape'] as const; +const dataElements = { + ADD_REDACTION: 'add-redaction', + ADD_DICTIONARY: 'add-dictionary', + ADD_RECTANGLE: 'add-rectangle', + ADD_FALSE_POSITIVE: 'add-false-positive', + SHAPE_TOOL_GROUP_BUTTON: 'shapeToolGroupButton', + RECTANGLE_TOOL_DIVIDER: 'rectangleToolDivider', + ANNOTATION_POPUP: 'annotationPopup', + COMPARE_BUTTON: 'compareButton', + CLOSE_COMPARE_BUTTON: 'closeCompareButton', + COMPARE_TOOL_DIVIDER: 'compareToolDivider', +} as const; @Component({ selector: 'redaction-pdf-viewer', @@ -57,6 +73,8 @@ export class PdfViewerComponent implements OnInit, OnChanges { @ViewChild('viewer', { static: true }) viewer: ElementRef; @ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef; instance: WebViewerInstance; + documentViewer: Core.DocumentViewer; + annotationManager: Core.AnnotationManager; utils: PdfViewerUtils; private _selectedText = ''; private _firstPageChange = true; @@ -87,96 +105,90 @@ export class PdfViewerComponent implements OnInit, OnChanges { } async ngOnInit() { - this._documentLoaded = this._documentLoaded.bind(this); + this._setReadyAndInitialState = this._setReadyAndInitialState.bind(this); await this._loadViewer(); } ngOnChanges(changes: SimpleChanges): void { - if (this.instance) { - if (changes.fileData) { - this._loadDocument(); - } - if (changes.canPerformActions) { - this._handleCustomActions(); - } - if (changes.multiSelectActive) { - this.utils.multiSelectActive = this.multiSelectActive; - } + if (!this.instance) { + return; } - } - setInitialViewerState() { - // viewer init - this.instance.UI.setFitMode('FitPage'); + if (changes.fileData) { + this._loadDocument(); + } - const instanceDisplayMode = this.instance.Core.documentViewer.getDisplayModeManager().getDisplayMode(); - instanceDisplayMode.mode = this.viewMode === 'STANDARD' ? 'Single' : 'Facing'; - this.instance.Core.documentViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); + if (changes.canPerformActions) { + this._handleCustomActions(); + } + + if (changes.multiSelectActive) { + this.utils.multiSelectActive = this.multiSelectActive; + } } uploadFile(files: any) { const fileToCompare = files[0]; this.compareFileInput.nativeElement.value = null; - const fileReader = new FileReader(); - - if (fileToCompare) { - fileReader.onload = async () => { - const pdfData = fileReader.result; - const pdfNet = this.instance.Core.PDFNet; - - await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null); - - const mergedDocument = await pdfNet.PDFDoc.create(); - const compareDocument = await pdfNet.PDFDoc.createFromBuffer(pdfData); - const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer()); - - const currentDocumentPageCount = await currentDocument.getPageCount(); - const compareDocumentPageCount = await compareDocument.getPageCount(); - - const loadCompareDocument = async () => { - this._loadingService.start(); - this.utils.ready = false; - await loadCompareDocumentWrapper( - currentDocumentPageCount, - compareDocumentPageCount, - currentDocument, - compareDocument, - mergedDocument, - this.instance, - this.file, - () => { - this.viewMode = 'COMPARE'; - }, - () => { - this.utils.navigateToPage(1); - }, - this.instance.Core.PDFNet, - ); - this._loadingService.stop(); - }; - - if (currentDocumentPageCount !== compareDocumentPageCount) { - this._dialogService.openDialog( - 'confirm', - null, - new ConfirmationDialogInput({ - title: _('confirmation-dialog.compare-file.title'), - question: _('confirmation-dialog.compare-file.question'), - translateParams: { - fileName: fileToCompare.name, - currentDocumentPageCount, - compareDocumentPageCount, - }, - }), - loadCompareDocument, - ); - } else { - await loadCompareDocument(); - } - }; + if (!fileToCompare) { + console.error('No file to compare!'); + return; } + const fileReader = new FileReader(); + fileReader.onload = async () => { + const pdfNet = this.instance.Core.PDFNet; + + await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null); + + const compareDocument = await pdfNet.PDFDoc.createFromBuffer(fileReader.result as ArrayBuffer); + const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer()); + + const loadCompareDocument = async () => { + this._loadingService.start(); + this.utils.ready = false; + const mergedDocument = await pdfNet.PDFDoc.create(); + await loadCompareDocumentWrapper( + currentDocument, + compareDocument, + mergedDocument, + this.instance, + this.file, + () => { + this.viewMode = 'COMPARE'; + }, + () => { + this.utils.navigateToPage(1); + }, + this.instance.Core.PDFNet, + ); + this._loadingService.stop(); + }; + + const currentDocumentPageCount = await currentDocument.getPageCount(); + const compareDocumentPageCount = await compareDocument.getPageCount(); + + if (currentDocumentPageCount !== compareDocumentPageCount) { + this._dialogService.openDialog( + 'confirm', + null, + new ConfirmationDialogInput({ + title: _('confirmation-dialog.compare-file.title'), + question: _('confirmation-dialog.compare-file.question'), + translateParams: { + fileName: fileToCompare.name, + currentDocumentPageCount, + compareDocumentPageCount, + }, + }), + loadCompareDocument, + ); + } else { + await loadCompareDocument(); + } + }; + fileReader.readAsArrayBuffer(fileToCompare); } @@ -188,11 +200,18 @@ export class PdfViewerComponent implements OnInit, OnChanges { this.instance.UI.loadDocument(currentDocument, { filename: this.file ? this.file.filename : 'document.pdf', }); - this.instance.UI.disableElements(['closeCompareButton']); - this.instance.UI.enableElements(['compareButton']); + this.instance.UI.disableElements([dataElements.CLOSE_COMPARE_BUTTON]); + this.instance.UI.enableElements([dataElements.COMPARE_BUTTON]); this.utils.navigateToPage(1); } + private _setInitialDisplayMode() { + this.instance.UI.setFitMode('FitPage'); + const instanceDisplayMode = this.documentViewer.getDisplayModeManager().getDisplayMode(); + instanceDisplayMode.mode = this.viewMode === 'STANDARD' ? 'Single' : 'Facing'; + this.documentViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); + } + private _convertPath(path: string): string { return this._baseHref + path; } @@ -209,6 +228,8 @@ export class PdfViewerComponent implements OnInit, OnChanges { this.viewer.nativeElement, ); + this.documentViewer = this.instance.Core.documentViewer; + this.annotationManager = this.instance.Core.annotationManager; this.utils = new PdfViewerUtils(this.instance, this.viewMode, this.multiSelectActive); this._setSelectionMode(); @@ -216,8 +237,8 @@ export class PdfViewerComponent implements OnInit, OnChanges { this.utils.disableHotkeys(); this._configureTextPopup(); - this.instance.Core.annotationManager.on('annotationSelected', (annotations, action) => { - this.annotationSelected.emit(this.instance.Core.annotationManager.getSelectedAnnotations().map(ann => ann.Id)); + this.annotationManager.on('annotationSelected', (annotations, action) => { + this.annotationSelected.emit(this.annotationManager.getSelectedAnnotations().map(ann => ann.Id)); if (action === 'deselected') { this._toggleRectangleAnnotationAction(true); } else { @@ -226,16 +247,16 @@ export class PdfViewerComponent implements OnInit, OnChanges { } }); - this.instance.Core.annotationManager.on('annotationChanged', annotations => { + this.annotationManager.on('annotationChanged', annotations => { // when a rectangle is drawn, // it returns one annotation with tool name 'AnnotationCreateRectangle; // this will auto select rectangle after drawing if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') { - this.instance.Core.annotationManager.selectAnnotations(annotations); + this.annotationManager.selectAnnotations(annotations); } }); - this.instance.Core.documentViewer.on('pageNumberUpdated', pageNumber => { + this.documentViewer.on('pageNumberUpdated', pageNumber => { if (this.shouldDeselectAnnotationsOnPageChange) { this.utils.deselectAllAnnotations(); } @@ -251,9 +272,9 @@ export class PdfViewerComponent implements OnInit, OnChanges { this._handleCustomActions(); }); - this.instance.Core.documentViewer.on('documentLoaded', this._documentLoaded); + this.documentViewer.on('documentLoaded', this._setReadyAndInitialState); - this.instance.Core.documentViewer.on('keyUp', $event => { + this.documentViewer.on('keyUp', $event => { // arrows and full-screen if ($event.target?.tagName?.toLowerCase() !== 'input') { if ($event.key.startsWith('Arrow') || $event.key === 'f') { @@ -265,18 +286,20 @@ export class PdfViewerComponent implements OnInit, OnChanges { } } - if (allowedKeyboardShortcuts.indexOf($event.key) < 0) { + if (ALLOWED_KEYBOARD_SHORTCUTS.indexOf($event.key) < 0) { $event.preventDefault(); $event.stopPropagation(); } }); - this.instance.Core.documentViewer.on('textSelected', (quads, selectedText) => { + this.documentViewer.on('textSelected', (quads, selectedText) => { this._selectedText = selectedText; - if (selectedText.length > 2 && this.canPerformActions) { - this.instance.UI.enableElements(['add-dictionary', 'add-false-positive']); + const textActions = [dataElements.ADD_DICTIONARY, dataElements.ADD_FALSE_POSITIVE]; + + if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded) { + this.instance.UI.enableElements(textActions); } else { - this.instance.UI.disableElements(['add-dictionary', 'add-false-positive']); + this.instance.UI.disableElements(textActions); } }); @@ -287,7 +310,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { inputElement.value = ''; }, 0); if (!event.detail.isVisible) { - this.instance.Core.documentViewer.clearSearchResults(); + this.documentViewer.clearSearchResults(); } } }); @@ -296,15 +319,15 @@ export class PdfViewerComponent implements OnInit, OnChanges { } private _setSelectionMode(): void { - const textTool = ( this.instance.Core.Tools.TextTool) as TextTool; + const textTool = this.instance.Core.Tools.TextTool as unknown as TextTool; textTool.SELECTION_MODE = this._configService.values.SELECTION_MODE; } private _toggleRectangleAnnotationAction(readonly: boolean) { if (!readonly) { - this.instance.UI.enableElements(['add-rectangle']); + this.instance.UI.enableElements([dataElements.ADD_RECTANGLE]); } else { - this.instance.UI.disableElements(['add-rectangle']); + this.instance.UI.disableElements([dataElements.ADD_RECTANGLE]); } } @@ -332,50 +355,59 @@ export class PdfViewerComponent implements OnInit, OnChanges { 'annotationGroupButton', ]); - this.instance.UI.setHeaderItems(header => { - const originalHeaderItems = header.getItems(); - originalHeaderItems.splice(8, 0, { + const headerItems = [ + { type: 'divider', - dataElement: 'rectangleToolDivider', - }); - originalHeaderItems.splice(9, 0, { + dataElement: dataElements.RECTANGLE_TOOL_DIVIDER, + }, + { type: 'toolGroupButton', toolGroup: 'rectangleTools', - dataElement: 'shapeToolGroupButton', + dataElement: dataElements.SHAPE_TOOL_GROUP_BUTTON, img: this._convertPath('/assets/icons/general/rectangle.svg'), title: 'annotation.rectangle', - }); + }, + ]; + + this.instance.UI.setHeaderItems(header => { + const originalHeaderItems = header.getItems(); + originalHeaderItems.splice(8, 0, ...headerItems); + if (this._userPreferenceService.areDevFeaturesEnabled) { - originalHeaderItems.splice(11, 0, { - type: 'actionButton', - element: 'compare', - dataElement: 'compareButton', - img: this._convertPath('/assets/icons/general/pdftron-action-compare.svg'), - title: 'Compare', - onClick: () => { - this.compareFileInput.nativeElement.click(); + const devHeaderItems = [ + { + type: 'actionButton', + element: 'compare', + dataElement: dataElements.COMPARE_BUTTON, + img: this._convertPath('/assets/icons/general/pdftron-action-compare.svg'), + title: 'Compare', + onClick: () => { + this.compareFileInput.nativeElement.click(); + }, }, - }); - originalHeaderItems.splice(11, 0, { - type: 'actionButton', - element: 'closeCompare', - dataElement: 'closeCompareButton', - img: this._convertPath('/assets/icons/general/pdftron-action-close-compare.svg'), - title: 'Leave Compare Mode', - onClick: () => { - this.closeCompareMode(); + { + type: 'actionButton', + element: 'closeCompare', + dataElement: dataElements.CLOSE_COMPARE_BUTTON, + img: this._convertPath('/assets/icons/general/pdftron-action-close-compare.svg'), + title: 'Leave Compare Mode', + onClick: async () => { + await this.closeCompareMode(); + }, }, - }); - originalHeaderItems.splice(13, 0, { - type: 'divider', - dataElement: 'compareToolDivider', - }); + { + type: 'divider', + dataElement: dataElements.COMPARE_TOOL_DIVIDER, + }, + ]; + + originalHeaderItems.splice(11, 0, ...devHeaderItems); } }); - this.instance.UI.disableElements(['closeCompareButton']); + this.instance.UI.disableElements([dataElements.CLOSE_COMPARE_BUTTON]); - this.instance.Core.documentViewer.getTool('AnnotationCreateRectangle').setStyles(() => ({ + this.documentViewer.getTool('AnnotationCreateRectangle').setStyles(() => ({ StrokeThickness: 2, StrokeColor: this._annotationDrawService.getColor(this.instance, 'manual'), FillColor: this._annotationDrawService.getColor(this.instance, 'manual'), @@ -411,11 +443,11 @@ export class PdfViewerComponent implements OnInit, OnChanges { onClick: () => { this._ngZone.run(() => { if (allAreVisible) { - this.instance.Core.annotationManager.hideAnnotations(viewerAnnotations); + this.annotationManager.hideAnnotations(viewerAnnotations); } else { - this.instance.Core.annotationManager.showAnnotations(viewerAnnotations); + this.annotationManager.showAnnotations(viewerAnnotations); } - this.instance.Core.annotationManager.deselectAllAnnotations(); + this.annotationManager.deselectAllAnnotations(); this._annotationActionsService.updateHiddenAnnotation(this.annotations, viewerAnnotations, allAreVisible); }); }, @@ -429,38 +461,43 @@ export class PdfViewerComponent implements OnInit, OnChanges { } private _configureRectangleAnnotationPopup() { - this.instance.UI.annotationPopup.add({ - type: 'actionButton', - dataElement: 'add-rectangle', - img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'), - title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')), - onClick: () => { - const selectedAnnotations = this.instance.Core.annotationManager.getSelectedAnnotations(); - const activeAnnotation = selectedAnnotations[0]; - const activePage = selectedAnnotations[0].getPageNumber(); - const quad = this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance); - const quadsObject = {}; - quadsObject[activePage] = [quad]; - const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle'); - // cleanup selection and button state - this.utils.deselectAllAnnotations(); - this.instance.UI.disableElements(['shapeToolGroupButton', 'rectangleToolDivider']); - this.instance.UI.enableElements(['shapeToolGroupButton', 'rectangleToolDivider']); - // dispatch event - this.manualAnnotationRequested.emit( - new ManualRedactionEntryWrapper([quad], mre, 'REDACTION', 'RECTANGLE', activeAnnotation.Id), - ); + this.instance.UI.annotationPopup.add([ + { + type: 'actionButton', + dataElement: dataElements.ADD_RECTANGLE, + img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'), + title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')), + onClick: () => this._addRectangleManualRedaction(), }, - }); + ]); + } + + private _addRectangleManualRedaction() { + const activeAnnotation = this.annotationManager.getSelectedAnnotations()[0]; + const activePage = activeAnnotation.getPageNumber(); + const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance)]; + const manualRedaction = this._getManualRedaction({ [activePage]: quads }, 'Rectangle'); + this._cleanUpSelectionAndButtonState(); + + this.manualAnnotationRequested.emit( + new ManualRedactionEntryWrapper(quads, manualRedaction, 'REDACTION', 'RECTANGLE', activeAnnotation.Id), + ); + } + + private _cleanUpSelectionAndButtonState() { + const rectangleElements = [dataElements.SHAPE_TOOL_GROUP_BUTTON, dataElements.RECTANGLE_TOOL_DIVIDER]; + this.utils.deselectAllAnnotations(); + this.instance.UI.disableElements(rectangleElements); + this.instance.UI.enableElements(rectangleElements); } private _configureTextPopup() { - this.instance.UI.textPopup.add({ + const searchButton = { type: 'actionButton', img: this._convertPath('/assets/icons/general/pdftron-action-search.svg'), title: this._translateService.instant('pdf-viewer.text-popup.actions.search'), onClick: () => { - const text = this.instance.Core.documentViewer.getSelectedText(); + const text = this.documentViewer.getSelectedText(); const searchOptions = { caseSensitive: true, // match case wholeWord: true, // match whole words only @@ -470,117 +507,118 @@ export class PdfViewerComponent implements OnInit, OnChanges { ambientString: true, // return ambient string as part of the result }; this.instance.UI.openElements(['searchPanel']); - setTimeout(() => { - this.instance.UI.searchTextFull(text, searchOptions); - }, 250); + setTimeout(() => this.instance.UI.searchTextFull(text, searchOptions), 250); }, - }); + }; + + this.instance.UI.textPopup.add([searchButton]); // Adding directly to the false-positive dict is only available in dev-mode if (this._userPreferenceService.areDevFeaturesEnabled) { - this.instance.UI.textPopup.add({ - type: 'actionButton', - dataElement: 'add-false-positive', - img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'), - title: this._translateService.instant(this._manualAnnotationService.getTitle('FALSE_POSITIVE')), - onClick: () => { - const selectedQuads = this.instance.Core.documentViewer.getSelectedTextQuads(); - const text = this.instance.Core.documentViewer.getSelectedText(); - const mre = this._getManualRedactionEntry(selectedQuads, text, true); - this.manualAnnotationRequested.emit( - new ManualRedactionEntryWrapper(this.instance.Core.documentViewer.getSelectedTextQuads(), mre, 'FALSE_POSITIVE'), - ); + this.instance.UI.textPopup.add([ + { + type: 'actionButton', + dataElement: 'add-false-positive', + img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'), + title: this._translateService.instant(this._manualAnnotationService.getTitle(ManualRedactionEntryTypes.FALSE_POSITIVE)), + onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.FALSE_POSITIVE), }, - }); + ]); } - this.instance.UI.textPopup.add({ - type: 'actionButton', - dataElement: 'add-dictionary', - img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'), - title: this._translateService.instant(this._manualAnnotationService.getTitle('DICTIONARY')), - onClick: () => { - const selectedQuads = this.instance.Core.documentViewer.getSelectedTextQuads(); - const text = this.instance.Core.documentViewer.getSelectedText(); - const mre = this._getManualRedactionEntry(selectedQuads, text, true); - this.manualAnnotationRequested.emit( - new ManualRedactionEntryWrapper(this.instance.Core.documentViewer.getSelectedTextQuads(), mre, 'DICTIONARY'), - ); + this.instance.UI.textPopup.add([ + { + type: 'actionButton', + dataElement: 'add-redaction', + img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'), + title: this._translateService.instant(this._manualAnnotationService.getTitle(ManualRedactionEntryTypes.REDACTION)), + onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION), }, - }); + { + type: 'actionButton', + dataElement: 'add-dictionary', + img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'), + title: this._translateService.instant(this._manualAnnotationService.getTitle(ManualRedactionEntryTypes.DICTIONARY)), + onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY), + }, + ]); - this.instance.UI.textPopup.add({ - type: 'actionButton', - dataElement: 'add-redaction', - img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'), - title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')), - onClick: () => { - const selectedQuads = this.instance.Core.documentViewer.getSelectedTextQuads(); - const text = this.instance.Core.documentViewer.getSelectedText(); - const mre = this._getManualRedactionEntry(selectedQuads, text, true); - this.manualAnnotationRequested.emit( - new ManualRedactionEntryWrapper(this.instance.Core.documentViewer.getSelectedTextQuads(), mre, 'REDACTION'), - ); - }, - }); this._handleCustomActions(); } + private _addManualRedactionOfType(type: ManualRedactionEntryType) { + const selectedQuads = this.documentViewer.getSelectedTextQuads(); + const text = this.documentViewer.getSelectedText(); + const manualRedaction = this._getManualRedaction(selectedQuads, text, true); + this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(selectedQuads, manualRedaction, type)); + } + private _handleCustomActions() { this.instance.UI.setToolMode('AnnotationEdit'); + const elements = [ + 'add-redaction', + 'add-rectangle', + 'add-false-positive', + 'shapeToolGroupButton', + 'rectangleToolDivider', + 'annotationPopup', + ]; + if (this.canPerformActions && !this.utils.isCurrentPageExcluded) { this.instance.UI.enableTools(['AnnotationCreateRectangle']); - this.instance.UI.enableElements([ - 'add-redaction', - 'add-rectangle', - 'add-false-positive', - 'shapeToolGroupButton', - 'rectangleToolDivider', - 'annotationPopup', - ]); + this.instance.UI.enableElements(elements); + if (this._selectedText.length > 2) { - this.instance.UI.enableElements(['add-dictionary', 'add-false-positive']); + this.instance.UI.enableElements([dataElements.ADD_DICTIONARY, dataElements.ADD_FALSE_POSITIVE]); } - } else { - this.instance.UI.disableTools(['AnnotationCreateRectangle']); - this.instance.UI.disableElements([ - 'add-redaction', - 'add-dictionary', - 'add-false-positive', - 'add-rectangle', - 'shapeToolGroupButton', - 'rectangleToolDivider', - 'annotationPopup', - ]); + + return; } + + const elementsToDisable = [...elements, dataElements.ADD_RECTANGLE]; + if (this.utils.isCurrentPageExcluded) { + elementsToDisable.splice(elementsToDisable.indexOf(dataElements.ADD_REDACTION), 1); + elementsToDisable.splice(elementsToDisable.indexOf(dataElements.SHAPE_TOOL_GROUP_BUTTON), 1); + } + + this.instance.UI.disableTools(['AnnotationCreateRectangle']); + this.instance.UI.disableElements(elementsToDisable); } - private _getManualRedactionEntry(quads: any, text: string, convertQuads: boolean = false): IManualRedactionEntry { + private _getManualRedaction( + quads: Readonly>, + text: string, + convertQuads = false, + ): IManualRedactionEntry { const entry: IManualRedactionEntry = { positions: [] }; + for (const key of Object.keys(quads)) { for (const quad of quads[key]) { const page = parseInt(key, 10); entry.positions.push(this.utils.toPosition(page, convertQuads ? this.utils.translateQuads(page, quad) : quad)); } } + entry.value = text; return entry; } private _loadDocument() { - if (this.fileData) { - this.instance.UI.loadDocument(this.fileData, { - filename: this.file ? this.file.filename : 'document.pdf', - }); + if (!this.fileData) { + return; } + + this.instance.UI.loadDocument(this.fileData, { + filename: this.file ? this.file.filename : 'document.pdf', + }); } - private _documentLoaded(): void { + private _setReadyAndInitialState(): void { this._ngZone.run(() => { this.utils.ready = true; this._firstPageChange = true; this.viewerReady.emit(this.instance); - this.setInitialViewerState(); + this._setInitialDisplayMode(); }); } } diff --git a/apps/red-ui/src/app/modules/dossier/utils/compare-mode.utils.ts b/apps/red-ui/src/app/modules/dossier/utils/compare-mode.utils.ts index 3074b808b..06876a51b 100644 --- a/apps/red-ui/src/app/modules/dossier/utils/compare-mode.utils.ts +++ b/apps/red-ui/src/app/modules/dossier/utils/compare-mode.utils.ts @@ -1,34 +1,42 @@ import { stampPDFPage } from '@utils/page-stamper'; +import { Core, WebViewerInstance } from '@pdftron/webviewer'; +import { File } from '@red/domain'; -const processPage = async (pageNumber, document1, document2, mergedDocument, pdfNet) => { +const processPage = async ( + pageNumber: number, + document1: Core.PDFNet.PDFDoc, + document2: Core.PDFNet.PDFDoc, + mergedDocument: Core.PDFNet.PDFDoc, + pdfNet: typeof Core.PDFNet, +) => { const document1PageCount = await document1.getPageCount(); + if (document1PageCount >= pageNumber) { await mergedDocument.insertPages(pageNumber * 2, document1, pageNumber, pageNumber, pdfNet.PDFDoc.InsertFlag.e_none); - } else { - const pageToCopy = await document2.getPage(pageNumber); - const blankPage = await mergedDocument.pageCreate(await pageToCopy.getCropBox()); - await blankPage.setRotation(await pageToCopy.getRotation()); - await mergedDocument.pagePushBack(blankPage); - await stampPDFPage(mergedDocument, pdfNet, '<< Compare Placeholder Page >>', 20, 'courier', 'DIAGONAL', 33, '#ffb83b', [ - await mergedDocument.getPageCount(), - ]); + return; } + + const pageToCopy = await document2.getPage(pageNumber); + const blankPage = await mergedDocument.pageCreate(await pageToCopy.getCropBox()); + await blankPage.setRotation(await pageToCopy.getRotation()); + await mergedDocument.pagePushBack(blankPage); + await stampPDFPage(mergedDocument, pdfNet, '<< Compare Placeholder Page >>', 20, 'courier', 'DIAGONAL', 33, '#ffb83b', [ + await mergedDocument.getPageCount(), + ]); }; export const loadCompareDocumentWrapper = async ( - currentDocumentPageCount, - compareDocumentPageCount, - currentDocument, - compareDocument, - mergedDocument, - instance, - file, + currentDocument: Core.PDFNet.PDFDoc, + compareDocument: Core.PDFNet.PDFDoc, + mergedDocument: Core.PDFNet.PDFDoc, + instance: WebViewerInstance, + file: File, setCompareViewMode: () => void, navigateToPage: () => void, - pdfNet: any, + pdfNet: typeof Core.PDFNet, ) => { try { - const maxPageCount = Math.max(currentDocumentPageCount, compareDocumentPageCount); + const maxPageCount = Math.max(await currentDocument.getPageCount(), await compareDocument.getPageCount()); for (let idx = 1; idx <= maxPageCount; idx++) { await processPage(idx, currentDocument, compareDocument, mergedDocument, pdfNet); @@ -43,11 +51,11 @@ export const loadCompareDocumentWrapper = async ( setCompareViewMode(); - instance.loadDocument(mergedDocumentBuffer, { + instance.UI.loadDocument(mergedDocumentBuffer, { filename: file?.filename ?? 'document.pdf', }); - instance.disableElements(['compareButton']); - instance.enableElements(['closeCompareButton']); + instance.UI.disableElements(['compareButton']); + instance.UI.enableElements(['closeCompareButton']); navigateToPage(); } catch (e) {