Pull request #303: RED-2484
Merge in RED/ui from RED-2484 to master * commit '0504086fefd96d5800607cb1f1fb6c0f9425ab22': show manual redactions and their pages when excluded little refactor for pdf viewer add operator & fix icon name add excluded page icon update view accordingly to excluded pages design
This commit is contained in:
commit
1e14469941
@ -6,9 +6,9 @@ import { UserService } from '@services/user.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { NotificationsService } from '@services/notifications.service';
|
||||
import { Notification } from '@red/domain';
|
||||
import { distinctUntilChanged, map, shareReplay } from 'rxjs/operators';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { List } from '@iqser/common-ui';
|
||||
import { List, shareLast } from '@iqser/common-ui';
|
||||
|
||||
interface NotificationsGroup {
|
||||
date: string;
|
||||
@ -25,7 +25,7 @@ export class NotificationsComponent implements OnInit {
|
||||
notifications$: Observable<Notification[]>;
|
||||
hasUnreadNotifications$: Observable<boolean>;
|
||||
groupedNotifications$: Observable<NotificationsGroup[]>;
|
||||
private _notifications$ = new BehaviorSubject([]);
|
||||
private _notifications$ = new BehaviorSubject<Notification[]>([]);
|
||||
|
||||
constructor(
|
||||
private readonly _translateService: TranslateService,
|
||||
@ -35,12 +35,12 @@ export class NotificationsComponent implements OnInit {
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _datePipe: DatePipe,
|
||||
) {
|
||||
this.notifications$ = this._notifications$.asObservable();
|
||||
this.notifications$ = this._notifications$.asObservable().pipe(shareLast());
|
||||
this.groupedNotifications$ = this.notifications$.pipe(map(notifications => this._groupNotifications(notifications)));
|
||||
this.hasUnreadNotifications$ = this.notifications$.pipe(
|
||||
map(notifications => notifications.filter(n => !n.readDate).length > 0),
|
||||
distinctUntilChanged(),
|
||||
shareReplay(1),
|
||||
shareLast(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ export class FileDataModel {
|
||||
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
||||
let allAnnotations = entries
|
||||
.map(entry => AnnotationWrapper.fromData(entry))
|
||||
.filter(ann => !this.file.excludedPages.includes(ann.pageNumber));
|
||||
.filter(ann => ann.manual || !this.file.excludedPages.includes(ann.pageNumber));
|
||||
|
||||
if (!areDevFeaturesEnabled) {
|
||||
allAnnotations = allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||
|
||||
@ -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,
|
||||
) {}
|
||||
|
||||
@ -88,6 +88,7 @@
|
||||
[activeSelection]="pageHasSelection(pageNumber)"
|
||||
[active]="pageNumber === activeViewerPage"
|
||||
[number]="pageNumber"
|
||||
[showDottedIcon]="hasOnlyManualRedactionsAndNotExcluded(pageNumber)"
|
||||
[viewedPages]="fileData?.viewedPages"
|
||||
></redaction-page-indicator>
|
||||
</div>
|
||||
@ -104,11 +105,21 @@
|
||||
|
||||
<div style="overflow: hidden; width: 100%">
|
||||
<ng-container *ngIf="!excludePages">
|
||||
<div [attr.anotation-page-header]="activeViewerPage" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="all-caps-label">
|
||||
<span translate="page"></span> {{ activeViewerPage }} -
|
||||
{{ activeAnnotations?.length || 0 }}
|
||||
<span [translate]="activeAnnotations?.length === 1 ? 'annotation' : 'annotations'"></span>
|
||||
<div [attr.anotation-page-header]="activeViewerPage" [class.padding-left-0]="isExcluded" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="flex-align-items-center">
|
||||
<iqser-circle-button
|
||||
(action)="viewExcludePages()"
|
||||
*ngIf="isExcluded"
|
||||
[tooltip]="'file-preview.excluded-from-redaction' | translate | capitalize"
|
||||
icon="red:exclude-pages"
|
||||
tooltipPosition="above"
|
||||
></iqser-circle-button>
|
||||
|
||||
<span class="all-caps-label">
|
||||
<span translate="page"></span> {{ activeViewerPage }} -
|
||||
{{ activeAnnotations?.length || 0 }}
|
||||
<span [translate]="activeAnnotations?.length === 1 ? 'annotation' : 'annotations'"></span>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div *ngIf="multiSelectActive">
|
||||
@ -141,13 +152,9 @@
|
||||
[verticalPadding]="40"
|
||||
icon="iqser:document"
|
||||
>
|
||||
<ng-container *ngIf="fileData?.file?.excludedPages?.includes(activeViewerPage)">
|
||||
<ng-container *ngIf="isExcluded">
|
||||
{{ 'file-preview.tabs.annotations.page-is' | translate }}
|
||||
<a
|
||||
(click)="actionPerformed.emit('view-exclude-pages')"
|
||||
class="with-underline"
|
||||
translate="file-preview.excluded-from-redaction"
|
||||
></a
|
||||
<a (click)="viewExcludePages()" class="with-underline" translate="file-preview.excluded-from-redaction"></a
|
||||
>.
|
||||
</ng-container>
|
||||
</iqser-empty-state>
|
||||
@ -173,11 +180,11 @@
|
||||
<redaction-annotations-list
|
||||
(deselectAnnotations)="deselectAnnotations.emit($event)"
|
||||
(pagesPanelActive)="pagesPanelActive = $event"
|
||||
(selectAnnotations)="selectAnnotations.emit($event)"
|
||||
[(multiSelectActive)]="multiSelectActive"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
|
||||
(selectAnnotations)="selectAnnotations.emit($event)"
|
||||
[canMultiSelect]="!isReadOnly"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
></redaction-annotations-list>
|
||||
|
||||
@ -167,3 +167,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.padding-left-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
::ng-deep .page-separator iqser-circle-button mat-icon {
|
||||
color: var(--iqser-primary);
|
||||
}
|
||||
|
||||
@ -90,6 +90,10 @@ export class FileWorkloadComponent {
|
||||
return !this._permissionsService.canPerformAnnotationActions();
|
||||
}
|
||||
|
||||
get isExcluded(): boolean {
|
||||
return this.fileData?.file?.excludedPages?.includes(this.activeViewerPage);
|
||||
}
|
||||
|
||||
private get _firstSelectedAnnotation() {
|
||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
||||
}
|
||||
@ -114,6 +118,11 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
}
|
||||
|
||||
hasOnlyManualRedactionsAndNotExcluded(pageNumber: number): boolean {
|
||||
const hasOnlyManualRedactions = this.displayedAnnotations.get(pageNumber).every(annotation => annotation.manual);
|
||||
return hasOnlyManualRedactions && this.fileData.file.excludedPages.includes(pageNumber);
|
||||
}
|
||||
|
||||
pageHasSelection(page: number) {
|
||||
return this.multiSelectActive && !!this.selectedAnnotations?.find(a => a.pageNumber === page);
|
||||
}
|
||||
@ -225,6 +234,10 @@ export class FileWorkloadComponent {
|
||||
this.selectPage.emit(this._nextPageWithAnnotations());
|
||||
}
|
||||
|
||||
viewExcludePages(): void {
|
||||
this.actionPerformed.emit('view-exclude-pages');
|
||||
}
|
||||
|
||||
private _filterAnnotations(
|
||||
annotations: AnnotationWrapper[],
|
||||
primary: INestedFilter[],
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
[id]="'quick-nav-page-' + number"
|
||||
class="page-wrapper"
|
||||
>
|
||||
<mat-icon svgIcon="red:page"></mat-icon>
|
||||
<mat-icon [svgIcon]="showDottedIcon ? 'red:excluded-page' : 'red:page'"></mat-icon>
|
||||
<div class="text">
|
||||
{{ number }}
|
||||
</div>
|
||||
|
||||
@ -2,18 +2,19 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, S
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { ViewedPagesService } from '../../shared/services/viewed-pages.service';
|
||||
import { IViewedPage } from '@red/domain';
|
||||
import { AutoUnsubscribe } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-page-indicator',
|
||||
templateUrl: './page-indicator.component.html',
|
||||
styleUrls: ['./page-indicator.component.scss'],
|
||||
})
|
||||
export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges, OnInit, OnDestroy {
|
||||
@Input() active: boolean;
|
||||
@Input() showDottedIcon = false;
|
||||
@Input() number: number;
|
||||
@Input() viewedPages: IViewedPage[];
|
||||
@Input() activeSelection = false;
|
||||
@ -22,7 +23,6 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
|
||||
pageReadTimeout: number = null;
|
||||
canMarkPagesAsViewed: boolean;
|
||||
private _subscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private readonly _viewedPagesService: ViewedPagesService,
|
||||
@ -30,7 +30,9 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _permissionService: PermissionsService,
|
||||
) {}
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
get activePage() {
|
||||
return this.viewedPages?.find(p => p.page === this.number);
|
||||
@ -46,7 +48,7 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._subscription = this._appStateService.fileChanged$.subscribe(() => {
|
||||
this.addSubscription = this._appStateService.fileChanged$.subscribe(() => {
|
||||
if (this.canMarkPagesAsViewed !== this._permissionService.canMarkPagesAsViewed()) {
|
||||
this.canMarkPagesAsViewed = this._permissionService.canMarkPagesAsViewed();
|
||||
this._handlePageRead();
|
||||
@ -70,24 +72,21 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this._subscription) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private _handlePageRead() {
|
||||
if (this.canMarkPagesAsViewed) {
|
||||
if (this.pageReadTimeout) {
|
||||
clearTimeout(this.pageReadTimeout);
|
||||
}
|
||||
if (this.active && !this.read) {
|
||||
this.pageReadTimeout = window.setTimeout(() => {
|
||||
if (this.active && !this.read) {
|
||||
this._markPageRead();
|
||||
}
|
||||
}, this._configService.values.AUTO_READ_TIME * 1000);
|
||||
}
|
||||
if (!this.canMarkPagesAsViewed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pageReadTimeout) {
|
||||
clearTimeout(this.pageReadTimeout);
|
||||
}
|
||||
|
||||
if (this.active && !this.read) {
|
||||
this.pageReadTimeout = window.setTimeout(() => {
|
||||
if (this.active && !this.read) {
|
||||
this._markPageRead();
|
||||
}
|
||||
}, this._configService.values.AUTO_READ_TIME * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +108,7 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
// }
|
||||
|
||||
private _markPageRead() {
|
||||
this._viewedPagesService
|
||||
this.addSubscription = this._viewedPagesService
|
||||
.addPage({ page: this.number }, this._dossiersService.activeDossierId, this._appStateService.activeFileId)
|
||||
.subscribe(() => {
|
||||
if (this.activePage) {
|
||||
@ -121,7 +120,7 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
private _markPageUnread() {
|
||||
this._viewedPagesService
|
||||
this.addSubscription = this._viewedPagesService
|
||||
.removePage(this._dossiersService.activeDossierId, this._appStateService.activeFileId, this.number)
|
||||
.subscribe(() => {
|
||||
this.viewedPages?.splice(
|
||||
|
||||
@ -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,6 +37,20 @@ import Tools = Core.Tools;
|
||||
import TextTool = Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
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',
|
||||
templateUrl: './pdf-viewer.component.html',
|
||||
@ -55,10 +73,11 @@ 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;
|
||||
private readonly _allowedKeyboardShortcuts = ['+', '-', 'p', 'r', 'Escape'];
|
||||
|
||||
constructor(
|
||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||
@ -86,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);
|
||||
}
|
||||
|
||||
@ -187,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;
|
||||
}
|
||||
@ -208,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();
|
||||
@ -215,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 {
|
||||
@ -225,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();
|
||||
}
|
||||
@ -250,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') {
|
||||
@ -264,18 +286,20 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
if (this._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);
|
||||
}
|
||||
});
|
||||
|
||||
@ -286,7 +310,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
inputElement.value = '';
|
||||
}, 0);
|
||||
if (!event.detail.isVisible) {
|
||||
this.instance.Core.documentViewer.clearSearchResults();
|
||||
this.documentViewer.clearSearchResults();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -295,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) {
|
||||
private _toggleRectangleAnnotationAction(readonly = false) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,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'),
|
||||
@ -410,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);
|
||||
});
|
||||
},
|
||||
@ -428,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
|
||||
@ -469,117 +507,121 @@ 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: dataElements.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: dataElements.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 { ANNOTATION_POPUP, ADD_RECTANGLE, ADD_REDACTION, SHAPE_TOOL_GROUP_BUTTON } = dataElements;
|
||||
const elements = [
|
||||
ADD_REDACTION,
|
||||
ADD_RECTANGLE,
|
||||
'add-false-positive',
|
||||
SHAPE_TOOL_GROUP_BUTTON,
|
||||
'rectangleToolDivider',
|
||||
ANNOTATION_POPUP,
|
||||
];
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let elementsToDisable = [...elements, ADD_RECTANGLE];
|
||||
|
||||
if (this.utils.isCurrentPageExcluded) {
|
||||
const allowedActionsWhenPageExcluded: string[] = [ANNOTATION_POPUP, ADD_RECTANGLE, ADD_REDACTION, SHAPE_TOOL_GROUP_BUTTON];
|
||||
elementsToDisable = elementsToDisable.filter(element => !allowedActionsWhenPageExcluded.includes(element));
|
||||
} else {
|
||||
this.instance.UI.disableTools(['AnnotationCreateRectangle']);
|
||||
this.instance.UI.disableElements([
|
||||
'add-redaction',
|
||||
'add-dictionary',
|
||||
'add-false-positive',
|
||||
'add-rectangle',
|
||||
'shapeToolGroupButton',
|
||||
'rectangleToolDivider',
|
||||
'annotationPopup',
|
||||
]);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@ -29,6 +29,7 @@ export class IconsModule {
|
||||
'enter',
|
||||
'entries',
|
||||
'exclude-pages',
|
||||
'excluded-page',
|
||||
'exit-fullscreen',
|
||||
'folder',
|
||||
'fullscreen',
|
||||
|
||||
@ -1017,7 +1017,7 @@
|
||||
"document-info": "Your Document Info lives here. This includes metadata required on each document.",
|
||||
"download-original-file": "Download Original File",
|
||||
"exclude-pages": "Exclude pages from redaction",
|
||||
"excluded-from-redaction": "excluded from redaction",
|
||||
"excluded-from-redaction": "excluded from automatic redaction",
|
||||
"fullscreen": "Full Screen (F)",
|
||||
"last-reviewer": "Last Reviewed by:",
|
||||
"no-data": {
|
||||
|
||||
16
apps/red-ui/src/assets/icons/general/excluded-page.svg
Normal file
16
apps/red-ui/src/assets/icons/general/excluded-page.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="40px" version="1.1" viewBox="0 0 36 40" width="36px" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" fill-rule="evenodd" id="Exclude-page-from-redaction-NEW" stroke="none" stroke-width="1">
|
||||
<g fill="currentColor" fill-rule="nonzero" id="05.Excluded-with-redactions" transform="translate(-1099.000000, -218.000000)">
|
||||
<g id="Group-16" transform="translate(1087.000000, 173.000000)">
|
||||
<g id="pages" transform="translate(0.000000, 33.000000)">
|
||||
<g id="Group-8" transform="translate(12.000000, 12.000000)">
|
||||
<path
|
||||
d="M36,0 L36,40 L0,40 L1.81854531e-13,35.5 L1.333,35.5 L1.33333333,38.6666667 L4,38.666 L4,40 L8,40 L8,38.666 L12,38.666 L12,40 L16,40 L16,38.666 L20,38.666 L20,40 L24,40 L24,38.666 L28,38.666 L28,40 L32,40 L32,38.666 L34.6666667,38.6666667 L34.666,35.5 L36,35.5 L36,31 L34.666,31 L34.666,26.5 L36,26.5 L36,22 L34.666,22 L34.666,17.5 L36,17.5 L36,13 L34.666,13 L34.666,8.5 L36,8.5 L36,4 L34.666,4 L34.6666667,1.33333333 L32,1.333 L32,1.13242749e-14 L36,0 Z M1.333,26.5 L1.333,31 L1.81854531e-13,31 L1.81854531e-13,26.5 L1.333,26.5 Z M1.333,17.5 L1.333,22 L1.81854531e-13,22 L1.81854531e-13,17.5 L1.333,17.5 Z M1.333,8.5 L1.333,13 L1.81854531e-13,13 L0,8.5 L1.333,8.5 Z M4,1.13242749e-14 L4,1.333 L1.33333333,1.33333333 L1.333,4 L0,4 L0,0 L4,1.13242749e-14 Z M20,1.13242749e-14 L20,1.333 L16,1.333 L16,1.13242749e-14 L20,1.13242749e-14 Z M12,1.13242749e-14 L12,1.333 L8,1.333 L8,1.13242749e-14 L12,1.13242749e-14 Z M28,1.13242749e-14 L28,1.333 L24,1.333 L24,1.13242749e-14 L28,1.13242749e-14 Z"
|
||||
id="Excluded-Page"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@ -1 +1 @@
|
||||
Subproject commit 1df1b1ab899e21093eb07c444acf90def933cb02
|
||||
Subproject commit e1ce89e38d3520ad11960074f74c381429c0251a
|
||||
Loading…
x
Reference in New Issue
Block a user