Add pdf webworker and progress bar

Generate PDF in the background using webworker
Shows a progress bar to estimate the PDF generation progress
This commit is contained in:
Sean Engelhardt 2019-08-01 13:23:56 +02:00
parent 2f7937a27d
commit 6123216afc
27 changed files with 455 additions and 135 deletions

View File

@ -31,7 +31,8 @@
{ "glob": "**/*", "input": "node_modules/tinymce/plugins", "output": "/tinymce/plugins/" } { "glob": "**/*", "input": "node_modules/tinymce/plugins", "output": "/tinymce/plugins/" }
], ],
"styles": ["src/styles.scss"], "styles": ["src/styles.scss"],
"scripts": ["node_modules/tinymce/tinymce.min.js"] "scripts": ["node_modules/tinymce/tinymce.min.js"],
"webWorkerTsConfig": "tsconfig.worker.json"
}, },
"configurations": { "configurations": {
"production": { "production": {

View File

@ -2,7 +2,7 @@ import { inject, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module'; import { E2EImportsModule } from 'e2e-imports.module';
import { PdfDocumentService } from '../ui-services/pdf-document.service'; import { PdfDocumentService } from './pdf-document.service';
describe('PdfDocumentService', () => { describe('PdfDocumentService', () => {
beforeEach(() => { beforeEach(() => {

View File

@ -1,13 +1,15 @@
import { HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import pdfMake from 'pdfmake/build/pdfmake';
import { ProgressSnackBarComponent } from 'app/shared/components/progress-snack-bar/progress-snack-bar.component';
import { ExportFormData } from 'app/site/motions/modules/motion-list/components/motion-export-dialog/motion-export-dialog.component'; import { ExportFormData } from 'app/site/motions/modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
import { ConfigService } from './config.service'; import { ConfigService } from '../ui-services/config.service';
import { HttpService } from '../core-services/http.service'; import { HttpService } from '../core-services/http.service';
import { ProgressService } from '../ui-services/progress.service';
/** /**
* Enumeration to define possible values for the styling. * Enumeration to define possible values for the styling.
@ -51,7 +53,7 @@ export class PdfError extends Error {
* @example * @example
* ```ts * ```ts
* const motionContent = this.motionPdfService.motionToDocDef(this.motion); * const motionContent = this.motionPdfService.motionToDocDef(this.motion);
* this.this.pdfDocumentService.open(motionContent); * this.pdfDocumentService.download(motionContent, 'test.pdf');
* ``` * ```
*/ */
@Injectable({ @Injectable({
@ -64,25 +66,6 @@ export class PdfDocumentService {
*/ */
private imageUrls: string[] = []; private imageUrls: string[] = [];
/**
* Table layout that switches the background color every other row.
* @example
* ```ts
* layout: this.pdfDocumentService.switchColorTableLayout
* ```
*/
public switchColorTableLayout = {
hLineWidth: rowIndex => {
return rowIndex === 1;
},
vLineWidth: () => {
return 0;
},
fillColor: rowIndex => {
return rowIndex % 2 === 0 ? '#EEEEEE' : null;
}
};
/** /**
* Constructor * Constructor
* *
@ -93,7 +76,9 @@ export class PdfDocumentService {
public constructor( public constructor(
private translate: TranslateService, private translate: TranslateService,
private configService: ConfigService, private configService: ConfigService,
private httpService: HttpService private httpService: HttpService,
private matSnackBar: MatSnackBar,
private progressService: ProgressService
) {} ) {}
/** /**
@ -183,13 +168,9 @@ export class PdfDocumentService {
customMargins?: [number, number, number, number], customMargins?: [number, number, number, number],
landscape?: boolean landscape?: boolean
): Promise<object> { ): Promise<object> {
this.initFonts();
this.imageUrls = imageUrls ? imageUrls : []; this.imageUrls = imageUrls ? imageUrls : [];
pdfMake.vfs = await this.initVfs();
const pageSize = this.configService.instant('general_export_pdf_pagesize'); const pageSize = this.configService.instant('general_export_pdf_pagesize');
const defaultMargins = pageSize === 'A5' ? [45, 30, 45, 45] : [75, 90, 75, 75]; const defaultMargins = pageSize === 'A5' ? [45, 30, 45, 45] : [75, 90, 75, 75];
// needs to be done before, cause the footer is async
this.loadFooterImages();
const result = { const result = {
pageSize: pageSize || 'A4', pageSize: pageSize || 'A4',
pageOrientation: landscape ? 'landscape' : 'portrait', pageOrientation: landscape ? 'landscape' : 'portrait',
@ -199,21 +180,12 @@ export class PdfDocumentService {
fontSize: this.configService.instant('general_export_pdf_fontsize') fontSize: this.configService.instant('general_export_pdf_fontsize')
}, },
header: this.getHeader(customMargins ? [customMargins[0], customMargins[2]] : null), header: this.getHeader(customMargins ? [customMargins[0], customMargins[2]] : null),
// real footer gets created in the worker
// TODO: option for no footer, wherever this can be defined tmpfooter: this.getFooter(customMargins ? [customMargins[0], customMargins[2]] : null, exportInfo),
footer: (currentPage, pageCount) => {
return this.getFooter(
currentPage,
pageCount,
customMargins ? [customMargins[0], customMargins[2]] : null,
exportInfo
);
},
info: metadata, info: metadata,
content: documentContent, content: documentContent,
styles: this.getStandardPaperStyles() styles: this.getStandardPaperStyles()
}; };
await this.loadAllImages();
return result; return result;
} }
@ -226,9 +198,7 @@ export class PdfDocumentService {
* @returns the pdf document definition ready to export * @returns the pdf document definition ready to export
*/ */
private async getBallotPaper(documentContent: object, imageUrl?: string): Promise<object> { private async getBallotPaper(documentContent: object, imageUrl?: string): Promise<object> {
this.initFonts();
this.imageUrls = imageUrl ? [imageUrl] : []; this.imageUrls = imageUrl ? [imageUrl] : [];
pdfMake.vfs = await this.initVfs();
const result = { const result = {
pageSize: 'A4', pageSize: 'A4',
pageMargins: [0, 0, 0, 0], pageMargins: [0, 0, 0, 0],
@ -239,21 +209,18 @@ export class PdfDocumentService {
content: documentContent, content: documentContent,
styles: this.getBlankPaperStyles() styles: this.getBlankPaperStyles()
}; };
await this.loadAllImages();
return result; return result;
} }
/** /**
* Define the fonts * Get pdfFonts from storage
*/ */
private initFonts(): void { private getPdfFonts(): object {
pdfMake.fonts = { return {
PdfFont: {
normal: this.getFontName('font_regular'), normal: this.getFontName('font_regular'),
bold: this.getFontName('font_bold'), bold: this.getFontName('font_bold'),
italics: this.getFontName('font_italic'), italics: this.getFontName('font_italic'),
bolditalics: this.getFontName('font_bold_italic') bolditalics: this.getFontName('font_bold_italic')
}
}; };
} }
@ -341,14 +308,9 @@ export class PdfDocumentService {
* @param lrMargin optionally overriding the margins * @param lrMargin optionally overriding the margins
* @returns the footer doc definition * @returns the footer doc definition
*/ */
private getFooter( private getFooter(lrMargin?: [number, number], exportInfo?: ExportFormData): object {
currentPage: number,
pageCount: number,
lrMargin?: [number, number],
exportInfo?: ExportFormData
): object {
const columns = []; const columns = [];
const showPage = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true; const showPageNr = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true;
const showDate = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('date') : false; const showDate = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('date') : false;
let logoContainerWidth: string; let logoContainerWidth: string;
let pageNumberPosition: string; let pageNumberPosition: string;
@ -357,11 +319,10 @@ export class PdfDocumentService {
const logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path; const logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
let footerPageNumber = ''; let footerPageNumber = '';
if (showPage) { if (showPageNr) {
footerPageNumber += `${currentPage} / ${pageCount}`; // footerPageNumber += `${currentPage} / ${pageCount}`;
if (showDate) { // replace with `${currentPage} / ${pageCount}` in worker
footerPageNumber += '\n'; footerPageNumber += `%PAGENR%`;
}
} }
let footerDate = {}; let footerDate = {};
@ -402,11 +363,12 @@ export class PdfDocumentService {
width: logoContainerWidth, width: logoContainerWidth,
alignment: 'left' alignment: 'left'
}); });
this.imageUrls.push(logoFooterLeftUrl);
} }
// add the page number // add the page number
columns.push({ columns.push({
text: [footerPageNumber, footerDate], stack: [footerPageNumber, footerDate],
style: 'footerPageNumber', style: 'footerPageNumber',
alignment: pageNumberPosition alignment: pageNumberPosition
}); });
@ -419,6 +381,7 @@ export class PdfDocumentService {
width: logoContainerWidth, width: logoContainerWidth,
alignment: 'right' alignment: 'right'
}); });
this.imageUrls.push(logoFooterRightUrl);
} }
const margin = [lrMargin ? lrMargin[0] : 75, 0, lrMargin ? lrMargin[0] : 75, 10]; const margin = [lrMargin ? lrMargin[0] : 75, 0, lrMargin ? lrMargin[0] : 75, 10];
@ -454,6 +417,7 @@ export class PdfDocumentService {
this.createPdf(doc, filename); this.createPdf(doc, filename);
}); });
} }
/** /**
* Downloads a pdf with the ballot papet page definitions. * Downloads a pdf with the ballot papet page definitions.
* *
@ -471,12 +435,57 @@ export class PdfDocumentService {
* Triggers the actual page creation and saving. * Triggers the actual page creation and saving.
* *
* @param doc the finished layout * @param doc the finished layout
* @param filename the filename (without extension) to save as * @param filetitle the filename (without extension) to save as
*/ */
private createPdf(doc: object, filename: string): void { private async createPdf(doc: object, filetitle: string): Promise<void> {
pdfMake.createPdf(doc).getBlob(blob => { const filename = `${filetitle}.pdf`;
saveAs(blob, `${filename}.pdf`, { autoBOM: true });
// set the required progress info
this.progressService.progressInfo = {
mode: 'determinate',
text: filename
};
// open progress bar
this.matSnackBar.openFromComponent(ProgressSnackBarComponent, {
duration: 0
}); });
const fonts = this.getPdfFonts();
const vfs = await this.initVfs();
await this.loadAllImages(vfs);
if (typeof Worker !== 'undefined') {
const worker = new Worker('./pdf-worker.worker', {
type: 'module'
});
// the result of the worker
worker.onmessage = ({ data }) => {
// if the worker returns a numbers, is always the progress
if (typeof data === 'number') {
// update progress
const progress = Math.ceil(data * 100);
this.progressService.progressAmount = progress;
}
// if the worker returns an object, it's always the document
if (typeof data === 'object') {
// close progress bar
this.matSnackBar.dismiss();
saveAs(data, filename, { autoBOM: true });
}
};
worker.postMessage({
doc: JSON.parse(JSON.stringify(doc)),
fonts: fonts,
vfs: vfs
});
} else {
this.matSnackBar.open(this.translate.instant('Web workers are not supported on your browser.'), '', {
duration: 0
});
}
} }
/** /**
@ -612,30 +621,13 @@ export class PdfDocumentService {
}; };
} }
/**
* Adds the footer images to the imageUrls-list, cause the create footer function is async and
* potentially called after loadAllImages was called.
*/
private loadFooterImages(): void {
const logoFooterLeftUrl = this.configService.instant<any>('logo_pdf_footer_L').path;
const logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
if (logoFooterLeftUrl) {
this.imageUrls.push(logoFooterLeftUrl);
}
if (logoFooterRightUrl) {
this.imageUrls.push(logoFooterRightUrl);
}
}
/** /**
* Triggers the addition of all images found during creation(including header and footer) * Triggers the addition of all images found during creation(including header and footer)
* to the vfs. * to the vfs.
*/ */
private async loadAllImages(): Promise<void> { private async loadAllImages(vfs: object): Promise<void> {
const promises = this.imageUrls.map(image => { const promises = this.imageUrls.map(image => {
return this.addImageToVfS(image); return this.addImageToVfS(image, vfs);
}); });
await Promise.all(promises); await Promise.all(promises);
} }
@ -645,14 +637,14 @@ export class PdfDocumentService {
* *
* @param url * @param url
*/ */
private async addImageToVfS(url: string): Promise<void> { private async addImageToVfS(url: string, vfs: object): Promise<void> {
if (url.indexOf('/') === 0) { if (url.indexOf('/') === 0) {
url = url.substr(1); url = url.substr(1);
} }
if (!pdfMake.vfs[url]) { if (!vfs[url]) {
const base64 = await this.convertUrlToBase64(url); const base64 = await this.convertUrlToBase64(url);
pdfMake.vfs[url] = base64; vfs[url] = base64;
} }
} }

View File

@ -0,0 +1,114 @@
/// <reference lib="webworker" />
import pdfMake from 'pdfmake/build/pdfmake';
const osTableLayout = {
switchColorTableLayout: {
hLineWidth: rowIndex => {
return rowIndex === 1;
},
vLineWidth: () => {
return 0;
},
fillColor: rowIndex => {
return rowIndex % 2 === 0 ? '#EEEEEE' : null;
}
},
metaboxLayout: {
fillColor: () => {
return '#dddddd';
},
hLineWidth: (i, node) => {
return i === 0 || i === node.table.body.length ? 0 : 0.5;
},
vLineWidth: () => {
return 0;
},
hLineColor: () => {
return 'white';
}
}
};
function applyLayout(content: any): void {
for (const section of content) {
if (Array.isArray(section)) {
applyLayout(section);
} else {
if (!!section.layout) {
let layout: object;
switch (section.layout) {
case 'switchColorTableLayout': {
layout = osTableLayout.switchColorTableLayout;
break;
}
case 'metaboxLayout': {
layout = osTableLayout.metaboxLayout;
break;
}
}
if (!!layout) {
section.layout = layout;
}
}
}
}
}
/**
* Sets the internal PdfMake fonts and VFS
*/
function initPdfMake(data: any): void {
pdfMake.fonts = {
PdfFont: data.fonts
};
pdfMake.vfs = data.vfs;
}
/**
* Replace the palceholder with actual page numbers
* @param data
*/
function addPageNumbers(data: any): void {
// to allow page numbers in every page, after the initial "%PAGENR%" placeholder was reset
let countPageNumbers = false;
data.doc.footer = (currentPage, pageCount) => {
const footer = data.doc.tmpfooter;
// "%PAGENR% needs to be found once. After that, the same position should always update page numbers"
if (footer.columns[0].stack[0] === '%PAGENR%' || countPageNumbers) {
countPageNumbers = true;
footer.columns[0].stack[0] = `${currentPage} / ${pageCount}`;
}
return footer;
};
}
/**
* The actual web worker code
*/
addEventListener('message', ({ data }) => {
initPdfMake(data);
applyLayout(data.doc.content);
if (!!data.doc.tmpfooter) {
addPageNumbers(data);
}
const pdfGenerator = pdfMake.createPdf(data.doc);
pdfGenerator.getBlob(
blob => {
// post the result back to the main thread
postMessage(blob);
},
{
progressCallback: progress => {
postMessage(progress);
}
}
);
});

View File

@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { ProgressService } from './progress.service';
describe('ProgressService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: ProgressService = TestBed.get(ProgressService);
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,63 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
/**
* Determine the progress mode
*/
export type ProgressMode = 'determinate' | 'indeterminate' | 'buffer' | 'query';
/**
* Shape of the progress info
*/
export interface ProgressInfo {
mode: ProgressMode;
text?: string;
}
/**
* Helper service to announce some sort of progress, determinate or indeterminate.
*/
@Injectable({
providedIn: 'root'
})
export class ProgressService {
/**
* Subject to get progress information
*/
private _progressInfo: Subject<ProgressInfo> = new Subject();
/**
* Subject to get the progress amount
*/
private _progressAmount: Subject<number> = new Subject();
/**
* Get the progress information as observable
*/
public get info(): Subject<ProgressInfo> {
return this._progressInfo;
}
/**
* Get the progres amount as observable
*/
public get amount(): Subject<number> {
return this._progressAmount;
}
/**
* Set the progress info. Usually only required once for every part if new information
*/
public set progressInfo(newInfo: ProgressInfo) {
setTimeout(() => this._progressInfo.next(newInfo));
}
/**
* Set the new progress amount. Can be called whenever new info about the progress
* is available. Required only if the ProgressMode is set to 'determinate'
*/
public set progressAmount(newAmount: number) {
this._progressAmount.next(newAmount);
}
}

View File

@ -0,0 +1,6 @@
<div>
{{ message | translate }}
</div>
<div>
<mat-progress-bar color="accent" [mode]="mode" [value]="value"></mat-progress-bar>
</div>

View File

@ -0,0 +1,26 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { E2EImportsModule } from 'e2e-imports.module';
import { ProgressSnackBarComponent } from './progress-snack-bar.component';
describe('ProgressSnackBarComponent', () => {
let component: ProgressSnackBarComponent;
let fixture: ComponentFixture<ProgressSnackBarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [E2EImportsModule]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProgressSnackBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,110 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
ViewEncapsulation
} from '@angular/core';
import { Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { ProgressMode, ProgressService } from 'app/core/ui-services/progress.service';
/**
* Component to show the progress announced in the progress service in a snack bar
* component
*/
@Component({
selector: 'os-progress-snack-bar',
templateUrl: './progress-snack-bar.component.html',
styleUrls: ['./progress-snack-bar.component.scss'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProgressSnackBarComponent implements OnInit, OnDestroy {
/**
* Private declaration of the mode
*/
private _mode: ProgressMode = 'indeterminate';
/**
* Private declaration of the progress message
*/
private _message = '';
/**
* Private declaration of the value
*/
private _value = 0;
/**
* The sub for the info
*/
private infoSubscription: Subscription;
/**
* The sub for the amount
*/
private valueSubscription: Subscription;
/**
* Public getter of the progress bar mode
*/
public get mode(): ProgressMode {
return this._mode;
}
/**
* Public getter of the progress bar message. Will be trasnslated in the UIs
*/
public get message(): string {
return this._message;
}
/**
* Public getter for the progress value
*/
public get value(): number {
return this._value;
}
/**
* Declare the progressService
*/
public constructor(private progressService: ProgressService, private cd: ChangeDetectorRef) {}
/**
* Get the progress subject and subscribe to the info subject
*/
public ngOnInit(): void {
this.infoSubscription = this.progressService.info.subscribe(info => {
this._message = info.text;
this._mode = info.mode;
this.cd.detectChanges();
});
this.valueSubscription = this.progressService.amount.pipe(distinctUntilChanged()).subscribe(value => {
if (value - this._value >= 5 || value === 100) {
this._value = value;
this.cd.detectChanges();
}
});
}
/**
* clear the Subscriptions
*/
public ngOnDestroy(): void {
if (this.infoSubscription) {
this.infoSubscription.unsubscribe();
this.infoSubscription = null;
}
if (this.valueSubscription) {
this.valueSubscription.unsubscribe();
this.valueSubscription = null;
}
}
}

View File

@ -96,6 +96,7 @@ import { AgendaContentObjectFormComponent } from './components/agenda-content-ob
import { ExtensionFieldComponent } from './components/extension-field/extension-field.component'; import { ExtensionFieldComponent } from './components/extension-field/extension-field.component';
import { AttachmentControlComponent } from './components/attachment-control/attachment-control.component'; import { AttachmentControlComponent } from './components/attachment-control/attachment-control.component';
import { RoundedInputComponent } from './components/rounded-input/rounded-input.component'; import { RoundedInputComponent } from './components/rounded-input/rounded-input.component';
import { ProgressSnackBarComponent } from './components/progress-snack-bar/progress-snack-bar.component';
/** /**
* Share Module for all "dumb" components and pipes. * Share Module for all "dumb" components and pipes.
@ -274,7 +275,8 @@ import { RoundedInputComponent } from './components/rounded-input/rounded-input.
AgendaContentObjectFormComponent, AgendaContentObjectFormComponent,
ExtensionFieldComponent, ExtensionFieldComponent,
AttachmentControlComponent, AttachmentControlComponent,
RoundedInputComponent RoundedInputComponent,
ProgressSnackBarComponent
], ],
providers: [ providers: [
{ provide: DateAdapter, useClass: OpenSlidesDateAdapter }, { provide: DateAdapter, useClass: OpenSlidesDateAdapter },
@ -283,14 +285,16 @@ import { RoundedInputComponent } from './components/rounded-input/rounded-input.
SortingTreeComponent, SortingTreeComponent,
SortFilterBarComponent, SortFilterBarComponent,
SortBottomSheetComponent, SortBottomSheetComponent,
DecimalPipe DecimalPipe,
ProgressSnackBarComponent
], ],
entryComponents: [ entryComponents: [
SortBottomSheetComponent, SortBottomSheetComponent,
C4DialogComponent, C4DialogComponent,
PromptDialogComponent, PromptDialogComponent,
ChoiceDialogComponent, ChoiceDialogComponent,
ProjectionDialogComponent ProjectionDialogComponent,
ProgressSnackBarComponent
] ]
}) })
export class SharedModule {} export class SharedModule {}

View File

@ -12,13 +12,13 @@ import { AgendaFilterListService } from '../../services/agenda-filter-list.servi
import { AgendaPdfService } from '../../services/agenda-pdf.service'; import { AgendaPdfService } from '../../services/agenda-pdf.service';
import { OperatorService } from 'app/core/core-services/operator.service'; import { OperatorService } from 'app/core/core-services/operator.service';
import { StorageService } from 'app/core/core-services/storage.service'; import { StorageService } from 'app/core/core-services/storage.service';
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service'; import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service'; import { ListOfSpeakersRepositoryService } from 'app/core/repositories/agenda/list-of-speakers-repository.service';
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service'; import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
import { _ } from 'app/core/translate/translation-marker'; import { _ } from 'app/core/translate/translation-marker';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { DurationService } from 'app/core/ui-services/duration.service'; import { DurationService } from 'app/core/ui-services/duration.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { PromptService } from 'app/core/ui-services/prompt.service'; import { PromptService } from 'app/core/ui-services/prompt.service';
import { ViewportService } from 'app/core/ui-services/viewport.service'; import { ViewportService } from 'app/core/ui-services/viewport.service';
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component'; import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { PdfDocumentService, PdfError } from 'app/core/ui-services/pdf-document.service'; import { PdfDocumentService, PdfError } from 'app/core/pdf-services/pdf-document.service';
import { AssignmentPdfService } from './assignment-pdf.service'; import { AssignmentPdfService } from './assignment-pdf.service';
import { ViewAssignment } from '../models/view-assignment'; import { ViewAssignment } from '../models/view-assignment';

View File

@ -2,8 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { HtmlToPdfService } from 'app/core/ui-services/html-to-pdf.service'; import { HtmlToPdfService } from 'app/core/pdf-services/html-to-pdf.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { PollVoteValue } from 'app/core/ui-services/poll.service'; import { PollVoteValue } from 'app/core/ui-services/poll.service';
import { AssignmentPollService } from './assignment-poll.service'; import { AssignmentPollService } from './assignment-poll.service';
import { ViewAssignment } from '../models/view-assignment'; import { ViewAssignment } from '../models/view-assignment';
@ -34,7 +33,6 @@ export class AssignmentPdfService {
public constructor( public constructor(
private translate: TranslateService, private translate: TranslateService,
private pollService: AssignmentPollService, private pollService: AssignmentPollService,
private pdfDocumentService: PdfDocumentService,
private htmlToPdfService: HtmlToPdfService private htmlToPdfService: HtmlToPdfService
) {} ) {}
@ -265,7 +263,7 @@ export class AssignmentPdfService {
headerRows: 1, headerRows: 1,
body: pollTableBody body: pollTableBody
}, },
layout: this.pdfDocumentService.switchColorTableLayout layout: 'switchColorTableLayout'
}); });
} }
} }

View File

@ -2,11 +2,11 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { AbstractPollData, BallotCountChoices, PollPdfService } from 'app/core/pdf-services/base-poll-pdf-service';
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service'; import { AssignmentRepositoryService } from 'app/core/repositories/assignments/assignment-repository.service';
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { AbstractPollData, BallotCountChoices, PollPdfService } from 'app/core/ui-services/poll-pdf-service';
import { AssignmentPollMethod } from './assignment-poll.service'; import { AssignmentPollMethod } from './assignment-poll.service';
import { ViewAssignmentPoll } from '../models/view-assignment-poll'; import { ViewAssignmentPoll } from '../models/view-assignment-poll';

View File

@ -8,13 +8,13 @@ import { TranslateService } from '@ngx-translate/core';
import { PblColumnDefinition } from '@pebula/ngrid'; import { PblColumnDefinition } from '@pebula/ngrid';
import { StorageService } from 'app/core/core-services/storage.service'; import { StorageService } from 'app/core/core-services/storage.service';
import { PdfError } from 'app/core/pdf-services/pdf-document.service';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service'; import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service'; import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service'; import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service';
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service'; import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { PdfError } from 'app/core/ui-services/pdf-document.service';
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component'; import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';
import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings'; import { infoDialogSettings, largeDialogSettings } from 'app/shared/utils/dialog-settings';
import { BaseListViewComponent } from 'app/site/base/base-list-view'; import { BaseListViewComponent } from 'app/site/base/base-list-view';

View File

@ -3,10 +3,10 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { BorderType, PdfDocumentService, PdfError, StyleType } from 'app/core/pdf-services/pdf-document.service';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service'; import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { BorderType, PdfDocumentService, PdfError, StyleType } from 'app/core/ui-services/pdf-document.service';
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component'; import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
import { MotionPdfService } from './motion-pdf.service'; import { MotionPdfService } from './motion-pdf.service';
import { ViewCategory } from '../models/view-category'; import { ViewCategory } from '../models/view-category';

View File

@ -2,8 +2,8 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note'; import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component'; import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
import { MotionPdfCatalogService } from './motion-pdf-catalog.service'; import { MotionPdfCatalogService } from './motion-pdf-catalog.service';

View File

@ -2,14 +2,14 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { HtmlToPdfService } from 'app/core/pdf-services/html-to-pdf.service';
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service'; import { ChangeRecommendationRepositoryService } from 'app/core/repositories/motions/change-recommendation-repository.service';
import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service'; import { MotionCommentSectionRepositoryService } from 'app/core/repositories/motions/motion-comment-section-repository.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service'; import { StatuteParagraphRepositoryService } from 'app/core/repositories/motions/statute-paragraph-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { HtmlToPdfService } from 'app/core/ui-services/html-to-pdf.service';
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service'; import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { CalculablePollKey } from 'app/core/ui-services/poll.service'; import { CalculablePollKey } from 'app/core/ui-services/poll.service';
import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change'; import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change';
import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names'; import { getRecommendationTypeName } from 'app/shared/utils/recommendation-type-names';
@ -547,21 +547,7 @@ export class MotionPdfService {
body: metaTableBody body: metaTableBody
}, },
margin: [0, 0, 0, 20], margin: [0, 0, 0, 20],
// That did not work too well in the past. Perhaps substitution by a pdfWorker the worker will be necessary layout: 'metaboxLayout'
layout: {
fillColor: () => {
return '#dddddd';
},
hLineWidth: (i, node) => {
return i === 0 || i === node.table.body.length ? 0 : 0.5;
},
vLineWidth: () => {
return 0;
},
hLineColor: () => {
return 'white';
}
}
}; };
} }
} }
@ -776,7 +762,7 @@ export class MotionPdfService {
dontBreakRows: true, dontBreakRows: true,
body: callListTableBody.concat(callListRows) body: callListTableBody.concat(callListRows)
}, },
layout: this.pdfDocumentService.switchColorTableLayout layout: 'switchColorTableLayout'
}; };
return [title, table]; return [title, table];
} }

View File

@ -2,11 +2,11 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { AbstractPollData, PollPdfService } from 'app/core/pdf-services/base-poll-pdf-service';
import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service'; import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service'; import { UserRepositoryService } from 'app/core/repositories/users/user-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { AbstractPollData, PollPdfService } from 'app/core/ui-services/poll-pdf-service';
import { MotionPoll } from 'app/shared/models/motions/motion-poll'; import { MotionPoll } from 'app/shared/models/motions/motion-poll';
type BallotCountChoices = 'NUMBER_OF_DELEGATES' | 'NUMBER_OF_ALL_PARTICIPANTS' | 'CUSTOM_NUMBER'; type BallotCountChoices = 'NUMBER_OF_DELEGATES' | 'NUMBER_OF_ALL_PARTICIPANTS' | 'CUSTOM_NUMBER';

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service'; import { PdfDocumentService } from 'app/core/pdf-services/pdf-document.service';
import { UserPdfService } from './user-pdf.service'; import { UserPdfService } from './user-pdf.service';
import { ViewUser } from '../models/view-user'; import { ViewUser } from '../models/view-user';

View File

@ -3,7 +3,6 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ConfigService } from 'app/core/ui-services/config.service'; import { ConfigService } from 'app/core/ui-services/config.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { ViewUser } from '../models/view-user'; import { ViewUser } from '../models/view-user';
/** /**
@ -25,13 +24,8 @@ export class UserPdfService {
* *
* @param translate handle translations * @param translate handle translations
* @param configService Read config variables * @param configService Read config variables
* @param pdfDocumentService Global PDF Functions
*/ */
public constructor( public constructor(private translate: TranslateService, private configService: ConfigService) {}
private translate: TranslateService,
private configService: ConfigService,
private pdfDocumentService: PdfDocumentService
) {}
/** /**
* Converts a user to PdfMake doc definition, containing access information * Converts a user to PdfMake doc definition, containing access information
@ -254,7 +248,7 @@ export class UserPdfService {
headerRows: 1, headerRows: 1,
body: userTableBody.concat(this.getListUsers(users)) body: userTableBody.concat(this.getListUsers(users))
}, },
layout: this.pdfDocumentService.switchColorTableLayout layout: 'switchColorTableLayout'
}; };
} }

View File

@ -8,5 +8,5 @@
"enableIvy": false "enableIvy": false
}, },
"include": ["src/**/*.ts"], "include": ["src/**/*.ts"],
"exclude": ["src/test.ts", "**/*.spec.ts"] "exclude": ["src/test.ts", "**/*.spec.ts", "src/**/*.worker.ts"]
} }

View File

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/worker",
"lib": [
"es2018",
"webworker"
],
"types": []
},
"include": [
"src/**/*.worker.ts"
]
}