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';
|
||||
|
||||
declare var ace;
|
||||
@ -11,5 +11,5 @@ ace.config.set('basePath', '/assets/ace-builds/');
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
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 { ProjectListingActionsComponent } from './screens/project-listing-screen/project-listing-actions/project-listing-actions.component';
|
||||
import { HasScrollbarDirective } from './utils/has-scrollbar.directive';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { PendingChangesGuard } from './utils/can-deactivate.guard';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
@ -223,6 +224,7 @@ const matImports = [
|
||||
MatToolbarModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatSliderModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
|
||||
@ -41,6 +41,7 @@ redaction-table-col-name::ng-deep {
|
||||
}
|
||||
|
||||
.dict-name {
|
||||
z-index: 1;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@ -18,13 +18,23 @@
|
||||
<div class="left-container">
|
||||
<div class="overlay-shadow"></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 class="right-container">
|
||||
<div class="heading-xl" [translate]="'watermark-screen.title'"></div>
|
||||
<form [formGroup]="configForm" (keyup)="configChanged()">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="watermark-screen.form.text"></label>
|
||||
<div class="red-input-group w-300">
|
||||
<textarea
|
||||
redactionHasScrollbar
|
||||
formControlName="text"
|
||||
@ -32,11 +42,36 @@
|
||||
class="w-full"
|
||||
name="text"
|
||||
type="text"
|
||||
rows="10"
|
||||
rows="4"
|
||||
></textarea>
|
||||
</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
|
||||
formControlName="hexColor"
|
||||
class="hex-color-input"
|
||||
@ -49,6 +84,8 @@
|
||||
[style.background]="configForm.get('hexColor').value"
|
||||
[colorPicker]="configForm.get('hexColor').value"
|
||||
[cpOutputFormat]="'hex'"
|
||||
[cpPosition]="'top-right'"
|
||||
[cpUseRootViewContainer]="true"
|
||||
(colorPickerChange)="configForm.get('hexColor').setValue($event); configChanged()"
|
||||
>
|
||||
<mat-icon
|
||||
@ -58,24 +95,26 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-75">
|
||||
<label translate="watermark-screen.form.opacity"></label>
|
||||
<input formControlName="opacity" name="opacity" type="number" (change)="configChanged()" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-75">
|
||||
<label translate="watermark-screen.form.font-size"></label>
|
||||
<input formControlName="fontSize" name="fontSize" type="number" (change)="configChanged()" />
|
||||
<div class="red-input-group">
|
||||
<label class="all-caps-label mb-8" translate="watermark-screen.form.font-type"></label>
|
||||
<div class="square-options">
|
||||
<div
|
||||
[class.active]="configForm.get('fontType').value === option.value"
|
||||
[ngClass]="option.value"
|
||||
(click)="configForm.get('fontType').setValue(option.value); configChanged()"
|
||||
*ngFor="
|
||||
let option of [
|
||||
{ value: 'sans-serif', display: 'Sans Serif' },
|
||||
{ value: 'serif', display: 'Serif' },
|
||||
{ value: 'monospace', display: 'Mono' }
|
||||
]
|
||||
"
|
||||
>
|
||||
{{ option.display }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</section>
|
||||
|
||||
@ -1,9 +1,19 @@
|
||||
@import '../../../../assets/styles/red-variables';
|
||||
|
||||
.red-content-inner {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.left-container {
|
||||
width: calc(100vw - 353px);
|
||||
|
||||
.viewer {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.changes-box {
|
||||
right: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-container {
|
||||
@ -11,24 +21,62 @@
|
||||
flex-direction: column;
|
||||
width: 353px;
|
||||
min-width: 353px;
|
||||
padding: 25px;
|
||||
overflow: visible;
|
||||
|
||||
&:hover {
|
||||
overflow: visible;
|
||||
}
|
||||
padding: 24px;
|
||||
border-left: none;
|
||||
border-right: 1px solid $separator;
|
||||
|
||||
.heading-xl {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
.square-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 24px;
|
||||
|
||||
button:first-child {
|
||||
margin-right: 8px;
|
||||
> div {
|
||||
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;
|
||||
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(
|
||||
(watermark) => {
|
||||
this._watermark = watermark;
|
||||
this.configForm.setValue(this._watermark);
|
||||
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
|
||||
this._loadViewer();
|
||||
},
|
||||
() => {
|
||||
this._watermark = DEFAULT_WATERMARK;
|
||||
this.configForm.setValue(this._watermark);
|
||||
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
|
||||
this._loadViewer();
|
||||
}
|
||||
);
|
||||
@ -111,7 +111,7 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
public revert() {
|
||||
this.configForm.setValue(this._watermark);
|
||||
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
|
||||
this.configChanged();
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
});
|
||||
|
||||
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() {
|
||||
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 fontType = this.configForm.get('fontType').value;
|
||||
const orientation = this.configForm.get('orientation').value;
|
||||
const lineHeight = fontSize + 4;
|
||||
const opacity = this.configForm.get('opacity').value;
|
||||
const color = this.configForm.get('hexColor').value;
|
||||
|
||||
this._instance.docViewer.setWatermark({
|
||||
custom: (ctx, pageNumber, pageWidth, pageHeight) => {
|
||||
ctx.fillStyle = this.configForm.get('hexColor').value;
|
||||
ctx.font = `${fontSize}pt Arial`;
|
||||
ctx.globalAlpha = this.configForm.get('opacity').value / 100;
|
||||
|
||||
for (let idx = 0; idx < lines.length; ++idx) {
|
||||
ctx.save();
|
||||
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();
|
||||
if (orientation === 'diagonal') {
|
||||
this._instance.docViewer.setWatermark({
|
||||
diagonal: {
|
||||
text,
|
||||
fontSize: fontSize,
|
||||
fontFamily: fontType,
|
||||
color,
|
||||
opacity
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} 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.updateView([0], 0);
|
||||
@ -174,7 +196,9 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
text: [null, Validators.required],
|
||||
hexColor: [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": {
|
||||
"form": {
|
||||
"text": "Text",
|
||||
"text-placeholder": "Enter text",
|
||||
"opacity": "Opacity",
|
||||
"color": "Color",
|
||||
"font-size": "Font Size"
|
||||
"font-size": "Font Size",
|
||||
"font-type": "Font Type",
|
||||
"orientation": "Orientation"
|
||||
},
|
||||
"action": {
|
||||
"save": "Save",
|
||||
"save": "Save Changes",
|
||||
"revert": "Revert",
|
||||
"success": "Watermark updated!",
|
||||
"error": "Failed to update Watermark"
|
||||
},
|
||||
"title": "Configure Watermark"
|
||||
"title": "Watermark"
|
||||
},
|
||||
"dictionaries": "Dictionaries",
|
||||
"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 {
|
||||
overflow: hidden;
|
||||
transition: width ease-in-out 0.2s, min-width ease-in-out 0.2s;
|
||||
position: relative;
|
||||
|
||||
&.extended {
|
||||
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-breadcrumbs';
|
||||
@import 'red-editor';
|
||||
@import 'red-slider';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user