diff --git a/client/src/app/core/services/pdf-document.service.ts b/client/src/app/core/services/pdf-document.service.ts index b934f517d..6387fe248 100644 --- a/client/src/app/core/services/pdf-document.service.ts +++ b/client/src/app/core/services/pdf-document.service.ts @@ -8,15 +8,6 @@ import { TranslateService } from '@ngx-translate/core'; import { ConfigService } from './config.service'; import { HttpService } from './http.service'; -/** - * An interface for the mapping of image placeholder name to the url of the - * image - */ -export interface ImagePlaceHolder { - placeholder: string; - url: string; -} - /** * Provides the general document structure for PDF documents, such as page margins, header, footer and styles. * Also provides general purpose open and download functions. @@ -34,6 +25,12 @@ export interface ImagePlaceHolder { providedIn: 'root' }) export class PdfDocumentService { + /** + * A list of all images to add to the virtual file system. + * May still be filling at header and footer creation + */ + private imageUrls: string[] = []; + /** * Constructor * @@ -48,13 +45,11 @@ export class PdfDocumentService { ) {} /** - * Define the pdfmake virtual file system for fonts + * Define the pdfmake virtual file system, adding the fonts * - * @param images an optional mapping of images urls to be fetched and inserted - * into placeholders * @returns the vfs-object */ - private async initVfs(images?: ImagePlaceHolder[]): Promise { + private async initVfs(): Promise { const fontPathList: string[] = Array.from( // create a list without redundancies new Set( @@ -72,17 +67,7 @@ export class PdfDocumentService { }; }); }); - let imagePromises = []; - if (images && images.length) { - imagePromises = images.map(image => { - return this.convertUrlToBase64(image.url).then(base64 => { - return { - [image.placeholder]: base64 - }; - }); - }); - } - const binaryDataUrls = await Promise.all(promises.concat(imagePromises)); + const binaryDataUrls = await Promise.all(promises); let vfs = {}; binaryDataUrls.map(entry => { vfs = { @@ -99,7 +84,7 @@ export class PdfDocumentService { * @param url file url * @returns a promise with a base64 string */ - public async convertUrlToBase64(url: string): Promise { + private async convertUrlToBase64(url: string): Promise { return new Promise((resolve, reject) => { const headers = new HttpHeaders(); this.httpService.get(url, {}, {}, headers, 'blob').then(file => { @@ -133,17 +118,14 @@ export class PdfDocumentService { * * @param documentContent the content of the pdf as object * @param metadata - * @param images Array of optional images (url, placeholder) to be inserted + * @param imageUrls Array of optional images (url, placeholder) to be inserted * @returns the pdf document definition ready to export */ - private async getStandardPaper( - documentContent: object, - metadata?: object, - images?: ImagePlaceHolder[] - ): Promise { + private async getStandardPaper(documentContent: object, metadata?: object, imageUrls?: string[]): Promise { this.initFonts(); - pdfMake.vfs = await this.initVfs(images); - return { + this.imageUrls = imageUrls ? imageUrls : []; + pdfMake.vfs = await this.initVfs(); + const result = { pageSize: 'A4', pageMargins: [75, 90, 75, 75], defaultStyle: { @@ -157,9 +139,10 @@ export class PdfDocumentService { }, info: metadata, content: documentContent, - styles: this.getStandardPaperStyles(), - images: this.getImageUrls() + styles: this.getStandardPaperStyles() }; + await this.loadAllImages(); + return result; } /** @@ -167,27 +150,29 @@ export class PdfDocumentService { * (e.g. ballots) * * @param documentContent the content of the pdf as object - * @param image an optional image to insert into the ballot + * @param imageUrl an optional image to insert into the ballot * @returns the pdf document definition ready to export */ - private async getBallotPaper(documentContentObject: object, image?: ImagePlaceHolder): Promise { - const images = image ? [image] : null; + private async getBallotPaper(documentContent: object, imageUrl?: string): Promise { this.initFonts(); - pdfMake.vfs = await this.initVfs(images); - return { + this.imageUrls = imageUrl ? [imageUrl] : []; + pdfMake.vfs = await this.initVfs(); + const result = { pageSize: 'A4', pageMargins: [0, 0, 0, 0], defaultStyle: { font: 'PdfFont', fontSize: 10 }, - content: documentContentObject, + content: documentContent, styles: this.getBlankPaperStyles() }; + await this.loadAllImages(); + return result; } /** - * Define fonts + * Define the fonts */ private initFonts(): void { pdfMake.fonts = { @@ -222,6 +207,7 @@ export class PdfDocumentService { fit: [180, 40], width: '20%' }); + this.imageUrls.push(logoHeaderLeftUrl); } // add the header text if no logo on the right was specified @@ -259,6 +245,7 @@ export class PdfDocumentService { alignment: 'right', width: '20%' }); + this.imageUrls.push(logoHeaderRightUrl); } return { @@ -284,17 +271,17 @@ export class PdfDocumentService { const columns = []; let logoContainerWidth: string; let pageNumberPosition: string; - let logoConteinerSize: Array; + let logoContainerSize: Array; let logoFooterLeftUrl = this.configService.instant('logo_pdf_footer_L').path; let logoFooterRightUrl = this.configService.instant('logo_pdf_footer_R').path; // if there is a single logo, give it a lot of space if (logoFooterLeftUrl && logoFooterRightUrl) { logoContainerWidth = '20%'; - logoConteinerSize = [180, 40]; + logoContainerSize = [180, 40]; } else { logoContainerWidth = '80%'; - logoConteinerSize = [400, 50]; + logoContainerSize = [400, 50]; } // the position of the page number depends on the logos @@ -315,10 +302,11 @@ export class PdfDocumentService { } columns.push({ image: logoFooterLeftUrl, - fit: logoConteinerSize, + fit: logoContainerSize, width: logoContainerWidth, alignment: 'left' }); + this.imageUrls.push(logoFooterLeftUrl); } // add the page number @@ -335,10 +323,11 @@ export class PdfDocumentService { } columns.push({ image: logoFooterRightUrl, - fit: logoConteinerSize, + fit: logoContainerSize, width: logoContainerWidth, alignment: 'right' }); + this.imageUrls.push(logoFooterRightUrl); } return { @@ -378,8 +367,7 @@ export class PdfDocumentService { * @param logo (optional) url of a logo to be placed as ballot logo */ public downloadWithBallotPaper(docDefinition: object, filename: string, logo?: string): void { - const images: ImagePlaceHolder = logo ? { placeholder: 'ballot-logo', url: logo } : null; - this.getBallotPaper(docDefinition, images).then(doc => { + this.getBallotPaper(docDefinition, logo).then(doc => { this.createPdf(doc, filename); }); } @@ -396,18 +384,6 @@ export class PdfDocumentService { }); } - /** - * TODO - * - * Should create an images section in the document definition holding the base64 strings - * for the urls - * - * @returns an object containing the image names and the corresponding base64 strings - */ - private getImageUrls(): object { - return {}; - } - /** * Definition of styles for standard papers * @@ -493,4 +469,27 @@ export class PdfDocumentService { } }; } + + /** + * Triggers the addition of all images found during creation(including header and footer) + * to the vfs. + */ + private async loadAllImages(): Promise { + const promises = this.imageUrls.map(image => { + return this.addImageToVfS(image); + }); + await Promise.all(promises); + } + + /** + * Creates an image in the pdfMake virtual file system, if it doesn't yet exist there + * + * @param url + */ + private async addImageToVfS(url: string): Promise { + if (!pdfMake.vfs[url]) { + const base64 = await this.convertUrlToBase64(url); + pdfMake.vfs[url] = base64; + } + } } diff --git a/client/src/app/site/motions/services/motion-poll-pdf.service.ts b/client/src/app/site/motions/services/motion-poll-pdf.service.ts index 023c43454..28e34c63c 100644 --- a/client/src/app/site/motions/services/motion-poll-pdf.service.ts +++ b/client/src/app/site/motions/services/motion-poll-pdf.service.ts @@ -71,6 +71,9 @@ export class MotionPollPdfService { this.configService.get('general_event_name').subscribe(name => (this.eventName = name)); this.configService.get('logo_pdf_ballot_paper').subscribe(url => { if (url && url.path) { + if (url.path.indexOf('/') === 0) { + url.path = url.path.substr(1); // remove prepending slash + } this.logo = url.path; } }); @@ -218,7 +221,7 @@ export class MotionPollPdfService { if (this.logo) { columns.push({ - image: 'ballot-logo', // fixed dummy name not used outside ballot creation + image: this.logo, fit: [90, 25], alignment: 'right', width: '40%'