RED-6014: show japanese characters in watermark text
This commit is contained in:
parent
ff32854e09
commit
528647146c
@ -1,8 +1,8 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { BaseFormComponent, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { AsControl, BaseFormComponent, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { ROLES } from '@users/roles';
|
||||
|
||||
interface PreferencesForm {
|
||||
@ -16,7 +16,6 @@ interface PreferencesForm {
|
||||
[k: string]: any;
|
||||
}
|
||||
|
||||
type AsControl<T> = { [K in keyof T]: FormControl<T[K]> };
|
||||
type Screen = 'preferences' | 'warnings-preferences';
|
||||
|
||||
const Screens = {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<div class="content-container">
|
||||
<div #viewer class="viewer"></div>
|
||||
<div class="viewer" id="viewer"></div>
|
||||
|
||||
<div *ngIf="changed && currentUser.isAdmin" class="changes-box">
|
||||
<iqser-icon-button
|
||||
(action)="save()"
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, Inject, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, Inject } from '@angular/core';
|
||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import {
|
||||
AsControl,
|
||||
BASE_HREF_FN,
|
||||
BaseHrefFn,
|
||||
Debounce,
|
||||
@ -35,6 +36,16 @@ export const DEFAULT_WATERMARK: Partial<IWatermark> = {
|
||||
orientation: WatermarkOrientations.HORIZONTAL,
|
||||
} as const;
|
||||
|
||||
interface WatermarkForm {
|
||||
name: string;
|
||||
text: string;
|
||||
hexColor: string;
|
||||
opacity: number;
|
||||
fontSize: number;
|
||||
fontType: string;
|
||||
orientation: WatermarkOrientation;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-watermark-screen',
|
||||
templateUrl: './watermark-screen.component.html',
|
||||
@ -49,13 +60,11 @@ export class WatermarkScreenComponent {
|
||||
readonly #watermarkId = Number(getParam(WATERMARK_ID));
|
||||
private _instance: WebViewerInstance;
|
||||
private _watermark: Partial<IWatermark> = {};
|
||||
@ViewChild('viewer', { static: true })
|
||||
private _viewer: ElementRef;
|
||||
|
||||
constructor(
|
||||
private readonly _http: HttpClient,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _formBuilder: UntypedFormBuilder,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
readonly permissionsService: IqserPermissionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _licenseService: LicenseService,
|
||||
@ -82,7 +91,7 @@ export class WatermarkScreenComponent {
|
||||
}
|
||||
|
||||
get valid(): boolean {
|
||||
if (!this.form.get('name')?.value || !this.form.get('text')?.value) {
|
||||
if (!this.form.controls.name?.value || !this.form.controls.text?.value) {
|
||||
return false;
|
||||
}
|
||||
return this.form.valid;
|
||||
@ -102,22 +111,13 @@ export class WatermarkScreenComponent {
|
||||
};
|
||||
this._loadingService.start();
|
||||
try {
|
||||
await firstValueFrom(
|
||||
this._watermarkService.saveWatermark(watermark).pipe(
|
||||
tap(() => {
|
||||
this._toaster.success(
|
||||
watermark.id ? _('watermark-screen.action.change-success') : _('watermark-screen.action.created-success'),
|
||||
);
|
||||
}),
|
||||
tap(async updatedWatermark => {
|
||||
if (!watermark.id) {
|
||||
await this._router.navigate([
|
||||
`/main/admin/dossier-templates/${this.#dossierTemplateId}/watermarks/${updatedWatermark.id}`,
|
||||
]);
|
||||
}
|
||||
}),
|
||||
),
|
||||
const updatedWatermark = await this._watermarkService.saveWatermark(watermark);
|
||||
this._toaster.success(
|
||||
watermark.id ? _('watermark-screen.action.change-success') : _('watermark-screen.action.created-success'),
|
||||
);
|
||||
if (!watermark.id) {
|
||||
await this._router.navigate([`/main/admin/dossier-templates/${this.#dossierTemplateId}/watermarks/${updatedWatermark.id}`]);
|
||||
}
|
||||
} catch (error) {
|
||||
this._toaster.error(_('watermark-screen.action.error'));
|
||||
}
|
||||
@ -136,46 +136,44 @@ export class WatermarkScreenComponent {
|
||||
}
|
||||
}
|
||||
|
||||
private _initForm(watermark: Partial<IWatermark>) {
|
||||
private async _initForm(watermark: Partial<IWatermark>) {
|
||||
this._watermark = { ...watermark, dossierTemplateId: this.#dossierTemplateId };
|
||||
this.form.patchValue({ ...watermark });
|
||||
this._loadViewer();
|
||||
await this._loadViewer();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
private _loadViewer() {
|
||||
if (!this._instance) {
|
||||
WebViewer(
|
||||
{
|
||||
licenseKey: this._licenseService.activeLicenseKey,
|
||||
path: this._convertPath('/assets/wv-resources'),
|
||||
css: this._convertPath('/assets/pdftron/stylesheet.css'),
|
||||
fullAPI: true,
|
||||
isReadOnly: true,
|
||||
backendType: 'ems',
|
||||
},
|
||||
this._viewer.nativeElement as HTMLElement,
|
||||
).then(instance => {
|
||||
this._instance = instance;
|
||||
|
||||
instance.UI.setTheme(this._userPreferenceService.getTheme());
|
||||
|
||||
instance.Core.documentViewer.on('documentLoaded', async () => {
|
||||
this._loadingService.stop();
|
||||
await this._drawWatermark();
|
||||
});
|
||||
|
||||
this._disableElements();
|
||||
|
||||
return this._http
|
||||
.request('get', '/assets/pdftron/blank.pdf', {
|
||||
responseType: 'blob',
|
||||
})
|
||||
.subscribe(blobData => {
|
||||
this._instance.UI.loadDocument(blobData, { filename: 'blank.pdf' });
|
||||
});
|
||||
});
|
||||
private async _loadViewer() {
|
||||
if (this._instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._instance = await WebViewer(
|
||||
{
|
||||
licenseKey: this._licenseService.activeLicenseKey,
|
||||
path: this._convertPath('/assets/wv-resources'),
|
||||
css: this._convertPath('/assets/pdftron/stylesheet.css'),
|
||||
fullAPI: true,
|
||||
isReadOnly: true,
|
||||
backendType: 'ems',
|
||||
},
|
||||
document.getElementById('viewer'),
|
||||
);
|
||||
|
||||
this._instance.UI.setTheme(this._userPreferenceService.getTheme());
|
||||
|
||||
this._instance.Core.documentViewer.addEventListener('documentLoaded', async () => {
|
||||
this._loadingService.stop();
|
||||
await this._drawWatermark();
|
||||
});
|
||||
|
||||
this._disableElements();
|
||||
|
||||
const request = this._http.get('/assets/pdftron/blank.pdf', {
|
||||
responseType: 'blob',
|
||||
});
|
||||
const blobData = await firstValueFrom(request);
|
||||
this._instance.UI.loadDocument(blobData, { filename: 'blank.pdf' });
|
||||
}
|
||||
|
||||
private _disableElements() {
|
||||
@ -186,22 +184,15 @@ export class WatermarkScreenComponent {
|
||||
const pdfNet = this._instance.Core.PDFNet;
|
||||
const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc();
|
||||
|
||||
const text: string = this.form.get('text').value || '';
|
||||
const fontSize: number = this.form.get('fontSize').value;
|
||||
const fontType: string = this.form.get('fontType').value;
|
||||
const orientation: WatermarkOrientation = this.form.get('orientation').value;
|
||||
const opacity: number = this.form.get('opacity').value;
|
||||
const color: string = this.form.get('hexColor').value;
|
||||
|
||||
await stampPDFPage(
|
||||
document,
|
||||
pdfNet,
|
||||
text,
|
||||
fontSize,
|
||||
fontType,
|
||||
orientation,
|
||||
opacity,
|
||||
color,
|
||||
this.form.controls.text.value || '',
|
||||
this.form.controls.fontSize.value,
|
||||
this.form.controls.fontType.value,
|
||||
this.form.controls.orientation.value,
|
||||
this.form.controls.opacity.value,
|
||||
this.form.controls.hexColor.value,
|
||||
[1],
|
||||
this._licenseService.activeLicenseKey,
|
||||
);
|
||||
@ -210,8 +201,8 @@ export class WatermarkScreenComponent {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
private _getForm(): UntypedFormGroup {
|
||||
const form = this._formBuilder.group({
|
||||
private _getForm() {
|
||||
const form: FormGroup<AsControl<WatermarkForm>> = this._formBuilder.group({
|
||||
name: [null],
|
||||
text: [null],
|
||||
hexColor: [null],
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
ConfirmationDialogInput,
|
||||
@ -14,7 +14,6 @@ import {
|
||||
} from '@iqser/common-ui';
|
||||
import { DOSSIER_TEMPLATE_ID, User, Watermark } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
|
||||
@ -23,7 +22,6 @@ import { ROLES } from '@users/roles';
|
||||
@Component({
|
||||
templateUrl: './watermarks-listing-screen.component.html',
|
||||
styleUrls: ['./watermarks-listing-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: listingProvidersFactory(WatermarksListingScreenComponent),
|
||||
})
|
||||
export class WatermarksListingScreenComponent extends ListingComponent<Watermark> {
|
||||
@ -53,8 +51,8 @@ export class WatermarksListingScreenComponent extends ListingComponent<Watermark
|
||||
this.entitiesService.setEntities(this._watermarksMapService.get(this.#dossierTemplateId));
|
||||
}
|
||||
|
||||
async openConfirmDeleteWatermarkDialog($event: MouseEvent, watermark: Watermark): Promise<void> {
|
||||
const isUsed = await firstValueFrom(this._watermarkService.isWatermarkUsed(watermark.id));
|
||||
async openConfirmDeleteWatermarkDialog($event: MouseEvent, watermark: Watermark) {
|
||||
const isUsed = await this._watermarkService.isWatermarkUsed(watermark.id);
|
||||
|
||||
const data = new ConfirmationDialogInput({
|
||||
question: isUsed ? _('watermarks-listing.watermark-is-used') : null,
|
||||
@ -66,7 +64,7 @@ export class WatermarksListingScreenComponent extends ListingComponent<Watermark
|
||||
|
||||
async toggleStatus(watermark: Watermark): Promise<void> {
|
||||
this._loadingService.start();
|
||||
const updatedWatermark = await firstValueFrom(this._watermarkService.saveWatermark({ ...watermark, enabled: !watermark.enabled }));
|
||||
const updatedWatermark = await this._watermarkService.saveWatermark({ ...watermark, enabled: !watermark.enabled });
|
||||
this.entitiesService.replace(updatedWatermark);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
@ -77,7 +75,7 @@ export class WatermarksListingScreenComponent extends ListingComponent<Watermark
|
||||
|
||||
private async _deleteWatermark(watermark: Watermark): Promise<void> {
|
||||
this._loadingService.start();
|
||||
await firstValueFrom(this._watermarkService.deleteWatermark(this.#dossierTemplateId, watermark.id));
|
||||
await this._watermarkService.deleteWatermark(this.#dossierTemplateId, watermark.id);
|
||||
this.entitiesService.setEntities(this._watermarksMapService.get(this.#dossierTemplateId));
|
||||
this._toaster.success(_('watermarks-listing.action.delete-success'));
|
||||
this._loadingService.stop();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { GenericService, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { IWatermark, Watermark } from '@red/domain';
|
||||
import { forkJoin, Observable } from 'rxjs';
|
||||
import { map, switchMap, tap } from 'rxjs/operators';
|
||||
import { firstValueFrom, forkJoin, Observable } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { WatermarksMapService } from '@services/entity-services/watermarks-map.service';
|
||||
|
||||
interface IsUsedResponse {
|
||||
@ -14,27 +14,19 @@ interface IsUsedResponse {
|
||||
})
|
||||
export class WatermarkService extends GenericService<IWatermark> {
|
||||
protected readonly _defaultModelPath = 'watermark';
|
||||
readonly #watermarksMapService = inject(WatermarksMapService);
|
||||
|
||||
constructor(private readonly _watermarksMapService: WatermarksMapService) {
|
||||
super();
|
||||
@Validate()
|
||||
async deleteWatermark(@RequiredParam() dossierTemplateId: string, @RequiredParam() watermarkId: number) {
|
||||
await firstValueFrom(super.delete(null, `${this._defaultModelPath}/${watermarkId}`));
|
||||
return firstValueFrom(this.loadForDossierTemplate(dossierTemplateId));
|
||||
}
|
||||
|
||||
@Validate()
|
||||
deleteWatermark(@RequiredParam() dossierTemplateId: string, @RequiredParam() watermarkId: number): Observable<Watermark[]> {
|
||||
return super
|
||||
.delete(null, `${this._defaultModelPath}/${watermarkId}`)
|
||||
.pipe(switchMap(() => this.loadForDossierTemplate(dossierTemplateId)));
|
||||
}
|
||||
|
||||
@Validate()
|
||||
saveWatermark(@RequiredParam() body: IWatermark): Observable<Watermark> {
|
||||
return this._post(body, `${this._defaultModelPath}`).pipe(
|
||||
switchMap(watermark =>
|
||||
this.loadForDossierTemplate(watermark.dossierTemplateId).pipe(
|
||||
map(() => this._watermarksMapService.get(watermark.dossierTemplateId, watermark.id)),
|
||||
),
|
||||
),
|
||||
);
|
||||
async saveWatermark(@RequiredParam() body: IWatermark) {
|
||||
const watermark = await firstValueFrom(this._post(body, `${this._defaultModelPath}`));
|
||||
await firstValueFrom(this.loadForDossierTemplate(watermark.dossierTemplateId));
|
||||
return this.#watermarksMapService.get(watermark.dossierTemplateId, watermark.id);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
@ -42,7 +34,7 @@ export class WatermarkService extends GenericService<IWatermark> {
|
||||
const queryParams: QueryParam[] = [{ key: 'dossierTemplateId', value: dossierTemplateId }];
|
||||
return this.getAll(this._defaultModelPath, queryParams).pipe(
|
||||
mapEach(entity => new Watermark(entity)),
|
||||
tap(entities => this._watermarksMapService.set(dossierTemplateId, entities.sort(this.sortByStatusFn))),
|
||||
tap(entities => this.#watermarksMapService.set(dossierTemplateId, entities.sort(this.sortByStatusFn))),
|
||||
);
|
||||
}
|
||||
|
||||
@ -52,9 +44,10 @@ export class WatermarkService extends GenericService<IWatermark> {
|
||||
}
|
||||
|
||||
@Validate()
|
||||
isWatermarkUsed(@RequiredParam() watermarkId: number): Observable<boolean> {
|
||||
async isWatermarkUsed(@RequiredParam() watermarkId: number) {
|
||||
const queryParams: QueryParam[] = [{ key: 'watermarkId', value: watermarkId }];
|
||||
return this.getAll<IsUsedResponse>(`${this._defaultModelPath}/used`, queryParams).pipe(map(result => result.value));
|
||||
const result = await firstValueFrom(this.getAll<IsUsedResponse>(`${this._defaultModelPath}/used`, queryParams));
|
||||
return result.value;
|
||||
}
|
||||
|
||||
sortByStatusFn = (a: Watermark, b: Watermark) => {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { List } from '@iqser/common-ui';
|
||||
import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import { ITrackable } from '../../../../../libs/common-ui/src/lib/listing/models/trackable';
|
||||
|
||||
export function hexToRgb(hex: string) {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
@ -89,7 +90,10 @@ export function compareLists(l1: string[], l2: string[]) {
|
||||
}
|
||||
|
||||
export const byPage = (page: number) => (annotation: AnnotationWrapper) => annotation.pageNumber === page;
|
||||
export const byId = (id: string) => (annotation: AnnotationWrapper) => annotation.annotationId === id;
|
||||
|
||||
export function byId<T extends ITrackable>(id: string) {
|
||||
return (item: T) => item.id === id;
|
||||
}
|
||||
|
||||
export function getLast<T>(list: List<T>) {
|
||||
return list[list.length - 1];
|
||||
|
||||
@ -12,16 +12,16 @@ async function createPageSet(pdfNet: typeof Core.PDFNet, pages: number[]) {
|
||||
return pageSet;
|
||||
}
|
||||
|
||||
function convertFont(type: string): number {
|
||||
function convertFont(pdfNet: typeof Core.PDFNet, type: string): number {
|
||||
switch (type) {
|
||||
case 'times-new-roman':
|
||||
return 0;
|
||||
return pdfNet.Font.StandardType1Font.e_times_roman;
|
||||
case 'helvetica':
|
||||
return 4;
|
||||
return pdfNet.Font.StandardType1Font.e_helvetica;
|
||||
case 'courier':
|
||||
return 8;
|
||||
return pdfNet.Font.StandardType1Font.e_courier;
|
||||
}
|
||||
return 4;
|
||||
return pdfNet.Font.StandardType1Font.e_helvetica;
|
||||
}
|
||||
|
||||
export async function clearStamps(document: PDFDoc, pdfNet: typeof Core.PDFNet, pages: number[], licenseKey: string) {
|
||||
@ -34,7 +34,7 @@ export async function clearStamps(document: PDFDoc, pdfNet: typeof Core.PDFNet,
|
||||
|
||||
export async function stampPDFPage(
|
||||
document: PDFDoc,
|
||||
pdfNet: any,
|
||||
pdfNet: typeof Core.PDFNet,
|
||||
text: string,
|
||||
fontSize: number,
|
||||
fontType: string,
|
||||
@ -50,10 +50,10 @@ export async function stampPDFPage(
|
||||
const pageSet = await createPageSet(pdfNet, pages);
|
||||
await pdfNet.Stamper.deleteStamps(document, pageSet);
|
||||
|
||||
const rgbColor = hexToRgb(color);
|
||||
const { b, g, r } = 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));
|
||||
const stamper = await pdfNet.Stamper.create(pdfNet.Stamper.SizeType.e_font_size, fontSize, 0);
|
||||
await stamper.setFontColor(await pdfNet.ColorPt.init(r / 255, g / 255, b / 255));
|
||||
await stamper.setOpacity(opacity / 100);
|
||||
|
||||
switch (orientation) {
|
||||
@ -74,8 +74,10 @@ export async function stampPDFPage(
|
||||
await stamper.setRotation(-45);
|
||||
}
|
||||
|
||||
const font = await pdfNet.Font.createAndEmbed(document, convertFont(fontType));
|
||||
await stamper.setFont(font);
|
||||
const initialFont = await pdfNet.Font.create(document, convertFont(pdfNet, fontType));
|
||||
// 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.stampText(document, text, pageSet);
|
||||
}, licenseKey);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user