From 32723d2fc0426e5be0b3e0eef2f77c86feff3e98 Mon Sep 17 00:00:00 2001 From: Timo Date: Thu, 3 Jun 2021 17:39:32 +0300 Subject: [PATCH] cherrypick compare view --- .../watermark/watermark-screen.component.ts | 84 ++---- .../pdf-viewer/pdf-viewer.component.html | 31 +++ .../pdf-viewer/pdf-viewer.component.scss | 61 +++++ .../pdf-viewer/pdf-viewer.component.ts | 250 +++++++++++++++++- .../file-preview-screen.component.ts | 29 +- .../services/annotation-draw.service.ts | 14 +- .../services/dossiers-dialog.service.ts | 33 +++ apps/red-ui/src/app/utils/page-stamper.ts | 63 +++++ apps/red-ui/src/assets/i18n/en.json | 4 + .../general/pdftron-action-close-compare.svg | 16 ++ .../icons/general/pdftron-action-compare.svg | 23 ++ .../src/assets/styles/red-controls.scss | 10 + 12 files changed, 537 insertions(+), 81 deletions(-) create mode 100644 apps/red-ui/src/app/utils/page-stamper.ts create mode 100644 apps/red-ui/src/assets/icons/general/pdftron-action-close-compare.svg create mode 100644 apps/red-ui/src/assets/icons/general/pdftron-action-compare.svg 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 */ +}