Merge branch 'VM/RED-6012'

This commit is contained in:
Valentin Mihai 2023-05-23 22:21:21 +03:00
commit f5ad51b258
23 changed files with 305 additions and 8 deletions

View File

@ -51,6 +51,38 @@
</div>
</div>
<div class="iqser-input-group">
<label [translate]="'watermark-screen.form.alignment'" class="all-caps-label mb-8"></label>
<div class="flex">
<div class="alignment-buttons">
<div
*ngFor="let alignment of watermarkHorizontalAlignments"
class="alignment"
[class.active]="currentAlignment.horizontal === alignment"
[ngClass]="'horizontal-' + alignment.toLowerCase()"
[matTooltip]="translations.HORIZONTAL[alignment] | translate"
[matTooltipPosition]="'above'"
(click)="alignHorizontally(alignment)"
>
<mat-icon [svgIcon]="'red:align-horizontal-' + alignment.toLowerCase()"></mat-icon>
</div>
</div>
<div class="alignment-buttons">
<div
*ngFor="let alignment of watermarkVerticalAlignments"
class="alignment"
[class.active]="currentAlignment.vertical === alignment"
[ngClass]="'vertical-' + alignment.toLowerCase()"
[matTooltip]="translations.VERTICAL[alignment] | translate"
[matTooltipPosition]="'above'"
(click)="alignVertically(alignment)"
>
<mat-icon [svgIcon]="'red:align-vertical-' + alignment.toLowerCase()"></mat-icon>
</div>
</div>
</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

@ -1,5 +1,3 @@
@use 'variables';
:host {
display: flex;
flex-direction: row !important;
@ -92,4 +90,47 @@
}
}
}
.alignment-buttons {
width: 99px;
height: 25px;
display: flex;
justify-content: space-between;
border-radius: 6px;
border: 1px solid var(--iqser-grey-5);
margin-right: 10px;
.alignment {
width: 33px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
mat-icon {
width: 14px;
}
&.active {
background-color: var(--iqser-primary);
cursor: default;
}
&.horizontal-left,
&.vertical-top {
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
&.horizontal-right,
&.vertical-bottom {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
}
.alignment:not(.active):hover {
background: var(--iqser-btn-bg-hover);
}
}
}

View File

@ -13,8 +13,21 @@ import {
LoadingService,
TenantsService,
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';
@ -26,6 +39,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',
@ -35,6 +49,8 @@ export const DEFAULT_WATERMARK: Partial<IWatermark> = {
fontSize: 40,
fontType: 'helvetica',
orientation: WatermarkOrientations.HORIZONTAL,
horizontalTextAlignment: WATERMARK_HORIZONTAL_ALIGNMENTS.CENTER,
verticalTextAlignment: WATERMARK_VERTICAL_ALIGNMENTS.CENTER,
} as const;
interface WatermarkForm {
@ -45,6 +61,8 @@ interface WatermarkForm {
fontSize: number;
fontType: string;
orientation: WatermarkOrientation;
horizontalTextAlignment: WatermarkHorizontalAlignment;
verticalTextAlignment: WatermarkVerticalAlignment;
}
@Component({
@ -53,6 +71,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>>;
@ -70,6 +90,9 @@ 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;
constructor(
private readonly _http: HttpClient,
@ -116,6 +139,8 @@ export class WatermarkScreenComponent implements OnInit {
fontSize: [null],
fontType: [null],
orientation: [null],
horizontalTextAlignment: [null],
verticalTextAlignment: [null],
});
if (!this.currentUser.isAdmin || !this.permissionsService.has(ROLES.watermarks.write)) {
@ -179,9 +204,25 @@ export class WatermarkScreenComponent implements OnInit {
this.instance.Core.documentViewer.displayPageLocation($event, 0, 0);
}
async alignHorizontally(alignment: WatermarkHorizontalAlignment) {
this.form.controls['horizontalTextAlignment'].setValue(alignment);
this.currentAlignment.horizontal = alignment;
await this.configChanged();
}
async alignVertically(alignment: WatermarkVerticalAlignment) {
this.form.controls['verticalTextAlignment'].setValue(alignment);
this.currentAlignment.vertical = alignment;
await this.configChanged();
}
async #initForm(watermark: Partial<IWatermark>) {
this.#watermark = { ...watermark, dossierTemplateId: this.#dossierTemplateId };
this.form.patchValue({ ...watermark });
this.currentAlignment = {
horizontal: this.#watermark.horizontalTextAlignment,
vertical: this.#watermark.verticalTextAlignment,
};
}
async #loadViewer() {
@ -238,6 +279,8 @@ export class WatermarkScreenComponent implements OnInit {
this.form.controls.fontSize.value,
this.form.controls.fontType.value,
this.form.controls.orientation.value,
this.form.controls.horizontalTextAlignment.value,
this.form.controls.verticalTextAlignment.value,
this.form.controls.opacity.value,
this.form.controls.hexColor.value,
[1, 2],

View File

@ -26,6 +26,7 @@ import { ReactiveFormsModule } from '@angular/forms';
import { MatLegacySliderModule } from '@angular/material/legacy-slider';
import { ColorPickerModule } from 'ngx-color-picker';
import { MatLegacySlideToggleModule } from '@angular/material/legacy-slide-toggle';
import { MatTooltipModule } from '@angular/material/tooltip';
const routes: IqserRoutes = [
{
@ -73,6 +74,7 @@ const routes: IqserRoutes = [
HasScrollbarDirective,
IqserAllowDirective,
TenantPipe,
MatTooltipModule,
],
})
export class WatermarkModule {}

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 {
@ -59,6 +60,8 @@ export class StampService {
17,
'courier',
'TOP_LEFT',
WATERMARK_HORIZONTAL_ALIGNMENTS.CENTER,
WATERMARK_VERTICAL_ALIGNMENTS.CENTER,
50,
'#dd4d50',
excludedPages,
@ -76,6 +79,8 @@ export class StampService {
watermark.fontSize,
watermark.fontType,
watermark.orientation,
watermark.horizontalTextAlignment,
watermark.verticalTextAlignment,
watermark.opacity,
watermark.hexColor,
Array.from({ length: await document.getPageCount() }, (_x, i) => i + 1),

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,
@ -27,6 +28,8 @@ export const processPage = async (
20,
'courier',
'DIAGONAL',
WATERMARK_HORIZONTAL_ALIGNMENTS.CENTER,
WATERMARK_VERTICAL_ALIGNMENTS.CENTER,
33,
'#ffb83b',
[await mergedDocument.getPageCount()],

View File

@ -13,6 +13,12 @@ export class IconsModule {
const icons = [
'ai',
'alert-circle',
'align-horizontal-center',
'align-horizontal-left',
'align-horizontal-right',
'align-vertical-bottom',
'align-vertical-center',
'align-vertical-top',
'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,15 @@
import { WatermarkAlignment, WatermarkHorizontalAlignment, WatermarkVerticalAlignment } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
export const watermarkTranslations: Record<string, Record<string, string>> = {
HORIZONTAL: {
LEFT: _('watermark-screen.alignment.align-left'),
CENTER: _('watermark-screen.alignment.align-horizontal-centers'),
RIGHT: _('watermark-screen.alignment.align-right'),
},
VERTICAL: {
TOP: _('watermark-screen.alignment.align-top'),
CENTER: 'watermark-screen.alignment.align-vertical-centers',
BOTTOM: _('watermark-screen.alignment.align-bottom'),
},
} as const;

View File

@ -1,6 +1,12 @@
import { hexToRgb } from './functions';
import { Core } from '@pdftron/webviewer';
import PDFDoc = Core.PDFNet.PDFDoc;
import {
WATERMARK_HORIZONTAL_ALIGNMENTS,
WATERMARK_VERTICAL_ALIGNMENTS,
WatermarkHorizontalAlignment,
WatermarkVerticalAlignment,
} from '@red/domain';
async function createPageSet(pdfNet: typeof Core.PDFNet, pages: number[]) {
const pageSet = await pdfNet.PageSet.create();
@ -39,6 +45,8 @@ export async function stampPDFPage(
fontSize: number,
fontType: string,
orientation: 'DIAGONAL' | 'HORIZONTAL' | 'VERTICAL' | 'TOP_LEFT',
horizontalTextAlignment: WatermarkHorizontalAlignment,
verticalTextAlignment: WatermarkVerticalAlignment,
opacity: number,
color: string,
pages: number[],
@ -56,21 +64,50 @@ 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;
switch (horizontalTextAlignment) {
case WATERMARK_HORIZONTAL_ALIGNMENTS.LEFT:
horizontalAlignment = -1;
break;
case WATERMARK_HORIZONTAL_ALIGNMENTS.CENTER:
horizontalAlignment = 0;
break;
case WATERMARK_HORIZONTAL_ALIGNMENTS.RIGHT:
horizontalAlignment = 1;
break;
}
switch (verticalTextAlignment) {
case WATERMARK_VERTICAL_ALIGNMENTS.TOP:
verticalAlignment = 1;
break;
case WATERMARK_VERTICAL_ALIGNMENTS.CENTER:
verticalAlignment = 0;
break;
case WATERMARK_VERTICAL_ALIGNMENTS.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 +115,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

@ -2257,7 +2257,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

@ -2257,7 +2257,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

@ -2257,7 +2257,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

@ -2257,7 +2257,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="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 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="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="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="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,12 @@
import { IWatermark, WatermarkOrientation } from './watermark';
import {
IWatermark,
WATERMARK_HORIZONTAL_ALIGNMENTS,
WATERMARK_VERTICAL_ALIGNMENTS,
WatermarkAlignment,
WatermarkHorizontalAlignment,
WatermarkOrientation,
WatermarkVerticalAlignment,
} from './watermark';
import { Entity } from '@iqser/common-ui';
export class Watermark extends Entity<IWatermark, number> {
@ -10,6 +18,8 @@ export class Watermark extends Entity<IWatermark, number> {
readonly hexColor: string;
readonly opacity: number;
readonly orientation: WatermarkOrientation;
readonly horizontalTextAlignment: WatermarkHorizontalAlignment;
readonly verticalTextAlignment: WatermarkVerticalAlignment;
readonly text: string;
readonly name: string;
readonly createdBy?: string;
@ -28,6 +38,8 @@ export class Watermark extends Entity<IWatermark, number> {
this.hexColor = watermark.hexColor;
this.opacity = watermark.opacity;
this.orientation = watermark.orientation;
this.horizontalTextAlignment = watermark.horizontalTextAlignment ?? WATERMARK_HORIZONTAL_ALIGNMENTS.CENTER;
this.verticalTextAlignment = watermark.verticalTextAlignment ?? WATERMARK_VERTICAL_ALIGNMENTS.CENTER;
this.text = watermark.text;
this.name = watermark.name;
this.createdBy = watermark.createdBy;

View File

@ -7,6 +7,8 @@ export interface IWatermark {
hexColor: string;
opacity: number;
orientation: WatermarkOrientation;
horizontalTextAlignment: WatermarkHorizontalAlignment;
verticalTextAlignment: WatermarkVerticalAlignment;
text: string;
name: string;
createdBy?: string;
@ -14,9 +16,30 @@ export interface IWatermark {
dateModified?: string;
}
export const WATERMARK_HORIZONTAL_ALIGNMENTS = {
LEFT: 'LEFT',
CENTER: 'CENTER',
RIGHT: 'RIGHT',
} as const;
export type WatermarkHorizontalAlignmentKey = keyof typeof WATERMARK_HORIZONTAL_ALIGNMENTS;
export type WatermarkHorizontalAlignment = (typeof WATERMARK_HORIZONTAL_ALIGNMENTS)[WatermarkHorizontalAlignmentKey];
export const WATERMARK_VERTICAL_ALIGNMENTS = {
TOP: 'TOP',
CENTER: 'CENTER',
BOTTOM: '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;
}