Pull request #79: Watermark screen
Merge in RED/ui from watermark to master * commit 'f240a4b2bafe0813d7c10217387b00b00b076053': Watermark
This commit is contained in:
commit
fbaf76e4f2
@ -1,4 +1,4 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, ViewContainerRef } from '@angular/core';
|
||||||
import { AppLoadStateService } from './utils/app-load-state.service';
|
import { AppLoadStateService } from './utils/app-load-state.service';
|
||||||
|
|
||||||
declare var ace;
|
declare var ace;
|
||||||
@ -11,5 +11,5 @@ ace.config.set('basePath', '/assets/ace-builds/');
|
|||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
constructor(public appLoadStateService: AppLoadStateService) {}
|
constructor(public appLoadStateService: AppLoadStateService, public viewContainerRef: ViewContainerRef) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,6 +98,7 @@ import { HtmlDebugScreenComponent } from './screens/html-debug-screen/html-debug
|
|||||||
import { ReportDownloadBtnComponent } from './components/buttons/report-download-btn/report-download-btn.component';
|
import { ReportDownloadBtnComponent } from './components/buttons/report-download-btn/report-download-btn.component';
|
||||||
import { ProjectListingActionsComponent } from './screens/project-listing-screen/project-listing-actions/project-listing-actions.component';
|
import { ProjectListingActionsComponent } from './screens/project-listing-screen/project-listing-actions/project-listing-actions.component';
|
||||||
import { HasScrollbarDirective } from './utils/has-scrollbar.directive';
|
import { HasScrollbarDirective } from './utils/has-scrollbar.directive';
|
||||||
|
import { MatSliderModule } from '@angular/material/slider';
|
||||||
import { PendingChangesGuard } from './utils/can-deactivate.guard';
|
import { PendingChangesGuard } from './utils/can-deactivate.guard';
|
||||||
|
|
||||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||||
@ -223,6 +224,7 @@ const matImports = [
|
|||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatSlideToggleModule,
|
MatSlideToggleModule,
|
||||||
|
MatSliderModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
MatTooltipModule,
|
MatTooltipModule,
|
||||||
|
|||||||
@ -41,6 +41,7 @@ redaction-table-col-name::ng-deep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dict-name {
|
.dict-name {
|
||||||
|
z-index: 1;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,13 +18,23 @@
|
|||||||
<div class="left-container">
|
<div class="left-container">
|
||||||
<div class="overlay-shadow"></div>
|
<div class="overlay-shadow"></div>
|
||||||
<div #viewer class="viewer"></div>
|
<div #viewer class="viewer"></div>
|
||||||
|
<div class="changes-box" *ngIf="changed">
|
||||||
|
<redaction-icon-button
|
||||||
|
*ngIf="permissionsService.isAdmin()"
|
||||||
|
[disabled]="configForm.invalid"
|
||||||
|
icon="red:check-alt"
|
||||||
|
(action)="save()"
|
||||||
|
text="watermark-screen.action.save"
|
||||||
|
type="primary"
|
||||||
|
></redaction-icon-button>
|
||||||
|
<div *ngIf="permissionsService.isAdmin()" (click)="revert()" translate="watermark-screen.action.revert" class="all-caps-label cancel"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-container">
|
<div class="right-container">
|
||||||
<div class="heading-xl" [translate]="'watermark-screen.title'"></div>
|
<div class="heading-xl" [translate]="'watermark-screen.title'"></div>
|
||||||
<form [formGroup]="configForm" (keyup)="configChanged()">
|
<form [formGroup]="configForm" (keyup)="configChanged()">
|
||||||
<div class="red-input-group required w-300">
|
<div class="red-input-group w-300">
|
||||||
<label translate="watermark-screen.form.text"></label>
|
|
||||||
<textarea
|
<textarea
|
||||||
redactionHasScrollbar
|
redactionHasScrollbar
|
||||||
formControlName="text"
|
formControlName="text"
|
||||||
@ -32,11 +42,36 @@
|
|||||||
class="w-full"
|
class="w-full"
|
||||||
name="text"
|
name="text"
|
||||||
type="text"
|
type="text"
|
||||||
rows="10"
|
rows="4"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="red-input-group required w-150">
|
|
||||||
<label translate="watermark-screen.form.color"></label>
|
<div class="red-input-group">
|
||||||
|
<label class="all-caps-label mb-8" translate="watermark-screen.form.orientation"></label>
|
||||||
|
<div class="square-options">
|
||||||
|
<div
|
||||||
|
[class.active]="configForm.get('orientation').value === option"
|
||||||
|
[ngClass]="option"
|
||||||
|
(click)="configForm.get('orientation').setValue(option); configChanged()"
|
||||||
|
*ngFor="let option of ['horizontal', 'diagonal', 'vertical']"
|
||||||
|
>
|
||||||
|
<span>ABC</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="red-input-group">
|
||||||
|
<label class="all-caps-label" translate="watermark-screen.form.font-size"></label>
|
||||||
|
<mat-slider formControlName="fontSize" min="5" max="50" color="primary" (change)="configChanged()"></mat-slider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="red-input-group">
|
||||||
|
<label class="all-caps-label" translate="watermark-screen.form.opacity"></label>
|
||||||
|
<mat-slider formControlName="opacity" min="1" color="primary" (change)="configChanged()"></mat-slider>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="red-input-group w-150">
|
||||||
|
<label class="all-caps-label mb-5" translate="watermark-screen.form.color"></label>
|
||||||
<input
|
<input
|
||||||
formControlName="hexColor"
|
formControlName="hexColor"
|
||||||
class="hex-color-input"
|
class="hex-color-input"
|
||||||
@ -49,6 +84,8 @@
|
|||||||
[style.background]="configForm.get('hexColor').value"
|
[style.background]="configForm.get('hexColor').value"
|
||||||
[colorPicker]="configForm.get('hexColor').value"
|
[colorPicker]="configForm.get('hexColor').value"
|
||||||
[cpOutputFormat]="'hex'"
|
[cpOutputFormat]="'hex'"
|
||||||
|
[cpPosition]="'top-right'"
|
||||||
|
[cpUseRootViewContainer]="true"
|
||||||
(colorPickerChange)="configForm.get('hexColor').setValue($event); configChanged()"
|
(colorPickerChange)="configForm.get('hexColor').setValue($event); configChanged()"
|
||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
@ -58,24 +95,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group required w-75">
|
<div class="red-input-group">
|
||||||
<label translate="watermark-screen.form.opacity"></label>
|
<label class="all-caps-label mb-8" translate="watermark-screen.form.font-type"></label>
|
||||||
<input formControlName="opacity" name="opacity" type="number" (change)="configChanged()" />
|
<div class="square-options">
|
||||||
</div>
|
<div
|
||||||
|
[class.active]="configForm.get('fontType').value === option.value"
|
||||||
<div class="red-input-group required w-75">
|
[ngClass]="option.value"
|
||||||
<label translate="watermark-screen.form.font-size"></label>
|
(click)="configForm.get('fontType').setValue(option.value); configChanged()"
|
||||||
<input formControlName="fontSize" name="fontSize" type="number" (change)="configChanged()" />
|
*ngFor="
|
||||||
|
let option of [
|
||||||
|
{ value: 'sans-serif', display: 'Sans Serif' },
|
||||||
|
{ value: 'serif', display: 'Serif' },
|
||||||
|
{ value: 'monospace', display: 'Mono' }
|
||||||
|
]
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ option.display }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="actions-container">
|
|
||||||
<button color="primary" mat-flat-button (click)="save()" [disabled]="configForm.invalid || !changed">
|
|
||||||
{{ 'watermark-screen.action.save' | translate }}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="all-caps-label pointer cancel" [class.disabled]="!changed" (click)="revert()" translate="watermark-screen.action.revert"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,9 +1,19 @@
|
|||||||
|
@import '../../../../assets/styles/red-variables';
|
||||||
|
|
||||||
|
.red-content-inner {
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
}
|
||||||
|
|
||||||
.left-container {
|
.left-container {
|
||||||
width: calc(100vw - 353px);
|
width: calc(100vw - 353px);
|
||||||
|
|
||||||
.viewer {
|
.viewer {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.changes-box {
|
||||||
|
right: 40px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-container {
|
.right-container {
|
||||||
@ -11,24 +21,62 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 353px;
|
width: 353px;
|
||||||
min-width: 353px;
|
min-width: 353px;
|
||||||
padding: 25px;
|
padding: 24px;
|
||||||
overflow: visible;
|
border-left: none;
|
||||||
|
border-right: 1px solid $separator;
|
||||||
&:hover {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading-xl {
|
.heading-xl {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-container {
|
.square-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
margin-top: 24px;
|
|
||||||
|
|
||||||
button:first-child {
|
> div {
|
||||||
margin-right: 8px;
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: $grey-6;
|
||||||
|
color: $grey-7;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($grey-6, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $primary;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.serif {
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.monospace {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.diagonal {
|
||||||
|
> span {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.vertical {
|
||||||
|
> span {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,3 +85,11 @@
|
|||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mb-5 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-8 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|||||||
@ -75,12 +75,12 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
this._watermarkControllerService.getWatermark(DEFAULT_RUL_SET_UUID).subscribe(
|
this._watermarkControllerService.getWatermark(DEFAULT_RUL_SET_UUID).subscribe(
|
||||||
(watermark) => {
|
(watermark) => {
|
||||||
this._watermark = watermark;
|
this._watermark = watermark;
|
||||||
this.configForm.setValue(this._watermark);
|
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
|
||||||
this._loadViewer();
|
this._loadViewer();
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this._watermark = DEFAULT_WATERMARK;
|
this._watermark = DEFAULT_WATERMARK;
|
||||||
this.configForm.setValue(this._watermark);
|
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
|
||||||
this._loadViewer();
|
this._loadViewer();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -111,7 +111,7 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public revert() {
|
public revert() {
|
||||||
this.configForm.setValue(this._watermark);
|
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
|
||||||
this.configChanged();
|
this.configChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this._disableElements();
|
this._disableElements();
|
||||||
this._instance.loadDocument(`${window.location.origin}/assets/pdftron/sample.pdf`);
|
this._instance.loadDocument(`${window.location.origin}/assets/pdftron/blank.pdf`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,26 +144,48 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _drawWatermark() {
|
private _drawWatermark() {
|
||||||
const lines = this.configForm.get('text').value.split('\n');
|
const text = this.configForm.get('text').value;
|
||||||
|
const lines = text.split('\n');
|
||||||
const fontSize = this.configForm.get('fontSize').value;
|
const fontSize = this.configForm.get('fontSize').value;
|
||||||
|
const fontType = this.configForm.get('fontType').value;
|
||||||
|
const orientation = this.configForm.get('orientation').value;
|
||||||
const lineHeight = fontSize + 4;
|
const lineHeight = fontSize + 4;
|
||||||
|
const opacity = this.configForm.get('opacity').value;
|
||||||
|
const color = this.configForm.get('hexColor').value;
|
||||||
|
|
||||||
this._instance.docViewer.setWatermark({
|
if (orientation === 'diagonal') {
|
||||||
custom: (ctx, pageNumber, pageWidth, pageHeight) => {
|
this._instance.docViewer.setWatermark({
|
||||||
ctx.fillStyle = this.configForm.get('hexColor').value;
|
diagonal: {
|
||||||
ctx.font = `${fontSize}pt Arial`;
|
text,
|
||||||
ctx.globalAlpha = this.configForm.get('opacity').value / 100;
|
fontSize: fontSize,
|
||||||
|
fontFamily: fontType,
|
||||||
for (let idx = 0; idx < lines.length; ++idx) {
|
color,
|
||||||
ctx.save();
|
opacity
|
||||||
ctx.translate(pageWidth / 2 - (lineHeight * (lines.length - 1)) / 4, pageHeight / 2 - (lineHeight * lines.length) / 2);
|
|
||||||
ctx.rotate(-Math.PI / 4);
|
|
||||||
ctx.translate(0, 10);
|
|
||||||
ctx.fillText(lines[idx], 0, idx * lineHeight);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
} else {
|
||||||
|
this._instance.docViewer.setWatermark({
|
||||||
|
custom: (ctx, pageNumber, pageWidth, pageHeight) => {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.font = `${fontSize}pt ${fontType}`;
|
||||||
|
ctx.globalAlpha = opacity / 100;
|
||||||
|
|
||||||
|
for (let idx = 0; idx < lines.length; ++idx) {
|
||||||
|
ctx.save();
|
||||||
|
if (orientation === 'horizontal') {
|
||||||
|
ctx.translate(pageWidth / 2, pageHeight / 2 - (lineHeight * lines.length) / 2);
|
||||||
|
}
|
||||||
|
if (orientation === 'vertical') {
|
||||||
|
ctx.translate(pageWidth / 2, 0);
|
||||||
|
ctx.rotate(-Math.PI / 2);
|
||||||
|
ctx.translate(-pageHeight / 2, -(lineHeight * lines.length) / 2);
|
||||||
|
}
|
||||||
|
ctx.fillText(lines[idx], 0, idx * lineHeight);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this._instance.docViewer.refreshAll();
|
this._instance.docViewer.refreshAll();
|
||||||
this._instance.docViewer.updateView([0], 0);
|
this._instance.docViewer.updateView([0], 0);
|
||||||
@ -174,7 +196,9 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
text: [null, Validators.required],
|
text: [null, Validators.required],
|
||||||
hexColor: [null, Validators.required],
|
hexColor: [null, Validators.required],
|
||||||
opacity: [null, Validators.required],
|
opacity: [null, Validators.required],
|
||||||
fontSize: [null, Validators.required]
|
fontSize: [null, Validators.required],
|
||||||
|
fontType: [null, Validators.required],
|
||||||
|
orientation: [null, Validators.required]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -627,18 +627,20 @@
|
|||||||
},
|
},
|
||||||
"watermark-screen": {
|
"watermark-screen": {
|
||||||
"form": {
|
"form": {
|
||||||
"text": "Text",
|
"text-placeholder": "Enter text",
|
||||||
"opacity": "Opacity",
|
"opacity": "Opacity",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
"font-size": "Font Size"
|
"font-size": "Font Size",
|
||||||
|
"font-type": "Font Type",
|
||||||
|
"orientation": "Orientation"
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"save": "Save",
|
"save": "Save Changes",
|
||||||
"revert": "Revert",
|
"revert": "Revert",
|
||||||
"success": "Watermark updated!",
|
"success": "Watermark updated!",
|
||||||
"error": "Failed to update Watermark"
|
"error": "Failed to update Watermark"
|
||||||
},
|
},
|
||||||
"title": "Configure Watermark"
|
"title": "Watermark"
|
||||||
},
|
},
|
||||||
"dictionaries": "Dictionaries",
|
"dictionaries": "Dictionaries",
|
||||||
"user-management": "User Management",
|
"user-management": "User Management",
|
||||||
|
|||||||
BIN
apps/red-ui/src/assets/pdftron/blank.pdf
Normal file
BIN
apps/red-ui/src/assets/pdftron/blank.pdf
Normal file
Binary file not shown.
Binary file not shown.
@ -70,6 +70,7 @@ body {
|
|||||||
.left-container {
|
.left-container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
|
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&.extended {
|
&.extended {
|
||||||
width: calc(100vw - 60px) !important;
|
width: calc(100vw - 60px) !important;
|
||||||
|
|||||||
36
apps/red-ui/src/assets/styles/red-slider.scss
Normal file
36
apps/red-ui/src/assets/styles/red-slider.scss
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
@import 'red-variables';
|
||||||
|
|
||||||
|
.mat-slider-horizontal {
|
||||||
|
width: 140px;
|
||||||
|
height: 32px !important;
|
||||||
|
|
||||||
|
.mat-slider-wrapper {
|
||||||
|
left: 0 !important;
|
||||||
|
top: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-slider-track-wrapper,
|
||||||
|
.mat-slider-track-fill {
|
||||||
|
height: 6px !important;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-slider-track-background {
|
||||||
|
height: 4px !important;
|
||||||
|
margin-top: 1px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: $grey-4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-slider-focus-ring {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-slider-thumb {
|
||||||
|
width: 16px !important;
|
||||||
|
height: 16px !important;
|
||||||
|
border-width: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
background-color: $primary !important;
|
||||||
|
}
|
||||||
@ -23,3 +23,4 @@
|
|||||||
@import 'red-grid';
|
@import 'red-grid';
|
||||||
@import 'red-breadcrumbs';
|
@import 'red-breadcrumbs';
|
||||||
@import 'red-editor';
|
@import 'red-editor';
|
||||||
|
@import 'red-slider';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user