diff --git a/apps/red-ui/src/app/i18n/language.service.ts b/apps/red-ui/src/app/i18n/language.service.ts
index 906ac365b..4f4331ae7 100644
--- a/apps/red-ui/src/app/i18n/language.service.ts
+++ b/apps/red-ui/src/app/i18n/language.service.ts
@@ -25,7 +25,6 @@ export class LanguageService {
} else {
defaultLang = 'en';
}
- console.log(defaultLang);
document.documentElement.lang = defaultLang;
this._translateService.setDefaultLang(defaultLang);
this._translateService.use(defaultLang).toPromise().then();
diff --git a/apps/red-ui/src/app/models/file/annotation.permissions.ts b/apps/red-ui/src/app/models/file/annotation.permissions.ts
index 62b5645c5..41695bc5b 100644
--- a/apps/red-ui/src/app/models/file/annotation.permissions.ts
+++ b/apps/red-ui/src/app/models/file/annotation.permissions.ts
@@ -12,6 +12,7 @@ export class AnnotationPermissions {
canRejectSuggestion = true;
canForceRedaction = true;
canChangeLegalBasis = true;
+ canResizeAnnotation = true;
canRecategorizeImage = true;
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
@@ -41,6 +42,8 @@ export class AnnotationPermissions {
permissions.canRecategorizeImage = annotation.isImage;
+ permissions.canResizeAnnotation = annotation.isRedacted || annotation.isImage;
+
summedPermissions._merge(permissions);
}
return summedPermissions;
diff --git a/apps/red-ui/src/app/models/file/annotation.wrapper.ts b/apps/red-ui/src/app/models/file/annotation.wrapper.ts
index ff1c95d17..81603ade5 100644
--- a/apps/red-ui/src/app/models/file/annotation.wrapper.ts
+++ b/apps/red-ui/src/app/models/file/annotation.wrapper.ts
@@ -45,6 +45,7 @@ export class AnnotationWrapper {
recommendationType: string;
legalBasisValue: string;
legalBasisChangeValue?: string;
+ resizing?: boolean;
manual?: boolean;
diff --git a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html
index e7ca4524f..49da6275b 100644
--- a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html
+++ b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html
@@ -1,109 +1,142 @@
-
+
+
+
-
+
+
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts
index 2b86746fd..e0aa2c6ec 100644
--- a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.ts
@@ -63,6 +63,10 @@ export class AnnotationActionsComponent implements OnInit {
return this.annotations?.reduce((accumulator, annotation) => annotation.isImage && accumulator, true);
}
+ get resizing() {
+ return this.annotations?.length === 1 && this.annotations?.[0].resizing;
+ }
+
ngOnInit(): void {
this._setPermissions();
}
@@ -97,4 +101,16 @@ export class AnnotationActionsComponent implements OnInit {
this.annotations,
);
}
+
+ resize($event: MouseEvent) {
+ this.annotationActionsService.resize($event, this.viewer, this.annotations[0]);
+ }
+
+ acceptResize($event: MouseEvent) {
+ this.annotationActionsService.acceptResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
+ }
+
+ cancelResize($event: MouseEvent) {
+ this.annotationActionsService.cancelResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
+ }
}
diff --git a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts
index 14c4a721f..122980e03 100644
--- a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts
+++ b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts
@@ -40,8 +40,9 @@ export class PageExclusionComponent implements OnChanges {
}, []);
}
- async excludePagesRange(value: string): Promise {
+ async excludePagesRange(inputValue: string): Promise {
this._loadingService.start();
+ const value = inputValue.replace(/[^0-9-,]/g, '');
try {
const pageRanges = value.split(',').map(range => {
const splitted = range.split('-');
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 e460b7e02..da430c61e 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
@@ -36,6 +36,7 @@ import { ActivatedRoute } from '@angular/router';
import Tools = Core.Tools;
import TextTool = Tools.TextTool;
import Annotation = Core.Annotations.Annotation;
+import { toPosition } from '../../utils/pdf-calculation.utils';
const ALLOWED_KEYBOARD_SHORTCUTS = ['+', '-', 'p', 'r', 'Escape'] as const;
const dataElements = {
@@ -253,6 +254,8 @@ export class PdfViewerComponent implements OnInit, OnChanges {
// this will auto select rectangle after drawing
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
this.annotationManager.selectAnnotations(annotations);
+ annotations[0].setRotationControlEnabled(false);
+ console.log(annotations[0]);
}
});
@@ -456,7 +459,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
this.instance.UI.annotationPopup.add(
- this._annotationActionsService.getViewerAvailableActions(annotationWrappers, this.annotationsChanged),
+ this._annotationActionsService.getViewerAvailableActions(this.instance, annotationWrappers, this.annotationsChanged),
);
}
@@ -598,7 +601,8 @@ export class PdfViewerComponent implements OnInit, OnChanges {
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));
+ const pageHeight = this.documentViewer.getPageHeight(page);
+ entry.positions.push(toPosition(page, pageHeight, convertQuads ? this.utils.translateQuads(page, quad) : quad));
}
}
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.html b/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.html
new file mode 100644
index 000000000..72a505f70
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.html
@@ -0,0 +1,21 @@
+
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.scss b/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.ts
new file mode 100644
index 000000000..f344de1cd
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/dialogs/resize-annotation-dialog/resize-annotation-dialog.component.ts
@@ -0,0 +1,36 @@
+import { Component, OnInit } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { MatDialogRef } from '@angular/material/dialog';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { PermissionsService } from '@services/permissions.service';
+
+@Component({
+ selector: 'redaction-resize-annotation-dialog',
+ templateUrl: './resize-annotation-dialog.component.html',
+ styleUrls: ['./resize-annotation-dialog.component.scss'],
+})
+export class ResizeAnnotationDialogComponent implements OnInit {
+ resizeForm: FormGroup;
+ isDocumentAdmin: boolean;
+
+ constructor(
+ private readonly _translateService: TranslateService,
+ private readonly _permissionsService: PermissionsService,
+ private readonly _formBuilder: FormBuilder,
+ public dialogRef: MatDialogRef,
+ ) {}
+
+ async ngOnInit() {
+ this.isDocumentAdmin = this._permissionsService.isApprover();
+
+ this.resizeForm = this._formBuilder.group({
+ comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
+ });
+ }
+
+ save() {
+ this.dialogRef.close({
+ comment: this.resizeForm.get('comment').value,
+ });
+ }
+}
diff --git a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts
index 4a1007320..507ab21b6 100644
--- a/apps/red-ui/src/app/modules/dossier/dossiers.module.ts
+++ b/apps/red-ui/src/app/modules/dossier/dossiers.module.ts
@@ -40,6 +40,7 @@ import { AnnotationSourceComponent } from './components/file-workload/components
import { OverlayModule } from '@angular/cdk/overlay';
import { SharedDossiersModule } from './shared/shared-dossiers.module';
import { PlatformSearchService } from './shared/services/platform-search.service';
+import { ResizeAnnotationDialogComponent } from './dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
const screens = [FilePreviewScreenComponent, SearchScreenComponent];
@@ -49,6 +50,7 @@ const dialogs = [
ManualAnnotationDialogComponent,
ForceRedactionDialogComponent,
RemoveAnnotationsDialogComponent,
+ ResizeAnnotationDialogComponent,
DocumentInfoDialogComponent,
AssignReviewerApproverDialogComponent,
ChangeLegalBasisDialogComponent,
diff --git a/apps/red-ui/src/app/modules/dossier/models/annotation-action-mode.model.ts b/apps/red-ui/src/app/modules/dossier/models/annotation-action-mode.model.ts
index eabbda0a9..83f3965a0 100644
--- a/apps/red-ui/src/app/modules/dossier/models/annotation-action-mode.model.ts
+++ b/apps/red-ui/src/app/modules/dossier/models/annotation-action-mode.model.ts
@@ -11,4 +11,6 @@ export type AnnotationActionMode =
| 'suggest'
| 'undo'
| 'force-redaction'
- | 'request-force-redaction';
+ | 'request-force-redaction'
+ | 'resize'
+ | 'request-resize';
diff --git a/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts
index c2836a9f7..d257e5cc6 100644
--- a/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts
@@ -9,8 +9,11 @@ import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { DossiersDialogService } from './dossiers-dialog.service';
import { BASE_HREF } from '../../../tokens';
import { UserService } from '@services/user.service';
-import { Core } from '@pdftron/webviewer';
-import { IAddRedactionRequest, ILegalBasisChangeRequest } from '@red/domain';
+import { Core, WebViewerInstance } from '@pdftron/webviewer';
+import { IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain';
+import { AppStateService } from '../../../state/app-state.service';
+import { toPosition } from '../utils/pdf-calculation.utils';
+import { AnnotationDrawService } from './annotation-draw.service';
import Annotation = Core.Annotations.Annotation;
@Injectable()
@@ -18,11 +21,13 @@ export class AnnotationActionsService {
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
private readonly _ngZone: NgZone,
+ private readonly _appStateService: AppStateService,
private readonly _userService: UserService,
private readonly _permissionsService: PermissionsService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _translateService: TranslateService,
private readonly _dialogService: DossiersDialogService,
+ private readonly _annotationDrawService: AnnotationDrawService,
) {}
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter) {
@@ -123,6 +128,7 @@ export class AnnotationActionsService {
}
getViewerAvailableActions(
+ viewer: WebViewerInstance,
annotations: AnnotationWrapper[],
annotationsChanged: EventEmitter,
): Record[] {
@@ -133,6 +139,49 @@ export class AnnotationActionsService {
permissions: AnnotationPermissions.forUser(this._permissionsService.isApprover(), this._userService.currentUser, annotation),
}));
+ // you can only resize one annotation at a time
+ const canResize =
+ annotationPermissions.length === 1 &&
+ annotationPermissions.reduce((acc, next) => acc && next.permissions.canResizeAnnotation, true);
+ if (canResize) {
+ const firstAnnotation = annotations[0];
+ // if we already entered resize-mode previously
+ if (firstAnnotation.resizing) {
+ availableActions.push({
+ type: 'actionButton',
+ img: this._convertPath('/assets/icons/general/check.svg'),
+ title: this._translateService.instant('annotation-actions.resize-accept.label'),
+ onClick: () => {
+ this._ngZone.run(() => {
+ this.acceptResize(null, viewer, firstAnnotation, annotationsChanged);
+ });
+ },
+ });
+ availableActions.push({
+ type: 'actionButton',
+ img: this._convertPath('/assets/icons/general/close.svg'),
+ title: this._translateService.instant('annotation-actions.resize-cancel.label'),
+ onClick: () => {
+ this._ngZone.run(() => {
+ this.cancelResize(null, viewer, firstAnnotation, annotationsChanged);
+ });
+ },
+ });
+ return availableActions;
+ }
+
+ availableActions.push({
+ type: 'actionButton',
+ img: this._convertPath('/assets/icons/general/resize.svg'),
+ title: this._translateService.instant('annotation-actions.resize.label'),
+ onClick: () => {
+ this._ngZone.run(() => {
+ this.resize(null, viewer, annotations[0]);
+ });
+ },
+ });
+ }
+
const canChangeLegalBasis = annotationPermissions.reduce((acc, next) => acc && next.permissions.canChangeLegalBasis, true);
if (canChangeLegalBasis) {
availableActions.push({
@@ -337,4 +386,116 @@ export class AnnotationActionsService {
private _convertPath(path: string): string {
return this._baseHref + path;
}
+
+ resize($event: MouseEvent, viewer: WebViewerInstance, annotationWrapper: AnnotationWrapper) {
+ $event?.stopPropagation();
+
+ annotationWrapper.resizing = true;
+
+ const annotationManager = viewer.Core.annotationManager;
+ const viewerAnnotation = annotationManager.getAnnotationById(annotationWrapper.id);
+ viewerAnnotation.ReadOnly = false;
+ viewerAnnotation.setRotationControlEnabled(false);
+ annotationManager.redrawAnnotation(viewerAnnotation);
+ annotationManager.selectAnnotation(viewerAnnotation);
+
+ // console.log(viewerAnnotation);
+ }
+
+ private async _extractTextAndPositions(viewer: WebViewerInstance, annotationId: string) {
+ const viewerAnnotation = viewer.Core.annotationManager.getAnnotationById(annotationId);
+
+ const document = await viewer.Core.documentViewer.getDocument().getPDFDoc();
+ const page = await document.getPage(viewerAnnotation.getPageNumber());
+ let quads = (viewerAnnotation).Quads;
+ if (!quads) {
+ quads = [this._annotationDrawService.annotationToQuads(viewerAnnotation, viewer)];
+ const rect = toPosition(viewerAnnotation.getPageNumber(), await page.getPageHeight(), quads[0]);
+ return {
+ positions: [rect],
+ text: null,
+ };
+ }
+
+ const words = [];
+ const rectangles: IRectangle[] = [];
+ for (const quad of quads) {
+ const rect = toPosition(viewerAnnotation.getPageNumber(), await page.getPageHeight(), quad);
+ rectangles.push(rect);
+ const pdfNetRect = new viewer.Core.PDFNet.Rect(
+ rect.topLeft.x,
+ rect.topLeft.y,
+ rect.topLeft.x + rect.width,
+ rect.topLeft.y + rect.height,
+ );
+ const quadWords = await this._extractTextFromRect(viewer, page, pdfNetRect);
+ words.push(...quadWords);
+ }
+
+ return {
+ text: words.join(' '),
+ positions: rectangles,
+ };
+ }
+
+ private async _extractTextFromRect(viewer: WebViewerInstance, page: Core.PDFNet.Page, rect: Core.PDFNet.Rect) {
+ const txt = await viewer.Core.PDFNet.TextExtractor.create();
+ txt.begin(page, rect); // Read the page.
+
+ const words = [];
+ // Extract words one by one.
+ let line = await txt.getFirstLine();
+ for (; await line.isValid(); line = await line.getNextLine()) {
+ for (let word = await line.getFirstWord(); await word.isValid(); word = await word.getNextWord()) {
+ words.push(await word.getString());
+ }
+ }
+ return words;
+ }
+
+ acceptResize(
+ $event: MouseEvent,
+ viewer: WebViewerInstance,
+ annotationWrapper: AnnotationWrapper,
+ annotationsChanged: EventEmitter,
+ ) {
+ this._dialogService.openDialog('resizeAnnotation', $event, null, async (result: { comment: string }) => {
+ const textAndPositions = await this._extractTextAndPositions(viewer, annotationWrapper.id);
+ const text =
+ annotationWrapper.value === 'Rectangle' ? 'Rectangle' : annotationWrapper.isImage ? 'Image' : textAndPositions.text;
+
+ const resizeRequest: IResizeRequest = {
+ annotationId: annotationWrapper.id,
+ comment: result.comment,
+ positions: textAndPositions.positions,
+ value: text,
+ };
+
+ console.log(resizeRequest);
+
+ this._processObsAndEmit(
+ this._manualAnnotationService.resizeOrSuggestToResize(annotationWrapper, resizeRequest),
+ annotationWrapper,
+ annotationsChanged,
+ );
+ });
+ }
+
+ cancelResize(
+ $event: MouseEvent,
+ viewer: WebViewerInstance,
+ annotationWrapper: AnnotationWrapper,
+ annotationsChanged: EventEmitter,
+ ) {
+ $event?.stopPropagation();
+
+ annotationWrapper.resizing = false;
+
+ const annotationManager = viewer.Core.annotationManager;
+ const viewerAnnotation = annotationManager.getAnnotationById(annotationWrapper.id);
+ viewerAnnotation.ReadOnly = false;
+ annotationManager.redrawAnnotation(viewerAnnotation);
+ annotationManager.deselectAllAnnotations();
+ annotationsChanged.emit(annotationWrapper);
+ }
}
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 f87dda6db..836b73c67 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
@@ -141,32 +141,50 @@ export class AnnotationDrawService {
compareMode = false,
) {
const pageNumber = compareMode ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
- const highlight = new activeViewer.Core.Annotations.TextHighlightAnnotation();
- highlight.PageNumber = pageNumber;
- highlight.StrokeColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
- highlight.setContents(annotationWrapper.content);
- highlight.Quads = this._rectanglesToQuads(annotationWrapper.positions, activeViewer, pageNumber);
- highlight.Id = annotationWrapper.id;
- highlight.ReadOnly = true;
+
+ let annotation;
+ if (annotationWrapper.value === 'Rectangle' || annotationWrapper.isImage) {
+ annotation = new activeViewer.Core.Annotations.RectangleAnnotation();
+ const pageHeight = activeViewer.Core.documentViewer.getPageHeight(pageNumber);
+ const firstPosition = annotationWrapper.positions[0];
+ annotation.X = firstPosition.topLeft.x;
+ annotation.Y = pageHeight - (firstPosition.topLeft.y + firstPosition.height);
+ annotation.Width = firstPosition.width;
+ annotation.FillColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
+ annotation.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 0.6;
+ annotation.Height = firstPosition.height;
+ annotation.Intensity = 100;
+ } else {
+ annotation = new activeViewer.Core.Annotations.TextHighlightAnnotation();
+ annotation.Quads = this._rectanglesToQuads(annotationWrapper.positions, activeViewer, pageNumber);
+ annotation.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 1;
+ }
+
+ annotation.setContents(annotationWrapper.content);
+
+ annotation.PageNumber = pageNumber;
+ annotation.StrokeColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
+ annotation.Id = annotationWrapper.id;
+ annotation.ReadOnly = true;
// change log entries are drawn lighter
- highlight.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 1;
- highlight.Hidden =
+
+ annotation.Hidden =
annotationWrapper.isChangeLogRemoved ||
(hideSkipped && annotationWrapper.isSkipped) ||
annotationWrapper.isOCR ||
annotationWrapper.hidden;
- highlight.setCustomData('redacto-manager', 'true');
- highlight.setCustomData('redaction', String(annotationWrapper.isRedacted));
- highlight.setCustomData('skipped', String(annotationWrapper.isSkipped));
- highlight.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
- highlight.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
- highlight.setCustomData('redactionColor', String(this.getColor(activeViewer, 'redaction', 'redaction')));
- highlight.setCustomData(
+ annotation.setCustomData('redacto-manager', 'true');
+ annotation.setCustomData('redaction', String(annotationWrapper.isRedacted));
+ annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
+ annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
+ annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
+ annotation.setCustomData('redactionColor', String(this.getColor(activeViewer, 'redaction', 'redaction')));
+ annotation.setCustomData(
'annotationColor',
String(this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type)),
);
- return highlight;
+ return annotation;
}
private _rectanglesToQuads(positions: IRectangle[], activeViewer: WebViewerInstance, pageNumber: number): any[] {
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 331349a4c..4e14a62ba 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
@@ -10,6 +10,7 @@ import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewe
import { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { RecategorizeImageDialogComponent } from '../dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
import { ConfirmationDialogComponent, DialogConfig, DialogService, largeDialogConfig } from '@iqser/common-ui';
+import { ResizeAnnotationDialogComponent } from '../dialogs/resize-annotation-dialog/resize-annotation-dialog.component';
type DialogType =
| 'confirm'
@@ -20,6 +21,7 @@ type DialogType =
| 'recategorizeImage'
| 'changeLegalBasis'
| 'removeAnnotations'
+ | 'resizeAnnotation'
| 'forceRedaction'
| 'manualAnnotation';
@@ -53,6 +55,9 @@ export class DossiersDialogService extends DialogService {
removeAnnotations: {
component: RemoveAnnotationsDialogComponent,
},
+ resizeAnnotation: {
+ component: ResizeAnnotationDialogComponent,
+ },
forceRedaction: {
component: ForceRedactionDialogComponent,
},
diff --git a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts
index 991478045..91bdd9a49 100644
--- a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts
@@ -7,6 +7,7 @@ import {
ILegalBasisChangeRequest,
IManualAddResponse,
IRemoveRedactionRequest,
+ IResizeRequest,
} from '@red/domain';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { CONFLICT_ERROR_CODE, ErrorMessageService, GenericService, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
@@ -51,6 +52,8 @@ export class ManualAnnotationService extends GenericService
suggest: 'requestAddRedaction',
'force-redaction': 'forceRedaction',
'request-force-redaction': 'requestForceRedaction',
+ resize: 'resize',
+ 'request-resize': 'requestResize',
};
}
@@ -162,6 +165,14 @@ export class ManualAnnotationService extends GenericService
return this._makeRequest(mode, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
}
+ // this wraps
+ // /manualRedaction/redaction/resize/
+ // /manualRedaction/request/resize/
+ resizeOrSuggestToResize(annotationWrapper: AnnotationWrapper, resizeRequest: IResizeRequest) {
+ const mode: AnnotationActionMode = this._permissionsService.isApprover() ? 'resize' : 'request-resize';
+ return this._makeRequest(mode, resizeRequest);
+ }
+
// this wraps
// /manualRedaction/redaction/remove/
// /manualRedaction/request/remove/
@@ -322,6 +333,18 @@ export class ManualAnnotationService extends GenericService
return this._post(body, url);
}
+ @Validate()
+ resize(@RequiredParam() body: IResizeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
+ const url = `${this._defaultModelPath}/redaction/resize/${dossierId}/${fileId}`;
+ return this._post(body, url);
+ }
+
+ @Validate()
+ requestResize(@RequiredParam() body: IResizeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
+ const url = `${this._defaultModelPath}/request/resize/${dossierId}/${fileId}`;
+ return this._post(body, url);
+ }
+
private _getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) {
const type = modifyDictionary ? 'dictionary' : 'manual-redaction';
const resultType = error ? (isConflict ? 'conflictError' : 'error') : 'success';
diff --git a/apps/red-ui/src/app/modules/dossier/utils/pdf-calculation.utils.ts b/apps/red-ui/src/app/modules/dossier/utils/pdf-calculation.utils.ts
new file mode 100644
index 000000000..7d53d0a12
--- /dev/null
+++ b/apps/red-ui/src/app/modules/dossier/utils/pdf-calculation.utils.ts
@@ -0,0 +1,18 @@
+import { IRectangle } from '@red/domain';
+
+export const toPosition = (
+ page: number,
+ pageHeight: number,
+ selectedQuad: { x1: number; x2: number; x3: number; x4: number; y4: number; y2: number },
+): IRectangle => {
+ const height = selectedQuad.y2 - selectedQuad.y4;
+ return {
+ page: page,
+ topLeft: {
+ x: Math.min(selectedQuad.x3, selectedQuad.x4, selectedQuad.x2, selectedQuad.x1),
+ y: pageHeight - (selectedQuad.y4 + height),
+ },
+ height: height,
+ width: Math.max(4, Math.abs(selectedQuad.x3 - selectedQuad.x4), Math.abs(selectedQuad.x3 - selectedQuad.x1)),
+ };
+};
diff --git a/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts b/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts
index 224d58bf2..1146630af 100644
--- a/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts
+++ b/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts
@@ -1,4 +1,4 @@
-import { IRectangle, ViewMode } from '@red/domain';
+import { ViewMode } from '@red/domain';
import { translateQuads } from '@utils/pdf-coordinates';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core, WebViewerInstance } from '@pdftron/webviewer';
@@ -120,20 +120,6 @@ export class PdfViewerUtils {
return translateQuads(page, rotation, quads);
}
- toPosition(page: number, selectedQuad: { x1: number; x2: number; x3: number; x4: number; y4: number; y2: number }): IRectangle {
- const pageHeight = this._documentViewer.getPageHeight(page);
- const height = selectedQuad.y2 - selectedQuad.y4;
- return {
- page: page,
- topLeft: {
- x: Math.min(selectedQuad.x3, selectedQuad.x4, selectedQuad.x2, selectedQuad.x1),
- y: pageHeight - (selectedQuad.y4 + height),
- },
- height: height,
- width: Math.max(4, Math.abs(selectedQuad.x3 - selectedQuad.x4), Math.abs(selectedQuad.x3 - selectedQuad.x1)),
- };
- }
-
deselectAllAnnotations() {
this._annotationManager.deselectAllAnnotations();
}
diff --git a/apps/red-ui/src/app/modules/icons/icons.module.ts b/apps/red-ui/src/app/modules/icons/icons.module.ts
index 4b98ecc4e..94a522966 100644
--- a/apps/red-ui/src/app/modules/icons/icons.module.ts
+++ b/apps/red-ui/src/app/modules/icons/icons.module.ts
@@ -52,6 +52,7 @@ export class IconsModule {
'reason',
'remove-from-dict',
'report',
+ 'resize',
'rule',
'secret',
'status',
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index b86ac2971..d5758f534 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -138,6 +138,15 @@
},
"annotation": "Annotation",
"annotation-actions": {
+ "resize": {
+ "label": "Resize"
+ },
+ "resize-accept": {
+ "label": "Save Resize"
+ },
+ "resize-cancel": {
+ "label": "Abort Resize"
+ },
"accept-recommendation": {
"label": "Accept Recommendation"
},
@@ -341,6 +350,16 @@
"logout": "Logout"
},
"by": "by",
+ "resize-annotation-dialog": {
+ "actions": {
+ "cancel": "Cancel",
+ "save": "Save Changes"
+ },
+ "content": {
+ "comment": "Comment"
+ },
+ "header": "Resize Redaction"
+ },
"change-legal-basis-dialog": {
"actions": {
"cancel": "Cancel",
diff --git a/apps/red-ui/src/assets/icons/general/resize.svg b/apps/red-ui/src/assets/icons/general/resize.svg
new file mode 100644
index 000000000..6f425dfc7
--- /dev/null
+++ b/apps/red-ui/src/assets/icons/general/resize.svg
@@ -0,0 +1,27 @@
+
+
diff --git a/libs/red-domain/src/lib/redaction-log/index.ts b/libs/red-domain/src/lib/redaction-log/index.ts
index eef5f3805..e2abbc277 100644
--- a/libs/red-domain/src/lib/redaction-log/index.ts
+++ b/libs/red-domain/src/lib/redaction-log/index.ts
@@ -9,3 +9,4 @@ export * from './remove-redaction.request';
export * from './manual-add.response';
export * from './approve-request';
export * from './image-recategorization.request';
+export * from './resize.request';
diff --git a/libs/red-domain/src/lib/redaction-log/resize.request.ts b/libs/red-domain/src/lib/redaction-log/resize.request.ts
new file mode 100644
index 000000000..faf9331f8
--- /dev/null
+++ b/libs/red-domain/src/lib/redaction-log/resize.request.ts
@@ -0,0 +1,8 @@
+import { IRectangle } from '../geometry';
+
+export interface IResizeRequest {
+ annotationId: string;
+ comment: string;
+ positions: IRectangle[];
+ value: string;
+}