little refactor for pdf viewer
This commit is contained in:
parent
0e90411f51
commit
a3e2f1d498
@ -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,
|
||||
) {}
|
||||
|
||||
@ -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(<any>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 = (<unknown> 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(<any>{
|
||||
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(<any>{
|
||||
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(<any>{
|
||||
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(<any>{
|
||||
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(<any>{
|
||||
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<Record<string, Core.Math.Quad[]>>,
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user