diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts
index c02e4e5f1..4b489fa1c 100644
--- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts
+++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts
@@ -10,8 +10,8 @@ import { WatermarkControllerService, WatermarkModelRes } from '@redaction/red-ui
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { ActivatedRoute } from '@angular/router';
-import { hexToRgb } from '@utils/functions';
import { BASE_HREF } from '../../../../tokens';
+import { stampPDFPage } from '../../../../utils/page-stamper';
export const DEFAULT_WATERMARK: WatermarkModelRes = {
text: null,
@@ -169,7 +169,6 @@ export class WatermarkScreenComponent implements OnInit {
responseType: 'blob'
})
.subscribe(blobData => {
- console.log('load blank');
this._instance.loadDocument(blobData, { filename: 'blank.pdf' });
});
});
@@ -184,57 +183,28 @@ export class WatermarkScreenComponent implements OnInit {
const pdfNet = this._instance.PDFNet;
const document = await this._instance.docViewer.getDocument().getPDFDoc();
- await pdfNet.runWithCleanup(
- async () => {
- await document.lock();
+ const text = this.configForm.get('text').value || '';
+ const fontSize = this.configForm.get('fontSize').value;
+ const fontType = this.configForm.get('fontType').value;
+ const orientation: WatermarkModelRes.OrientationEnum =
+ this.configForm.get('orientation').value;
+ const opacity = this.configForm.get('opacity').value;
+ const color = this.configForm.get('hexColor').value;
- const pageSet = await pdfNet.PageSet.createSinglePage(1);
-
- await pdfNet.Stamper.deleteStamps(document, pageSet);
-
- const text = this.configForm.get('text').value || '';
- const fontSize = this.configForm.get('fontSize').value;
- const fontType = this.configForm.get('fontType').value;
- const orientation: WatermarkModelRes.OrientationEnum =
- this.configForm.get('orientation').value;
- const opacity = this.configForm.get('opacity').value;
- const color = this.configForm.get('hexColor').value;
-
- const rgbColor = hexToRgb(color);
-
- const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
- await stamper.setFontColor(
- await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
- );
- await stamper.setOpacity(opacity / 100);
-
- switch (orientation) {
- case WatermarkModelRes.OrientationEnum.VERTICAL:
- await stamper.setAlignment(0, 0);
- await stamper.setRotation(-90);
- break;
- case WatermarkModelRes.OrientationEnum.HORIZONTAL:
- break;
- case WatermarkModelRes.OrientationEnum.DIAGONAL:
- default:
- await stamper.setAlignment(0, 0);
- await stamper.setRotation(-45);
- }
-
- const font = await pdfNet.Font.createAndEmbed(
- document,
- this._convertFont(fontType)
- );
- await stamper.setFont(font);
- await stamper.setTextAlignment(0);
- await stamper.stampText(document, text, pageSet);
-
- this._instance.docViewer.refreshAll();
- this._instance.docViewer.updateView([0], 0);
- this._changeDetectorRef.detectChanges();
- },
- environment.licenseKey ? atob(environment.licenseKey) : null
+ await stampPDFPage(
+ document,
+ pdfNet,
+ text,
+ fontSize,
+ fontType,
+ orientation,
+ opacity,
+ color,
+ 1
);
+ this._instance.docViewer.refreshAll();
+ this._instance.docViewer.updateView([0], 0);
+ this._changeDetectorRef.detectChanges();
}
private _initForm() {
@@ -262,16 +232,4 @@ export class WatermarkScreenComponent implements OnInit {
]
});
}
-
- private _convertFont(fontType: any): number {
- switch (fontType) {
- case 'times-new-roman':
- return 0;
- case 'helvetica':
- return 4;
- case 'courier':
- return 8;
- }
- return 4;
- }
}
diff --git a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.html b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.html
index af7ce7b13..12183c7b5 100644
--- a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.html
+++ b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.html
@@ -1,3 +1,34 @@
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.scss b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.scss
index fa75a03a3..a4ca9fb65 100644
--- a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.scss
+++ b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.scss
@@ -1,3 +1,5 @@
+@import '../../../../../assets/styles/red-variables';
+
.page {
display: flex;
flex-direction: column;
@@ -20,3 +22,62 @@
margin-left: 15px;
}
}
+
+.pagination {
+ position: absolute;
+ bottom: 20px;
+ left: 50%;
+ transform: translate(-50%);
+ background: $white;
+ color: $grey-7;
+ border: 1px solid $grey-7;
+ border-radius: 8px;
+ padding: 6px 2px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ > div {
+ height: 16px;
+ cursor: default;
+ }
+
+ .separator {
+ padding-left: 2px;
+ padding-right: 2px;
+ }
+
+ .page-number-input {
+ -moz-appearance: textfield;
+ &::-webkit-outer-spin-button,
+ &::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+ color: $grey-7;
+ text-decoration: none;
+ outline: none;
+ border: none;
+ padding: 0;
+ margin: 0;
+ cursor: pointer;
+
+ &:hover,
+ &:focus {
+ font-weight: bold;
+ border-bottom: 1px solid $grey-7;
+ }
+ }
+
+ .chevron-icon {
+ height: 16px;
+ transform: rotate(-90deg);
+ cursor: pointer;
+ padding-left: 4px;
+ padding-right: 4px;
+
+ &:hover {
+ color: $grey-1;
+ }
+ }
+}
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 ab0b3c1c3..2a78fb0a2 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
@@ -26,7 +26,10 @@ import { UserPreferenceService } from '@services/user-preference.service';
import { translateQuads } from '@utils/pdf-coordinates';
import { BASE_HREF } from '../../../../tokens';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
+import { stampPDFPage } from '../../../../utils/page-stamper';
+import { LoadingService } from '../../../../services/loading.service';
import TextTool = Tools.TextTool;
+import { DossiersDialogService } from '../../services/dossiers-dialog.service';
@Component({
selector: 'redaction-pdf-viewer',
@@ -48,6 +51,10 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@Output() viewerReady = new EventEmitter();
@Output() annotationsChanged = new EventEmitter();
@ViewChild('viewer', { static: true }) viewer: ElementRef;
+ @ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
+
+ viewMode: 'STANDARD' | 'COMPARE' = 'STANDARD';
+
instance: WebViewerInstance;
private _selectedText = '';
private readonly _allowedKeyboardShortcuts = ['+', '-', 'p', 'r', 'Escape'];
@@ -56,11 +63,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@Inject(BASE_HREF) private readonly _baseHref: string,
private readonly _translateService: TranslateService,
private readonly _manualAnnotationService: ManualAnnotationService,
+ private readonly _projectsDialogService: DossiersDialogService,
private readonly _ngZone: NgZone,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _annotationDrawService: AnnotationDrawService,
private readonly _annotationActionsService: AnnotationActionsService,
- private readonly _appConfigService: AppConfigService
+ private readonly _appConfigService: AppConfigService,
+ private readonly _appLoadStateService: LoadingService
) {}
ngOnInit() {
@@ -114,7 +123,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this.instance.annotManager.deselectAnnotations(ann);
}
- navigateToPage(pageNumber: number) {
+ navigateToPage(pageNumber) {
const activePage = this.instance.docViewer.getCurrentPage();
if (activePage !== pageNumber) {
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0);
@@ -124,10 +133,11 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
setInitialViewerState() {
// viewer init
this.instance.setFitMode('FitPage');
+
const instanceDisplayMode = this.instance.docViewer
.getDisplayModeManager()
.getDisplayMode();
- instanceDisplayMode.mode = 'Single';
+ instanceDisplayMode.mode = this.viewMode === 'STANDARD' ? 'Single' : 'Facing';
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
}
@@ -139,6 +149,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this.instance = await WebViewer(
{
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
+ fullAPI: true,
path: this._convertPath('/assets/wv-resources'),
css: this._convertPath('/assets/pdftron/stylesheet.css'),
backendType: 'ems'
@@ -245,6 +256,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
private _disableElements() {
this.instance.disableElements([
+ 'pageNavOverlay',
'menuButton',
'selectToolButton',
'textHighlightToolButton',
@@ -279,8 +291,36 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
img: this._convertPath('/assets/icons/general/rectangle.svg'),
title: 'annotation.rectangle'
});
+ 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();
+ }
+ });
+ 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();
+ }
+ });
+ originalHeaderItems.splice(13, 0, {
+ type: 'divider',
+ dataElement: 'compareToolDivider'
+ });
+
+ console.log(originalHeaderItems);
});
+ this.instance.disableElements(['closeCompareButton']);
+
this.instance.docViewer.getTool('AnnotationCreateRectangle').setStyles(() => ({
StrokeThickness: 2,
StrokeColor: this._annotationDrawService.getColor(this.instance, 'manual'),
@@ -365,8 +405,8 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
// cleanup selection and button state
this.deselectAllAnnotations();
- this.instance.disableElements(['shapeToolGroupButton']);
- this.instance.enableElements(['shapeToolGroupButton']);
+ this.instance.disableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
+ this.instance.enableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
// dispatch event
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(
@@ -480,6 +520,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
'add-rectangle',
'add-false-positive',
'shapeToolGroupButton',
+ 'rectangleToolDivider',
'annotationPopup'
]);
if (this._selectedText.length > 2) {
@@ -493,6 +534,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
'add-false-positive',
'add-rectangle',
'shapeToolGroupButton',
+ 'rectangleToolDivider',
'annotationPopup'
]);
}
@@ -591,4 +633,202 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
private _convertPath(path: string): string {
return this._baseHref + path;
}
+
+ async uploadFile(files: any) {
+ const fileToCompare = files[0];
+
+ const fileReader = new FileReader();
+
+ if (fileToCompare) {
+ fileReader.onload = async () => {
+ const pdfData = fileReader.result;
+
+ const PDFNet = this.instance.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._appLoadStateService.start();
+
+ try {
+ const maxPageCount = Math.max(
+ currentDocumentPageCount,
+ compareDocumentPageCount
+ );
+
+ for (let i = 1; i <= maxPageCount; i++) {
+ if (currentDocumentPageCount >= i) {
+ await mergedDocument.insertPages(
+ i * 2,
+ currentDocument,
+ i,
+ i,
+ PDFNet.PDFDoc.InsertFlag.e_none
+ );
+ } else {
+ const pageToCopy = await compareDocument.getPage(i);
+ 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()
+ );
+ }
+ if (compareDocumentPageCount >= i) {
+ await mergedDocument.insertPages(
+ i * 2,
+ compareDocument,
+ i,
+ i,
+ PDFNet.PDFDoc.InsertFlag.e_none
+ );
+ } else {
+ const pageToCopy = await currentDocument.getPage(i);
+ 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()
+ );
+ }
+ }
+
+ const buffer = await mergedDocument.saveMemoryBuffer(
+ PDFNet.SDFDoc.SaveOptions.e_linearized
+ );
+
+ const mergedDocumentBuffer = new Blob([buffer], {
+ type: 'application/pdf'
+ });
+
+ this.viewMode = 'COMPARE';
+
+ this.instance.loadDocument(mergedDocumentBuffer, {
+ filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
+ });
+ this.instance.disableElements(['compareButton']);
+ this.instance.enableElements(['closeCompareButton']);
+
+ this.compareFileInput.nativeElement.value = null;
+
+ this.navigateToPage(1);
+ } catch (e) {}
+ this._appLoadStateService.stop();
+ };
+
+ if (currentDocumentPageCount !== compareDocumentPageCount) {
+ this._projectsDialogService.openCompareFileConfirmationDialog(
+ null,
+ fileToCompare.name,
+ currentDocumentPageCount,
+ compareDocumentPageCount,
+ loadCompareDocument
+ );
+ } else {
+ loadCompareDocument();
+ }
+ };
+ }
+
+ fileReader.readAsArrayBuffer(fileToCompare);
+ }
+
+ get isCompareMode() {
+ return this.viewMode === 'COMPARE';
+ }
+
+ async closeCompareMode() {
+ this.viewMode = 'STANDARD';
+ const PDFNet = this.instance.PDFNet;
+ await PDFNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
+ const currentDocument = await PDFNet.PDFDoc.createFromBuffer(
+ await this.fileData.arrayBuffer()
+ );
+ this.instance.loadDocument(currentDocument, {
+ filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
+ });
+ this.instance.disableElements(['closeCompareButton']);
+ this.instance.enableElements(['compareButton']);
+ this.navigateToPage(1);
+ }
+
+ get currentPage() {
+ try {
+ return this.isCompareMode
+ ? Math.ceil(this.instance?.docViewer?.getCurrentPage() / 2)
+ : this.instance?.docViewer?.getCurrentPage();
+ } catch (e) {
+ return null;
+ }
+ }
+
+ get totalPages() {
+ try {
+ return this.isCompareMode
+ ? Math.ceil(this.instance?.docViewer?.getPageCount() / 2)
+ : this.instance?.docViewer?.getPageCount();
+ } catch (e) {
+ return null;
+ }
+ }
+
+ private get _currentInternalPage() {
+ return this.instance?.docViewer?.getCurrentPage();
+ }
+
+ private get _totalInternalPages() {
+ return this.instance?.docViewer?.getPageCount();
+ }
+
+ previousPage() {
+ if (this._currentInternalPage > 1) {
+ this.navigateToPage(Math.max(this._currentInternalPage - this.paginationOffset, 1));
+ }
+ }
+
+ nextPage() {
+ if (this._currentInternalPage < this._totalInternalPages) {
+ this.navigateToPage(
+ Math.min(
+ this._currentInternalPage + this.paginationOffset,
+ this._totalInternalPages
+ )
+ );
+ }
+ }
+
+ get paginationOffset() {
+ return this.isCompareMode ? 2 : 1;
+ }
}
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
index 6c3b829ce..9f3185390 100644
--- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts
@@ -77,7 +77,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private _instance: WebViewerInstance;
private _lastPage: string;
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
- @ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
+ @ViewChild(PdfViewerComponent) viewerComponent: PdfViewerComponent;
constructor(
readonly appStateService: AppStateService,
@@ -127,7 +127,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
}
get activeViewerPage() {
- return this._instance?.docViewer?.getCurrentPage();
+ return this.viewerComponent?.viewMode === 'STANDARD'
+ ? this._instance?.docViewer?.getCurrentPage()
+ : this._instance?.docViewer?.getCurrentPage() % 2 === 0
+ ? this._instance?.docViewer?.getCurrentPage() / 2
+ : (this._instance?.docViewer?.getCurrentPage() + 1) / 2;
}
get canSwitchToRedactedView() {
@@ -343,18 +347,20 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
| { annotations: AnnotationWrapper[]; multiSelect: boolean }
) {
if (annotations) {
- this._viewerComponent.selectAnnotations(annotations);
+ this.viewerComponent.selectAnnotations(annotations);
} else {
- this._viewerComponent.deselectAllAnnotations();
+ this.viewerComponent.deselectAllAnnotations();
}
}
deselectAnnotations(annotations: AnnotationWrapper[]) {
- this._viewerComponent.deselectAnnotations(annotations);
+ this.viewerComponent.deselectAnnotations(annotations);
}
selectPage(pageNumber: number) {
- this._viewerComponent.navigateToPage(pageNumber);
+ this.viewerComponent.navigateToPage(
+ this.viewerComponent.isCompareMode ? pageNumber * 2 - 1 : pageNumber
+ );
this._workloadComponent?.scrollAnnotationsToPage(pageNumber, 'always');
}
@@ -443,6 +449,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
viewerReady($event: WebViewerInstance) {
this._instance = $event;
this._cleanupAndRedrawManualAnnotations();
+ this._updateCanPerformActions();
this.viewReady = true;
// Go to initial page from query params
const pageNumber = this._lastPage || this._activatedRoute.snapshot.queryParams.page;
@@ -586,7 +593,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private _updateCanPerformActions() {
this.canPerformAnnotationActions =
- this.permissionsService.canPerformAnnotationActions() && this.viewMode === 'STANDARD';
+ this.permissionsService.canPerformAnnotationActions() &&
+ this.viewMode === 'STANDARD' &&
+ !this.viewerComponent?.isCompareMode;
}
private async _loadFileData(performUpdate: boolean = false): Promise {
@@ -626,7 +635,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._annotationDrawService.drawAnnotations(
this._instance,
this.annotationData.allAnnotations,
- this.hideSkipped
+ this.hideSkipped,
+ this.viewerComponent.isCompareMode
);
});
}
@@ -650,7 +660,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._annotationDrawService.drawAnnotations(
this._instance,
newPageAnnotations,
- this.hideSkipped
+ this.hideSkipped,
+ this.viewerComponent.isCompareMode
);
}
});
diff --git a/apps/red-ui/src/app/modules/dossier/services/annotation-draw.service.ts b/apps/red-ui/src/app/modules/dossier/services/annotation-draw.service.ts
index 70009633f..68a4dca74 100644
--- a/apps/red-ui/src/app/modules/dossier/services/annotation-draw.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/services/annotation-draw.service.ts
@@ -22,11 +22,14 @@ export class AnnotationDrawService {
drawAnnotations(
activeViewer: WebViewerInstance,
annotationWrappers: AnnotationWrapper[],
- hideSkipped: boolean = false
+ hideSkipped: boolean = false,
+ compareMode: boolean = false
) {
const annotations = [];
annotationWrappers.forEach(annotation => {
- annotations.push(this.computeAnnotation(activeViewer, annotation, hideSkipped));
+ annotations.push(
+ this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode)
+ );
});
const annotationManager = activeViewer.annotManager;
@@ -91,9 +94,12 @@ export class AnnotationDrawService {
computeAnnotation(
activeViewer: WebViewerInstance,
annotationWrapper: AnnotationWrapper,
- hideSkipped: boolean = false
+ hideSkipped: boolean = false,
+ compareMode: boolean = false
) {
- const pageNumber = annotationWrapper.pageNumber;
+ const pageNumber = compareMode
+ ? annotationWrapper.pageNumber * 2 - 1
+ : annotationWrapper.pageNumber;
const highlight = new activeViewer.Annotations.TextHighlightAnnotation();
highlight.PageNumber = pageNumber;
highlight.StrokeColor = this.getColor(
diff --git a/apps/red-ui/src/app/modules/dossier/services/dossiers-dialog.service.ts b/apps/red-ui/src/app/modules/dossier/services/dossiers-dialog.service.ts
index 2401e51b9..620255fb8 100644
--- a/apps/red-ui/src/app/modules/dossier/services/dossiers-dialog.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/services/dossiers-dialog.service.ts
@@ -363,4 +363,37 @@ export class DossiersDialogService {
return ref;
}
+
+ openCompareFileConfirmationDialog(
+ $event: MouseEvent,
+ fileName: string,
+ currentDocumentPageCount: number,
+ compareDocumentPageCount: number,
+ callback?: Function
+ ): MatDialogRef {
+ $event?.stopPropagation();
+
+ const ref = this._dialog.open(ConfirmationDialogComponent, {
+ ...dialogConfig,
+ data: new ConfirmationDialogInput({
+ title: 'confirmation-dialog.compare-file.title',
+ question: 'confirmation-dialog.compare-file.question',
+ translateParams: {
+ fileName: fileName,
+ currentDocumentPageCount,
+ compareDocumentPageCount
+ }
+ })
+ });
+
+ ref.afterClosed().subscribe(result => {
+ if (result) {
+ if (callback) {
+ callback();
+ }
+ }
+ });
+
+ return ref;
+ }
}
diff --git a/apps/red-ui/src/app/utils/page-stamper.ts b/apps/red-ui/src/app/utils/page-stamper.ts
new file mode 100644
index 000000000..0f33510e5
--- /dev/null
+++ b/apps/red-ui/src/app/utils/page-stamper.ts
@@ -0,0 +1,63 @@
+import { hexToRgb } from './functions';
+import { environment } from '../../environments/environment';
+
+export async function stampPDFPage(
+ document: any,
+ PDFNet: any,
+ text: string,
+ fontSize: number,
+ fontType: string,
+ orientation: string,
+ opacity: number,
+ color: string,
+ page: number
+) {
+ await PDFNet.runWithCleanup(
+ async () => {
+ await document.lock();
+
+ const pageSet = await PDFNet.PageSet.createSinglePage(page);
+
+ await PDFNet.Stamper.deleteStamps(document, pageSet);
+
+ const rgbColor = hexToRgb(color);
+
+ const stamper = await PDFNet.Stamper.create(3, fontSize, 0);
+ await stamper.setFontColor(
+ await PDFNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
+ );
+ await stamper.setOpacity(opacity / 100);
+
+ switch (orientation) {
+ case 'VERTICAL':
+ await stamper.setAlignment(0, 0);
+ await stamper.setRotation(-90);
+ break;
+ case 'HORIZONTAL':
+ break;
+ case 'DIAGONAL':
+ default:
+ await stamper.setAlignment(0, 0);
+ await stamper.setRotation(-45);
+ }
+
+ const font = await PDFNet.Font.createAndEmbed(document, convertFont(fontType));
+ await stamper.setFont(font);
+ await stamper.setTextAlignment(0);
+ await stamper.stampText(document, text, pageSet);
+ },
+ environment.licenseKey ? atob(environment.licenseKey) : null
+ );
+}
+
+function convertFont(type: string): number {
+ switch (type) {
+ case 'times-new-roman':
+ return 0;
+ case 'helvetica':
+ return 4;
+ case 'courier':
+ return 8;
+ }
+ return 4;
+}
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index 188084b22..62132e841 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -678,6 +678,10 @@
"deny": "Cancel"
},
"confirmation-dialog": {
+ "compare-file": {
+ "title": "Compare with file: {{fileName}}",
+ "question": "Warning!
Number of pages does not match, current document has {{currentDocumentPageCount}} page(s). Uploaded document has {{compareDocumentPageCount}} page(s).
Do you wish to proceed?"
+ },
"assign-file-to-me": {
"title": "Re-assign reviewer",
"question": "This document is currently reviewed by someone else. Do you want to become the reviewer and assign yourself to this document?"
diff --git a/apps/red-ui/src/assets/icons/general/pdftron-action-close-compare.svg b/apps/red-ui/src/assets/icons/general/pdftron-action-close-compare.svg
new file mode 100644
index 000000000..df53361e4
--- /dev/null
+++ b/apps/red-ui/src/assets/icons/general/pdftron-action-close-compare.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/apps/red-ui/src/assets/icons/general/pdftron-action-compare.svg b/apps/red-ui/src/assets/icons/general/pdftron-action-compare.svg
new file mode 100644
index 000000000..a7c3e888e
--- /dev/null
+++ b/apps/red-ui/src/assets/icons/general/pdftron-action-compare.svg
@@ -0,0 +1,23 @@
+
+
+
diff --git a/apps/red-ui/src/assets/styles/red-controls.scss b/apps/red-ui/src/assets/styles/red-controls.scss
index f8ac880d9..339ec9625 100644
--- a/apps/red-ui/src/assets/styles/red-controls.scss
+++ b/apps/red-ui/src/assets/styles/red-controls.scss
@@ -36,3 +36,13 @@
width: 10px;
height: 10px;
}
+
+.noselect {
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Safari */
+ -khtml-user-select: none; /* Konqueror HTML */
+ -moz-user-select: none; /* Old versions of Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none; /* Non-prefixed version, currently
+ supported by Chrome, Edge, Opera and Firefox */
+}