Merge pull request #4186 from MaximilianKrambach/pdflogos
add pdf header and footer images
This commit is contained in:
commit
793066935e
@ -8,15 +8,6 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { ConfigService } from './config.service';
|
import { ConfigService } from './config.service';
|
||||||
import { HttpService } from './http.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.
|
* Provides the general document structure for PDF documents, such as page margins, header, footer and styles.
|
||||||
* Also provides general purpose open and download functions.
|
* Also provides general purpose open and download functions.
|
||||||
@ -34,6 +25,12 @@ export interface ImagePlaceHolder {
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class PdfDocumentService {
|
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
|
* 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
|
* @returns the vfs-object
|
||||||
*/
|
*/
|
||||||
private async initVfs(images?: ImagePlaceHolder[]): Promise<object> {
|
private async initVfs(): Promise<object> {
|
||||||
const fontPathList: string[] = Array.from(
|
const fontPathList: string[] = Array.from(
|
||||||
// create a list without redundancies
|
// create a list without redundancies
|
||||||
new Set(
|
new Set(
|
||||||
@ -72,17 +67,7 @@ export class PdfDocumentService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
let imagePromises = [];
|
const binaryDataUrls = await Promise.all(promises);
|
||||||
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));
|
|
||||||
let vfs = {};
|
let vfs = {};
|
||||||
binaryDataUrls.map(entry => {
|
binaryDataUrls.map(entry => {
|
||||||
vfs = {
|
vfs = {
|
||||||
@ -99,7 +84,7 @@ export class PdfDocumentService {
|
|||||||
* @param url file url
|
* @param url file url
|
||||||
* @returns a promise with a base64 string
|
* @returns a promise with a base64 string
|
||||||
*/
|
*/
|
||||||
public async convertUrlToBase64(url: string): Promise<string> {
|
private async convertUrlToBase64(url: string): Promise<string> {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
const headers = new HttpHeaders();
|
const headers = new HttpHeaders();
|
||||||
this.httpService.get<Blob>(url, {}, {}, headers, 'blob').then(file => {
|
this.httpService.get<Blob>(url, {}, {}, headers, 'blob').then(file => {
|
||||||
@ -133,17 +118,14 @@ export class PdfDocumentService {
|
|||||||
*
|
*
|
||||||
* @param documentContent the content of the pdf as object
|
* @param documentContent the content of the pdf as object
|
||||||
* @param metadata
|
* @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
|
* @returns the pdf document definition ready to export
|
||||||
*/
|
*/
|
||||||
private async getStandardPaper(
|
private async getStandardPaper(documentContent: object, metadata?: object, imageUrls?: string[]): Promise<object> {
|
||||||
documentContent: object,
|
|
||||||
metadata?: object,
|
|
||||||
images?: ImagePlaceHolder[]
|
|
||||||
): Promise<object> {
|
|
||||||
this.initFonts();
|
this.initFonts();
|
||||||
pdfMake.vfs = await this.initVfs(images);
|
this.imageUrls = imageUrls ? imageUrls : [];
|
||||||
return {
|
pdfMake.vfs = await this.initVfs();
|
||||||
|
const result = {
|
||||||
pageSize: 'A4',
|
pageSize: 'A4',
|
||||||
pageMargins: [75, 90, 75, 75],
|
pageMargins: [75, 90, 75, 75],
|
||||||
defaultStyle: {
|
defaultStyle: {
|
||||||
@ -157,9 +139,10 @@ export class PdfDocumentService {
|
|||||||
},
|
},
|
||||||
info: metadata,
|
info: metadata,
|
||||||
content: documentContent,
|
content: documentContent,
|
||||||
styles: this.getStandardPaperStyles(),
|
styles: this.getStandardPaperStyles()
|
||||||
images: this.getImageUrls()
|
|
||||||
};
|
};
|
||||||
|
await this.loadAllImages();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,27 +150,29 @@ export class PdfDocumentService {
|
|||||||
* (e.g. ballots)
|
* (e.g. ballots)
|
||||||
*
|
*
|
||||||
* @param documentContent the content of the pdf as object
|
* @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
|
* @returns the pdf document definition ready to export
|
||||||
*/
|
*/
|
||||||
private async getBallotPaper(documentContentObject: object, image?: ImagePlaceHolder): Promise<object> {
|
private async getBallotPaper(documentContent: object, imageUrl?: string): Promise<object> {
|
||||||
const images = image ? [image] : null;
|
|
||||||
this.initFonts();
|
this.initFonts();
|
||||||
pdfMake.vfs = await this.initVfs(images);
|
this.imageUrls = imageUrl ? [imageUrl] : [];
|
||||||
return {
|
pdfMake.vfs = await this.initVfs();
|
||||||
|
const result = {
|
||||||
pageSize: 'A4',
|
pageSize: 'A4',
|
||||||
pageMargins: [0, 0, 0, 0],
|
pageMargins: [0, 0, 0, 0],
|
||||||
defaultStyle: {
|
defaultStyle: {
|
||||||
font: 'PdfFont',
|
font: 'PdfFont',
|
||||||
fontSize: 10
|
fontSize: 10
|
||||||
},
|
},
|
||||||
content: documentContentObject,
|
content: documentContent,
|
||||||
styles: this.getBlankPaperStyles()
|
styles: this.getBlankPaperStyles()
|
||||||
};
|
};
|
||||||
|
await this.loadAllImages();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define fonts
|
* Define the fonts
|
||||||
*/
|
*/
|
||||||
private initFonts(): void {
|
private initFonts(): void {
|
||||||
pdfMake.fonts = {
|
pdfMake.fonts = {
|
||||||
@ -222,6 +207,7 @@ export class PdfDocumentService {
|
|||||||
fit: [180, 40],
|
fit: [180, 40],
|
||||||
width: '20%'
|
width: '20%'
|
||||||
});
|
});
|
||||||
|
this.imageUrls.push(logoHeaderLeftUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the header text if no logo on the right was specified
|
// add the header text if no logo on the right was specified
|
||||||
@ -259,6 +245,7 @@ export class PdfDocumentService {
|
|||||||
alignment: 'right',
|
alignment: 'right',
|
||||||
width: '20%'
|
width: '20%'
|
||||||
});
|
});
|
||||||
|
this.imageUrls.push(logoHeaderRightUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -284,17 +271,17 @@ export class PdfDocumentService {
|
|||||||
const columns = [];
|
const columns = [];
|
||||||
let logoContainerWidth: string;
|
let logoContainerWidth: string;
|
||||||
let pageNumberPosition: string;
|
let pageNumberPosition: string;
|
||||||
let logoConteinerSize: Array<number>;
|
let logoContainerSize: Array<number>;
|
||||||
let logoFooterLeftUrl = this.configService.instant<any>('logo_pdf_footer_L').path;
|
let logoFooterLeftUrl = this.configService.instant<any>('logo_pdf_footer_L').path;
|
||||||
let logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
|
let logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
|
||||||
|
|
||||||
// if there is a single logo, give it a lot of space
|
// if there is a single logo, give it a lot of space
|
||||||
if (logoFooterLeftUrl && logoFooterRightUrl) {
|
if (logoFooterLeftUrl && logoFooterRightUrl) {
|
||||||
logoContainerWidth = '20%';
|
logoContainerWidth = '20%';
|
||||||
logoConteinerSize = [180, 40];
|
logoContainerSize = [180, 40];
|
||||||
} else {
|
} else {
|
||||||
logoContainerWidth = '80%';
|
logoContainerWidth = '80%';
|
||||||
logoConteinerSize = [400, 50];
|
logoContainerSize = [400, 50];
|
||||||
}
|
}
|
||||||
|
|
||||||
// the position of the page number depends on the logos
|
// the position of the page number depends on the logos
|
||||||
@ -315,10 +302,11 @@ export class PdfDocumentService {
|
|||||||
}
|
}
|
||||||
columns.push({
|
columns.push({
|
||||||
image: logoFooterLeftUrl,
|
image: logoFooterLeftUrl,
|
||||||
fit: logoConteinerSize,
|
fit: logoContainerSize,
|
||||||
width: logoContainerWidth,
|
width: logoContainerWidth,
|
||||||
alignment: 'left'
|
alignment: 'left'
|
||||||
});
|
});
|
||||||
|
this.imageUrls.push(logoFooterLeftUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the page number
|
// add the page number
|
||||||
@ -335,10 +323,11 @@ export class PdfDocumentService {
|
|||||||
}
|
}
|
||||||
columns.push({
|
columns.push({
|
||||||
image: logoFooterRightUrl,
|
image: logoFooterRightUrl,
|
||||||
fit: logoConteinerSize,
|
fit: logoContainerSize,
|
||||||
width: logoContainerWidth,
|
width: logoContainerWidth,
|
||||||
alignment: 'right'
|
alignment: 'right'
|
||||||
});
|
});
|
||||||
|
this.imageUrls.push(logoFooterRightUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -378,8 +367,7 @@ export class PdfDocumentService {
|
|||||||
* @param logo (optional) url of a logo to be placed as ballot logo
|
* @param logo (optional) url of a logo to be placed as ballot logo
|
||||||
*/
|
*/
|
||||||
public downloadWithBallotPaper(docDefinition: object, filename: string, logo?: string): void {
|
public downloadWithBallotPaper(docDefinition: object, filename: string, logo?: string): void {
|
||||||
const images: ImagePlaceHolder = logo ? { placeholder: 'ballot-logo', url: logo } : null;
|
this.getBallotPaper(docDefinition, logo).then(doc => {
|
||||||
this.getBallotPaper(docDefinition, images).then(doc => {
|
|
||||||
this.createPdf(doc, filename);
|
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
|
* 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<void> {
|
||||||
|
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<void> {
|
||||||
|
if (!pdfMake.vfs[url]) {
|
||||||
|
const base64 = await this.convertUrlToBase64(url);
|
||||||
|
pdfMake.vfs[url] = base64;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,9 @@ export class MotionPollPdfService {
|
|||||||
this.configService.get('general_event_name').subscribe(name => (this.eventName = name));
|
this.configService.get('general_event_name').subscribe(name => (this.eventName = name));
|
||||||
this.configService.get('logo_pdf_ballot_paper').subscribe(url => {
|
this.configService.get('logo_pdf_ballot_paper').subscribe(url => {
|
||||||
if (url && url.path) {
|
if (url && url.path) {
|
||||||
|
if (url.path.indexOf('/') === 0) {
|
||||||
|
url.path = url.path.substr(1); // remove prepending slash
|
||||||
|
}
|
||||||
this.logo = url.path;
|
this.logo = url.path;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -218,7 +221,7 @@ export class MotionPollPdfService {
|
|||||||
|
|
||||||
if (this.logo) {
|
if (this.logo) {
|
||||||
columns.push({
|
columns.push({
|
||||||
image: 'ballot-logo', // fixed dummy name not used outside ballot creation
|
image: this.logo,
|
||||||
fit: [90, 25],
|
fit: [90, 25],
|
||||||
alignment: 'right',
|
alignment: 'right',
|
||||||
width: '40%'
|
width: '40%'
|
||||||
|
Loading…
Reference in New Issue
Block a user