RED-6012 - WIP on Watermark alignment settings

This commit is contained in:
Valentin Mihai 2023-04-25 20:05:59 +03:00
parent 4e221d5e00
commit bd880dac26
22 changed files with 242 additions and 8 deletions

View File

@ -51,6 +51,26 @@
</div>
</div>
<div class="iqser-input-group">
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label"></label>
<div class="flex">
<iqser-circle-button
*ngFor="let alignment of watermarkHorizontalAlignments"
[tooltip]="translations[alignment] | translate"
[icon]="'red:' + alignment"
[type]="currentAlignment.horizontal === alignment ? iconButtonTypes.primary : iconButtonTypes.default"
(action)="alignHorizontally(alignment)"
></iqser-circle-button>
<iqser-circle-button
*ngFor="let alignment of watermarkVerticalAlignments"
[tooltip]="translations[alignment] | translate"
[icon]="'red:' + alignment"
[type]="currentAlignment.vertical === alignment ? iconButtonTypes.primary : iconButtonTypes.default"
(action)="alignVertically(alignment)"
></iqser-circle-button>
</div>
</div>
<div class="iqser-input-group">
<label [translate]="'watermark-screen.form.font-size'" class="all-caps-label"></label>
<mat-slider (change)="configChanged()" color="primary" formControlName="fontSize" max="50" min="5"></mat-slider>

View File

@ -14,8 +14,21 @@ import {
LoadingService,
TenantContextHolder,
Toaster,
trackByFactory,
} from '@iqser/common-ui';
import { DOSSIER_TEMPLATE_ID, type IWatermark, type User, WATERMARK_ID, WatermarkOrientation, WatermarkOrientations } from '@red/domain';
import {
DOSSIER_TEMPLATE_ID,
type IWatermark,
type User,
WATERMARK_HORIZONTAL_ALIGNMENTS,
WATERMARK_ID,
WATERMARK_VERTICAL_ALIGNMENTS,
WatermarkAlignment,
WatermarkHorizontalAlignment,
WatermarkOrientation,
WatermarkOrientations,
WatermarkVerticalAlignment,
} from '@red/domain';
import { stampPDFPage } from '@utils/page-stamper';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { WatermarkService } from '@services/entity-services/watermark.service';
@ -27,6 +40,7 @@ import { WatermarksMapService } from '@services/entity-services/watermarks-map.s
import { ROLES } from '@users/roles';
import { environment } from '@environments/environment';
import { tap } from 'rxjs/operators';
import { watermarkTranslations } from '@translations/watermark-translations';
export const DEFAULT_WATERMARK: Partial<IWatermark> = {
text: 'Watermark',
@ -36,6 +50,10 @@ export const DEFAULT_WATERMARK: Partial<IWatermark> = {
fontSize: 40,
fontType: 'helvetica',
orientation: WatermarkOrientations.HORIZONTAL,
alignment: {
horizontal: WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_HORIZONTAL_CENTERS,
vertical: WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_VERTICAL_CENTERS,
},
} as const;
interface WatermarkForm {
@ -45,6 +63,7 @@ interface WatermarkForm {
opacity: number;
fontSize: number;
fontType: string;
alignment: WatermarkAlignment;
orientation: WatermarkOrientation;
}
@ -54,6 +73,8 @@ interface WatermarkForm {
})
export class WatermarkScreenComponent implements OnInit {
readonly iconButtonTypes = IconButtonTypes;
readonly translations = watermarkTranslations;
readonly trackBy = trackByFactory();
readonly currentUser = getCurrentUser<User>();
readonly form = this.#form;
readonly watermark$: Observable<Partial<IWatermark>>;
@ -69,6 +90,12 @@ export class WatermarkScreenComponent implements OnInit {
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
readonly #watermarkId = Number(getParam(WATERMARK_ID));
#watermark: Partial<IWatermark> = {};
readonly watermarkHorizontalAlignments = Object.values(WATERMARK_HORIZONTAL_ALIGNMENTS);
readonly watermarkVerticalAlignments = Object.values(WATERMARK_VERTICAL_ALIGNMENTS);
currentAlignment: WatermarkAlignment = {
horizontal: WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_HORIZONTAL_CENTERS,
vertical: WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_VERTICAL_CENTERS,
};
constructor(
private readonly _http: HttpClient,
@ -114,6 +141,7 @@ export class WatermarkScreenComponent implements OnInit {
fontSize: [null],
fontType: [null],
orientation: [null],
alignment: [null],
});
if (!this.currentUser.isAdmin || !this.permissionsService.has(ROLES.watermarks.write)) {
@ -177,6 +205,26 @@ export class WatermarkScreenComponent implements OnInit {
this.instance.Core.documentViewer.displayPageLocation($event, 0, 0);
}
async alignHorizontally(alignment: WatermarkHorizontalAlignment) {
const formValue = this.form.controls['alignment'].value;
this.form.controls['alignment'].setValue({
...formValue,
horizontal: alignment,
});
this.currentAlignment.horizontal = alignment;
await this.configChanged();
}
async alignVertically(alignment: WatermarkVerticalAlignment) {
const formValue = this.form.controls['alignment'].value;
this.form.controls['alignment'].setValue({
...formValue,
vertical: alignment,
});
this.currentAlignment.vertical = alignment;
await this.configChanged();
}
async #initForm(watermark: Partial<IWatermark>) {
this.#watermark = { ...watermark, dossierTemplateId: this.#dossierTemplateId };
this.form.patchValue({ ...watermark });
@ -235,6 +283,7 @@ export class WatermarkScreenComponent implements OnInit {
this.form.controls.text.value || '',
this.form.controls.fontSize.value,
this.form.controls.fontType.value,
this.form.controls.alignment.value,
this.form.controls.orientation.value,
this.form.controls.opacity.value,
this.form.controls.hexColor.value,

View File

@ -9,6 +9,7 @@ import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.ser
import { LicenseService } from '@services/license.service';
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
import PDFNet = Core.PDFNet;
import { WATERMARK_HORIZONTAL_ALIGNMENTS, WATERMARK_VERTICAL_ALIGNMENTS } from '@red/domain';
@Injectable()
export class StampService {
@ -58,6 +59,10 @@ export class StampService {
this._translateService.instant('file-preview.excluded-from-redaction') as string,
17,
'courier',
{
horizontal: WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_HORIZONTAL_CENTERS,
vertical: WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_VERTICAL_CENTERS,
},
'TOP_LEFT',
50,
'#dd4d50',
@ -75,6 +80,10 @@ export class StampService {
watermark.text,
watermark.fontSize,
watermark.fontType,
{
horizontal: WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_HORIZONTAL_CENTERS,
vertical: WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_VERTICAL_CENTERS,
},
watermark.orientation,
watermark.opacity,
watermark.hexColor,

View File

@ -1,5 +1,6 @@
import { stampPDFPage } from '../../../utils';
import { Core } from '@pdftron/webviewer';
import { WATERMARK_HORIZONTAL_ALIGNMENTS, WATERMARK_VERTICAL_ALIGNMENTS } from '@red/domain';
export const processPage = async (
pageNumber: number,
@ -26,6 +27,10 @@ export const processPage = async (
'<< Compare Placeholder Page >>',
20,
'courier',
{
horizontal: WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_HORIZONTAL_CENTERS,
vertical: WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_VERTICAL_CENTERS,
},
'DIAGONAL',
33,
'#ffb83b',

View File

@ -13,6 +13,12 @@ export class IconsModule {
const icons = [
'ai',
'alert-circle',
'align-bottom',
'align-horizontal-centers',
'align-left',
'align-right',
'align-top',
'align-vertical-centers',
'approved',
'archive',
'arrow-up',
@ -52,6 +58,8 @@ export class IconsModule {
'needs-work',
'new-tab',
'notification',
'padding-left-right',
'padding-top-bottom',
'page',
'preview',
'put-back',

View File

@ -0,0 +1,11 @@
import { WatermarkAlignment, WatermarkHorizontalAlignment, WatermarkVerticalAlignment } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
export const watermarkTranslations: { readonly [key in WatermarkHorizontalAlignment & WatermarkVerticalAlignment]?: string } = {
'align-left': _('watermark-screen.alignment.align-left'),
'align-horizontal-centers': _('watermark-screen.alignment.align-horizontal-centers'),
'align-right': _('watermark-screen.alignment.align-right'),
'align-top': _('watermark-screen.alignment.align-top'),
'align-vertical-centers': 'watermark-screen.alignment.align-vertical-centers',
'align-bottom': _('watermark-screen.alignment.align-bottom'),
} as const;

View File

@ -1,6 +1,7 @@
import { hexToRgb } from './functions';
import { Core } from '@pdftron/webviewer';
import PDFDoc = Core.PDFNet.PDFDoc;
import { WATERMARK_HORIZONTAL_ALIGNMENTS, WATERMARK_VERTICAL_ALIGNMENTS, WatermarkAlignment } from '@red/domain';
async function createPageSet(pdfNet: typeof Core.PDFNet, pages: number[]) {
const pageSet = await pdfNet.PageSet.create();
@ -38,6 +39,7 @@ export async function stampPDFPage(
text: string,
fontSize: number,
fontType: string,
alignment: WatermarkAlignment,
orientation: 'DIAGONAL' | 'HORIZONTAL' | 'VERTICAL' | 'TOP_LEFT',
opacity: number,
color: string,
@ -56,21 +58,51 @@ export async function stampPDFPage(
await stamper.setFontColor(await pdfNet.ColorPt.init(r / 255, g / 255, b / 255));
await stamper.setOpacity(opacity / 100);
let horizontalAlignment: number;
let verticalAlignment: number;
console.log(alignment);
switch (alignment.horizontal) {
case WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_LEFT:
horizontalAlignment = -1;
break;
case WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_HORIZONTAL_CENTERS:
horizontalAlignment = 0;
break;
case WATERMARK_HORIZONTAL_ALIGNMENTS.ALIGN_RIGHT:
horizontalAlignment = 1;
break;
}
switch (alignment.vertical) {
case WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_TOP:
verticalAlignment = 1;
break;
case WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_VERTICAL_CENTERS:
verticalAlignment = 0;
break;
case WATERMARK_VERTICAL_ALIGNMENTS.ALIGN_BOTTOM:
verticalAlignment = -1;
break;
}
await stamper.setAlignment(horizontalAlignment, verticalAlignment);
switch (orientation) {
case 'VERTICAL':
await stamper.setAlignment(0, 0);
// await stamper.setAlignment(-1, 1);
await stamper.setRotation(-90);
break;
case 'HORIZONTAL':
break;
case 'TOP_LEFT':
await stamper.setAlignment(-1, 1);
// await stamper.setAlignment(-1, 1);
await stamper.setRotation(90);
await stamper.setPosition(20, 20);
break;
case 'DIAGONAL':
default:
await stamper.setAlignment(0, 0);
// await stamper.setAlignment(0, 0);
await stamper.setRotation(-45);
}
@ -78,7 +110,7 @@ export async function stampPDFPage(
// in case there are japanese characters in the text, we add them to the font
const fontWithAllTextChars = await pdfNet.Font.createFromFontDescriptor(document, initialFont, text);
await stamper.setFont(fontWithAllTextChars);
await stamper.setTextAlignment(0);
await stamper.setTextAlignment(1);
await stamper.stampText(document, text, pageSet);
}, licenseKey);
}

View File

@ -1,7 +1,7 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dom2.iqser.cloud/redaction-gateway-v1",
"API_URL": "https://dev-09.iqser.cloud/redaction-gateway-v1",
"APP_NAME": "RedactManager",
"AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40",
@ -11,7 +11,7 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://dom2.iqser.cloud/auth",
"OAUTH_URL": "https://dev-09.iqser.cloud/auth",
"RECENT_PERIOD_IN_HOURS": 24,
"SELECTION_MODE": "structural",
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",

View File

@ -2259,7 +2259,16 @@
"revert": "Rückgängig machen",
"save": "Änderungen speichern"
},
"alignment": {
"align-bottom": "",
"align-horizontal-centers": "",
"align-left": "",
"align-right": "",
"align-top": "",
"align-vertical-centers": ""
},
"form": {
"alignment": "",
"color": "Farbe",
"color-placeholder": "",
"font-size": "Schriftgröße",

View File

@ -2259,7 +2259,16 @@
"revert": "Revert",
"save": "Save Changes"
},
"alignment": {
"align-bottom": "Align bottom",
"align-horizontal-centers": "Align horizontal centers",
"align-left": "Align left",
"align-right": "Align right",
"align-top": "Align top",
"align-vertical-centers": "Align vertical centers"
},
"form": {
"alignment": "Alignment",
"color": "Color",
"color-placeholder": "#",
"font-size": "Font Size",

View File

@ -2259,7 +2259,16 @@
"revert": "Rückgängig machen",
"save": "Änderungen speichern"
},
"alignment": {
"align-bottom": "",
"align-horizontal-centers": "",
"align-left": "",
"align-right": "",
"align-top": "",
"align-vertical-centers": ""
},
"form": {
"alignment": "",
"color": "Farbe",
"color-placeholder": "",
"font-size": "Schriftgröße",

View File

@ -2259,7 +2259,16 @@
"revert": "Revert",
"save": "Save Changes"
},
"alignment": {
"align-bottom": "Align bottom",
"align-horizontal-centers": "Align horizontal centers",
"align-left": "Align left",
"align-right": "Align right",
"align-top": "Align top",
"align-vertical-centers": "Align vertical centers"
},
"form": {
"alignment": "Alignment",
"color": "Color",
"color-placeholder": "#",
"font-size": "Font Size",

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M41 0L41 80L27 80L27 -6.11959e-07L41 0Z" fill="#283241"/>
<path d="M73 25L73 80L59 80L59 25L73 25Z" fill="#283241"/>
<path d="M100 92L100 100L-3.49691e-07 100L0 92L100 92Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 27H90V41H10V27Z" fill="#283241"/>
<path d="M23 59H78V73H23V59Z" fill="#283241"/>
<path d="M46 0H54V100H46V0Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 247 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 27H100V41H20V27Z" fill="#283241"/>
<path d="M20 59H75V73H20V59Z" fill="#283241"/>
<path d="M0 0H8V100H0V0Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 245 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 27H80V41H0V27Z" fill="#283241"/>
<path d="M25 59H80V73H25V59Z" fill="#283241"/>
<path d="M92 0H100V100H92V0Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 246 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M41 20L41 100L27 100L27 20L41 20Z" fill="#283241"/>
<path d="M73 20L73 75L59 75L59 20L73 20Z" fill="#283241"/>
<path d="M100 0L100 8L-3.49691e-07 8L0 -4.37114e-06L100 0Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M41 10L41 90L27 90L27 10L41 10Z" fill="#283241"/>
<path d="M73 22L73 77L59 77L59 22L73 22Z" fill="#283241"/>
<path d="M100 46L100 54L-3.49691e-07 54L0 46L100 46Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 296 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 80L80 80L80 20L20 20L20 80ZM70 30L70 70L30 70L30 30L70 30Z" fill="#283241"/>
<path d="M0 0H10V100H0V0Z" fill="#283241"/>
<path d="M90 0H100V100H90V0Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 288 B

View File

@ -0,0 +1,5 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 20V80H80V20H20ZM70 70H30V30H70V70Z" fill="#283241"/>
<path d="M0 0H100V10H0V0Z" fill="#283241"/>
<path d="M0 90H100V100H0V90Z" fill="#283241"/>
</svg>

After

Width:  |  Height:  |  Size: 264 B

View File

@ -1,4 +1,4 @@
import { IWatermark, WatermarkOrientation } from './watermark';
import { IWatermark, WatermarkAlignment, WatermarkOrientation } from './watermark';
import { Entity } from '@iqser/common-ui';
export class Watermark extends Entity<IWatermark, number> {
@ -10,6 +10,7 @@ export class Watermark extends Entity<IWatermark, number> {
readonly hexColor: string;
readonly opacity: number;
readonly orientation: WatermarkOrientation;
readonly alignment: WatermarkAlignment;
readonly text: string;
readonly name: string;
readonly createdBy?: string;
@ -28,6 +29,7 @@ export class Watermark extends Entity<IWatermark, number> {
this.hexColor = watermark.hexColor;
this.opacity = watermark.opacity;
this.orientation = watermark.orientation;
this.alignment = watermark.alignment;
this.text = watermark.text;
this.name = watermark.name;
this.createdBy = watermark.createdBy;

View File

@ -7,6 +7,7 @@ export interface IWatermark {
hexColor: string;
opacity: number;
orientation: WatermarkOrientation;
alignment: WatermarkAlignment;
text: string;
name: string;
createdBy?: string;
@ -14,9 +15,30 @@ export interface IWatermark {
dateModified?: string;
}
export const WATERMARK_HORIZONTAL_ALIGNMENTS = {
ALIGN_LEFT: 'align-left',
ALIGN_HORIZONTAL_CENTERS: 'align-horizontal-centers',
ALIGN_RIGHT: 'align-right',
} as const;
export type WatermarkHorizontalAlignmentKey = keyof typeof WATERMARK_HORIZONTAL_ALIGNMENTS;
export type WatermarkHorizontalAlignment = (typeof WATERMARK_HORIZONTAL_ALIGNMENTS)[WatermarkHorizontalAlignmentKey];
export const WATERMARK_VERTICAL_ALIGNMENTS = {
ALIGN_TOP: 'align-top',
ALIGN_VERTICAL_CENTERS: 'align-vertical-centers',
ALIGN_BOTTOM: 'align-bottom',
} as const;
export type WatermarkVerticalAlignmentKey = keyof typeof WATERMARK_VERTICAL_ALIGNMENTS;
export type WatermarkVerticalAlignment = (typeof WATERMARK_VERTICAL_ALIGNMENTS)[WatermarkVerticalAlignmentKey];
export const WatermarkOrientations = {
DIAGONAL: 'DIAGONAL',
HORIZONTAL: 'HORIZONTAL',
VERTICAL: 'VERTICAL',
} as const;
export type WatermarkOrientation = keyof typeof WatermarkOrientations;
export interface WatermarkAlignment {
horizontal: WatermarkHorizontalAlignment;
vertical: WatermarkVerticalAlignment;
}