Pull request #236: RED-1771

Merge in RED/ui from RED-1771 to master

* commit '483bd71c2f4998961bc9b96f2c79ee2d6eb65bdd':
  Stamp excluded pages
  Display page excluded from redaction in workload
This commit is contained in:
Adina Teudan 2021-07-13 17:55:00 +02:00
commit 3616988cf3
18 changed files with 107 additions and 53 deletions

View File

@ -70,8 +70,8 @@ export class WatermarkScreenComponent implements OnInit {
} }
@debounce() @debounce()
configChanged() { async configChanged() {
this._drawWatermark(); await this._drawWatermark();
} }
save() { save() {
@ -111,17 +111,17 @@ export class WatermarkScreenComponent implements OnInit {
); );
} }
revert() { async revert() {
this.configForm.setValue({ ...this._watermark }); this.configForm.setValue({ ...this._watermark });
this.configChanged(); await this.configChanged();
} }
triggerChanges() {} triggerChanges() {}
setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) { async setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) {
if (!this.configForm.get(type).disabled) { if (!this.configForm.get(type).disabled) {
this.configForm.get(type).setValue(value); this.configForm.get(type).setValue(value);
this.configChanged(); await this.configChanged();
} }
} }
@ -157,9 +157,9 @@ export class WatermarkScreenComponent implements OnInit {
).then(instance => { ).then(instance => {
this._instance = instance; this._instance = instance;
instance.docViewer.on('documentLoaded', () => { instance.docViewer.on('documentLoaded', async () => {
this.viewReady = true; this.viewReady = true;
this._drawWatermark(); await this._drawWatermark();
}); });
this._disableElements(); this._disableElements();

View File

@ -30,6 +30,6 @@ export class DocumentInfoComponent {
} }
edit() { edit() {
this._dialogService.openDialog('document-info', null, this.file); this._dialogService.openDialog('documentInfo', null, this.file);
} }
} }

View File

@ -24,7 +24,7 @@ export class DossierListingActionsComponent {
) {} ) {}
openEditDossierDialog($event: MouseEvent, dossierWrapper: DossierWrapper) { openEditDossierDialog($event: MouseEvent, dossierWrapper: DossierWrapper) {
this._dialogService.openDialog('edit-dossier', $event, { this._dialogService.openDialog('editDossier', $event, {
dossierWrapper, dossierWrapper,
afterSave: () => { afterSave: () => {
this.actionPerformed.emit(); this.actionPerformed.emit();

View File

@ -27,7 +27,7 @@
> >
<div> <div>
<redaction-circle-button <redaction-circle-button
(action)="closeExcludePagesView.emit()" (action)="actionPerformed.emit('view-exclude-pages')"
icon="red:close" icon="red:close"
tooltip="file-preview.tabs.exclude-pages.close" tooltip="file-preview.tabs.exclude-pages.close"
tooltipPosition="before" tooltipPosition="before"
@ -142,13 +142,29 @@
redactionHasScrollbar redactionHasScrollbar
tabindex="1" tabindex="1"
> >
<ng-container *ngIf="!displayedAnnotations[activeViewerPage]"> <ng-container
*ngIf="activeViewerPage && !displayedAnnotations[activeViewerPage]"
>
<redaction-empty-state <redaction-empty-state
[horizontalPadding]="24" [horizontalPadding]="24"
[verticalPadding]="40" [verticalPadding]="40"
icon="red:document" icon="red:document"
screen="file-preview" screen="file-preview"
></redaction-empty-state> >
<ng-container
*ngIf="
fileData?.fileStatus?.excludedPages?.includes(activeViewerPage)
"
>
{{ 'file-preview.tabs.annotations.page-is' | translate }}
<a
(click)="actionPerformed.emit('view-exclude-pages')"
translate="file-preview.excluded-from-redaction"
>
</a
>.
</ng-container>
</redaction-empty-state>
<div class="no-annotations-buttons-container mt-32"> <div class="no-annotations-buttons-container mt-32">
<redaction-icon-button <redaction-icon-button
(action)="jumpToPreviousWithAnnotations()" (action)="jumpToPreviousWithAnnotations()"
@ -267,8 +283,8 @@
<ng-template #annotationFilterActionTemplate let-filter="filter"> <ng-template #annotationFilterActionTemplate let-filter="filter">
<redaction-circle-button <redaction-circle-button
*ngIf="filter.key === 'skipped'"
(action)="toggleSkipped.emit($event)" (action)="toggleSkipped.emit($event)"
*ngIf="filter.key === 'skipped'"
[icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'" [icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'"
type="dark-bg" type="dark-bg"
></redaction-circle-button> ></redaction-circle-button>

View File

@ -50,7 +50,6 @@ export class FileWorkloadComponent {
@Output() selectPage = new EventEmitter<number>(); @Output() selectPage = new EventEmitter<number>();
@Output() toggleSkipped = new EventEmitter<any>(); @Output() toggleSkipped = new EventEmitter<any>();
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>(); @Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
@Output() closeExcludePagesView = new EventEmitter();
@Output() actionPerformed = new EventEmitter<string>(); @Output() actionPerformed = new EventEmitter<string>();
displayedPages: number[] = []; displayedPages: number[] = [];
pagesPanelActive = true; pagesPanelActive = true;

View File

@ -9,14 +9,14 @@
type="file" type="file"
/> />
<div *ngIf="utils.totalPages && utils.currentPage" class="pagination noselect"> <div *ngIf="utils?.totalPages && utils?.currentPage" class="pagination noselect">
<div (click)="utils.previousPage()"> <div (click)="utils.previousPage()">
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon> <mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
</div> </div>
<div> <div>
<input <input
#pageInput #pageInput
(change)="utils.navigateToPageByInput(pageInput.value)" (change)="utils.navigateToPage(pageInput.value)"
[max]="utils.totalPages" [max]="utils.totalPages"
[value]="utils.currentPage" [value]="utils.currentPage"
class="page-number-input" class="page-number-input"

View File

@ -150,8 +150,8 @@ export class PdfViewerComponent implements OnInit, OnChanges {
() => { () => {
this.viewMode = 'COMPARE'; this.viewMode = 'COMPARE';
}, },
(page: number) => { () => {
this.utils.navigateToPage(page); this.utils.navigateToPage(1);
}, },
PDFNet PDFNet
); );

View File

@ -196,7 +196,7 @@ export class DossierListingScreenComponent
this._calculateData(); this._calculateData();
await this._router.navigate([`/main/dossiers/${addResponse.dossier.dossierId}`]); await this._router.navigate([`/main/dossiers/${addResponse.dossier.dossierId}`]);
if (addResponse.addMembers) { if (addResponse.addMembers) {
this._dialogService.openDialog('edit-dossier', null, { this._dialogService.openDialog('editDossier', null, {
dossierWrapper: addResponse.dossier, dossierWrapper: addResponse.dossier,
section: 'members' section: 'members'
}); });

View File

@ -293,14 +293,14 @@ export class DossierOverviewScreenComponent
} }
openEditDossierDialog($event: MouseEvent) { openEditDossierDialog($event: MouseEvent) {
this._dialogService.openDialog('edit-dossier', $event, { this._dialogService.openDialog('editDossier', $event, {
dossierWrapper: this.activeDossier dossierWrapper: this.activeDossier
}); });
} }
openAssignDossierMembersDialog(): void { openAssignDossierMembersDialog(): void {
this._dialogService.openDialog( this._dialogService.openDialog(
'edit-dossier', 'editDossier',
null, null,
{ {
dossierWrapper: this.activeDossier, dossierWrapper: this.activeDossier,

View File

@ -174,7 +174,6 @@
#fileWorkloadComponent #fileWorkloadComponent
(actionPerformed)="fileActionPerformed($event)" (actionPerformed)="fileActionPerformed($event)"
(annotationsChanged)="annotationsChangedByReviewAction($event)" (annotationsChanged)="annotationsChangedByReviewAction($event)"
(closeExcludePagesView)="excludePages = false"
(deselectAnnotations)="deselectAnnotations($event)" (deselectAnnotations)="deselectAnnotations($event)"
(selectAnnotations)="selectAnnotations($event)" (selectAnnotations)="selectAnnotations($event)"
(selectPage)="selectPage($event)" (selectPage)="selectPage($event)"

View File

@ -46,6 +46,8 @@ import {
processFilters processFilters
} from '@shared/components/filters/popup-filter/utils/filter-utils'; } from '@shared/components/filters/popup-filter/utils/filter-utils';
import { LoadingService } from '../../../../services/loading.service'; import { LoadingService } from '../../../../services/loading.service';
import { stampPDFPage } from '../../../../utils/page-stamper';
import { TranslateService } from '@ngx-translate/core';
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
@ -96,7 +98,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private readonly _statusControllerService: StatusControllerService, private readonly _statusControllerService: StatusControllerService,
private readonly _ngZone: NgZone, private readonly _ngZone: NgZone,
private readonly _fileManagementControllerService: FileManagementControllerService, private readonly _fileManagementControllerService: FileManagementControllerService,
private readonly _loadingService: LoadingService private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService
) { ) {
document.documentElement.addEventListener('fullscreenchange', () => { document.documentElement.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
@ -361,7 +364,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
} }
selectPage(pageNumber: number) { selectPage(pageNumber: number) {
this.viewerComponent.utils.navigateToPageByInput(pageNumber); this.viewerComponent.utils.navigateToPage(pageNumber);
this._workloadComponent?.scrollAnnotationsToPage(pageNumber, 'always'); this._workloadComponent?.scrollAnnotationsToPage(pageNumber, 'always');
} }
@ -447,8 +450,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this._changeDetectorRef.detectChanges(); this._changeDetectorRef.detectChanges();
} }
viewerReady($event: WebViewerInstance) { async viewerReady($event: WebViewerInstance) {
this._instance = $event; this._instance = $event;
await this._stampExcludedPages();
this._cleanupAndRedrawManualAnnotations(); this._cleanupAndRedrawManualAnnotations();
this._updateCanPerformActions(); this._updateCanPerformActions();
this.viewReady = true; this.viewReady = true;
@ -570,6 +574,30 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
window.open(`/html-debug/${this.dossierId}/${this.fileId}`, '_blank'); window.open(`/html-debug/${this.dossierId}/${this.fileId}`, '_blank');
} }
private async _stampPage(page: number) {
const document = await this._instance.docViewer.getDocument().getPDFDoc();
await stampPDFPage(
document,
this._instance.PDFNet,
this._translateService.instant('file-preview.excluded-from-redaction'),
25,
'courier',
'DIAGONAL',
33,
'#283241',
page
);
}
private async _stampExcludedPages() {
for (const page of this.fileData.fileStatus.excludedPages) {
await this._stampPage(page);
}
this._instance.docViewer.refreshAll();
this._instance.docViewer.updateView([this.activeViewerPage], this.activeViewerPage);
this._changeDetectorRef.detectChanges();
}
private _subscribeToFileUpdates(): void { private _subscribeToFileUpdates(): void {
this.filesAutoUpdateTimer = timer(0, 5000) this.filesAutoUpdateTimer = timer(0, 5000)
.pipe(tap(async () => await this.appStateService.reloadActiveFile())) .pipe(tap(async () => await this.appStateService.reloadActiveFile()))

View File

@ -33,7 +33,7 @@ const dialogConfig = {
// TODO: Continue refactor // TODO: Continue refactor
type DialogType = 'confirm' | 'document-info' | 'edit-dossier'; type DialogType = 'confirm' | 'documentInfo' | 'editDossier';
type DossiersDialogConfig = { type DossiersDialogConfig = {
[key in DialogType]: { [key in DialogType]: {
@ -48,11 +48,11 @@ export class DossiersDialogService extends DialogService<DialogType> {
confirm: { confirm: {
component: ConfirmationDialogComponent component: ConfirmationDialogComponent
}, },
'document-info': { documentInfo: {
component: DocumentInfoDialogComponent, component: DocumentInfoDialogComponent,
dialogConfig: { autoFocus: true } dialogConfig: { autoFocus: true }
}, },
'edit-dossier': { editDossier: {
component: EditDossierDialogComponent, component: EditDossierDialogComponent,
dialogConfig: { ...this._largeConfig, autoFocus: true } dialogConfig: { ...this._largeConfig, autoFocus: true }
} }

View File

@ -38,7 +38,7 @@ export const loadCompareDocumentWrapper = async (
instance, instance,
fileStatus, fileStatus,
setCompareViewMode: () => void, setCompareViewMode: () => void,
navigateToPage: (number) => void, navigateToPage: () => void,
PDFNet PDFNet
) => { ) => {
try { try {
@ -65,7 +65,7 @@ export const loadCompareDocumentWrapper = async (
instance.disableElements(['compareButton']); instance.disableElements(['compareButton']);
instance.enableElements(['closeCompareButton']); instance.enableElements(['closeCompareButton']);
navigateToPage(1); navigateToPage();
} catch (e) { } catch (e) {
console.error(e); console.error(e);
} }

View File

@ -91,28 +91,22 @@ export class PdfViewerUtils {
return this.instance?.docViewer?.getPageCount(); return this.instance?.docViewer?.getPageCount();
} }
navigateToPage(pageNumber) { navigateToPage(pageNumber: string | number) {
const activePage = this.instance.docViewer.getCurrentPage(); const parsedNumber = typeof pageNumber === 'string' ? parseInt(pageNumber, 10) : pageNumber;
if (activePage !== pageNumber) { this._navigateToPage(
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0); this.paginationOffset === 2 ? parsedNumber * this.paginationOffset - 1 : parsedNumber
}
}
navigateToPageByInput(pageNumber) {
this.navigateToPage(
this.paginationOffset === 2 ? pageNumber * this.paginationOffset - 1 : pageNumber
); );
} }
previousPage() { previousPage() {
if (this._currentInternalPage > 1) { if (this._currentInternalPage > 1) {
this.navigateToPage(Math.max(this._currentInternalPage - this.paginationOffset, 1)); this._navigateToPage(Math.max(this._currentInternalPage - this.paginationOffset, 1));
} }
} }
nextPage() { nextPage() {
if (this._currentInternalPage < this._totalInternalPages) { if (this._currentInternalPage < this._totalInternalPages) {
this.navigateToPage( this._navigateToPage(
Math.min( Math.min(
this._currentInternalPage + this.paginationOffset, this._currentInternalPage + this.paginationOffset,
this._totalInternalPages this._totalInternalPages
@ -178,6 +172,13 @@ export class PdfViewerUtils {
this.instance.annotManager.deselectAnnotations(ann); this.instance.annotManager.deselectAnnotations(ann);
} }
private _navigateToPage(pageNumber) {
const activePage = this.instance.docViewer.getCurrentPage();
if (activePage !== pageNumber) {
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0);
}
}
private _getAnnotationById(id: string): Annotations.Annotation { private _getAnnotationById(id: string): Annotations.Annotation {
return this.instance.annotManager.getAnnotationById(id); return this.instance.annotManager.getAnnotationById(id);
} }

View File

@ -7,6 +7,9 @@
class="empty-state" class="empty-state"
> >
<mat-icon [svgIcon]="icon"></mat-icon> <mat-icon [svgIcon]="icon"></mat-icon>
<div class="ng-content-wrapper heading-l">
<ng-content></ng-content>
</div>
<div [translate]="text || screen + '.' + type + '.title'" class="heading-l"></div> <div [translate]="text || screen + '.' + type + '.title'" class="heading-l"></div>
<redaction-icon-button <redaction-icon-button
(action)="action.emit()" (action)="action.emit()"

View File

@ -20,4 +20,8 @@
redaction-icon-button { redaction-icon-button {
margin-top: 24px; margin-top: 24px;
} }
.ng-content-wrapper:not(:empty) + .heading-l {
display: none;
}
} }

View File

@ -1,30 +1,32 @@
import { hexToRgb } from './functions'; import { hexToRgb } from './functions';
import { environment } from '../../environments/environment'; import { environment } from '../../environments/environment';
import { PDFNet } from '@pdftron/webviewer';
import PDFDoc = PDFNet.PDFDoc;
export async function stampPDFPage( export async function stampPDFPage(
document: any, document: PDFDoc,
PDFNet: any, PdfNet: any,
text: string, text: string,
fontSize: number, fontSize: number,
fontType: string, fontType: string,
orientation: string, orientation: 'DIAGONAL' | 'HORIZONTAL' | 'VERTICAL',
opacity: number, opacity: number,
color: string, color: string,
page: number page: number
) { ) {
await PDFNet.runWithCleanup( await PdfNet.runWithCleanup(
async () => { async () => {
await document.lock(); await document.lock();
const pageSet = await PDFNet.PageSet.createSinglePage(page); const pageSet = await PdfNet.PageSet.createSinglePage(page);
await PDFNet.Stamper.deleteStamps(document, pageSet); await PdfNet.Stamper.deleteStamps(document, pageSet);
const rgbColor = hexToRgb(color); const rgbColor = hexToRgb(color);
const stamper = await PDFNet.Stamper.create(3, fontSize, 0); const stamper = await PdfNet.Stamper.create(3, fontSize, 0);
await stamper.setFontColor( await stamper.setFontColor(
await PDFNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255) await PdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
); );
await stamper.setOpacity(opacity / 100); await stamper.setOpacity(opacity / 100);
@ -41,7 +43,7 @@ export async function stampPDFPage(
await stamper.setRotation(-45); await stamper.setRotation(-45);
} }
const font = await PDFNet.Font.createAndEmbed(document, convertFont(fontType)); const font = await PdfNet.Font.createAndEmbed(document, convertFont(fontType));
await stamper.setFont(font); await stamper.setFont(font);
await stamper.setTextAlignment(0); await stamper.setTextAlignment(0);
await stamper.stampText(document, text, pageSet); await stamper.stampText(document, text, pageSet);

View File

@ -923,6 +923,7 @@
"document-info": "Your Document Info lives here. This includes metadata required on each document.", "document-info": "Your Document Info lives here. This includes metadata required on each document.",
"download-original-file": "Download Original File", "download-original-file": "Download Original File",
"exclude-pages": "Exclude pages from redaction", "exclude-pages": "Exclude pages from redaction",
"excluded-from-redaction": "excluded from redaction",
"fullscreen": "Full Screen (F)", "fullscreen": "Full Screen (F)",
"html-debug": "Open Document HTML Debug", "html-debug": "Open Document HTML Debug",
"last-reviewer": "Last Reviewed by:", "last-reviewer": "Last Reviewed by:",
@ -948,7 +949,8 @@
"label": "Workload", "label": "Workload",
"select": "Select", "select": "Select",
"select-all": "All", "select-all": "All",
"select-none": "None" "select-none": "None",
"page-is": "This page is"
}, },
"document-info": { "document-info": {
"close": "Close Document Info", "close": "Close Document Info",