File

app/core/services/pdf-download-service/pdf-download.service.ts

Index

Properties
Methods

Constructor

constructor(utilService: UtilService, pubService: PublicationService, labelService: LabelService, dataService: DataResolverService)
Parameters :
Name Type Optional
utilService UtilService No
pubService PublicationService No
labelService LabelService No
dataService DataResolverService No

Methods

Private addAppropriates
addAppropriates(pdf: PdfDocument, row: any)

Adds a row of boxes with appropriates images.

Parameters :
Name Type Optional
pdf PdfDocument No
row any No
Returns : void
Private addImageColumn
addImageColumn(pdf: PdfDocument, column: any)

Adds a column of images.

Parameters :
Name Type Optional
pdf PdfDocument No
column any No
Returns : void
Private addRelatedInfo
addRelatedInfo(pdf: PdfDocument, row: any)

Adds a row of boxes with related info.

Parameters :
Name Type Optional
pdf PdfDocument No
row any No
Returns : void
Private addTableColumn
addTableColumn(pdf: PdfDocument, column: any, width: number)

Adds a column of tables.

Parameters :
Name Type Optional
pdf PdfDocument No
column any No
width number No
Returns : void
Public download
download()

Build the PDF using the data of the current product page and download it when finished.

Returns : void
Private getTermsAndConditions
getTermsAndConditions()

Returns the Terms & Conditions text in a Promise.

Returns : Promise<any>
ngOnInit
ngOnInit()
Returns : void
Private parseRelatedInfoCard
parseRelatedInfoCard(pdf: PdfDocument, element: any, contentWidth)

Parses a card element and fetches the title, info and description, along with its rendered height.

Parameters :
Name Type Optional
pdf PdfDocument No
element any No
contentWidth No
Returns : any

Properties

Public dataService
Type : DataResolverService
headerFooterImg
Type : any
Public labelService
Type : LabelService
notFoundImg
Type : any
printPageGeneratedFromLabel
Type : string
printPageNumberOfPagesLabel
Type : string
Public pubService
Type : PublicationService
Public utilService
Type : UtilService
import { Injectable, OnInit } from '@angular/core';
import { UtilService } from '../util-service/util.service';
import { PdfDocument } from './pdf-document'
import { PublicationService } from '../publication-service/publication.service';
import { DataResolverService } from '../data-resolver/data-resolver.service';
import { LabelService } from '../label-service/label-service.service';


@Injectable({
	providedIn: 'root'
})
export class PdfDownloadService implements OnInit {
	
	headerFooterImg: any;
	notFoundImg: any;

	printPageGeneratedFromLabel: string;
	printPageNumberOfPagesLabel: string;

	constructor(
		public utilService: UtilService,
		public pubService: PublicationService,
		public labelService: LabelService,
		public dataService: DataResolverService
	) {
		// Load local images
		this.headerFooterImg = new Image();
		this.headerFooterImg.src = require('src/v2/assets/img/pdf-header-footer.png');
		this.notFoundImg = new Image();
		this.notFoundImg.src = require('src/v2/assets/img/pdf-not-found.png');

		// Load label texts
		this.labelService.getLabel('printPageGeneratedFrom').then(label => this.printPageGeneratedFromLabel = label);
		this.labelService.getLabel('printPageNumberOfPages').then(label => this.printPageNumberOfPagesLabel = label);
	}

	ngOnInit(): void {
	}

	/**
	 * Adds a column of tables.
	 */
	private addTableColumn(pdf: PdfDocument, column: any, width: number): void {

		const tableMarginBottom = 10;
		pdf.cursorX = pdf.pageWidth - pdf.margins.left - width;

		let tables = column.querySelectorAll('table');
		for (let tableIndex = 0; tableIndex < tables.length; tableIndex++) {
			let table = tables[tableIndex];

			// Caption
			let caption = '';
			let captionElement = table.querySelector('.caption')
			if (captionElement) {
				caption = captionElement.innerHTML;
			} else {
				caption = table.parentNode.querySelector('h3').textContent;
			}

			pdf.addTable(table, caption, width);
		}
		pdf.cursorY += tableMarginBottom;
	}

	/**
	 * Adds a column of images.
	 */
	private addImageColumn(pdf: PdfDocument, column: any): void {

		// Spacing
		const imageMarginLeftRight = 50;
		const imageMarginTopBottom = 30;
		const imageWidth = pdf.pageWidth / 2 - imageMarginLeftRight * 2;
		const imageHeight = 130;
		const marginBottom = 40;
		pdf.cursorX = pdf.pageWidth / 4 - imageWidth / 2;
		
		// Parse imgs
		let images = column.querySelectorAll('img');
		for (let imageIndex = 0; imageIndex < images.length; imageIndex++) {
			pdf.cursorY += imageMarginTopBottom;
			try {
				pdf.addImage(images[imageIndex], imageWidth, imageHeight);
			} catch (Error) {
				pdf.addImage(this.notFoundImg, imageWidth, imageHeight);
			}
			pdf.cursorY += imageMarginTopBottom;
		}
		pdf.cursorY += marginBottom;
	}

	/**
	 * Adds a row of boxes with appropriates images.
	 */
	private addAppropriates(pdf: PdfDocument, row: any): void {

		// Spacing
		const cardsPerRow = 4;
		const cardMargin = 20
		const cardWidth = (pdf.pageWidth - pdf.margins.left * 2 - cardMargin * (cardsPerRow - 1)) / cardsPerRow;
		const cardPaddingRightLeft = 30;
		const cardPaddingTop = 20;
		const cardPaddingBottom = 30;
		const marginBottom = 40;
		pdf.cursorX = pdf.margins.left;

		// Caption
		let caption = row.querySelector('h3').textContent;
		pdf.addText(caption, 12);

		// Cards
		let cards = row.querySelectorAll('.card');
		for (let cardIndex = 0; cardIndex < cards.length; cardIndex++) {
			let image = cards[cardIndex].querySelector('img');
			let name = cards[cardIndex].querySelector('.card-body').textContent.trim();
			pdf.addBox(pdf.cursorX, pdf.cursorY, cardWidth, cardWidth);
			pdf.addImageAt(image, pdf.cursorX + cardPaddingRightLeft, pdf.cursorY + cardPaddingTop, cardWidth - cardPaddingRightLeft * 2, cardWidth / 2);
			pdf.addTextAt(name, pdf.cursorX + cardWidth / 2, pdf.cursorY + cardWidth - cardPaddingBottom, 12, '#282828', 'center');

			// Advance
			if (cardIndex % cardsPerRow == cardsPerRow - 1 || cardIndex == cards.length - 1) {
				pdf.cursorX = pdf.margins.left;
				if (!pdf.pageBreak(cardWidth + cardMargin)) {
					pdf.cursorY += cardWidth + cardMargin;
				}
			} else {
				pdf.cursorX += cardWidth + cardMargin;
			}
		}
		pdf.cursorY += marginBottom;
	}


	/**
	 * Parses a card element and fetches the title, info and description, along with its rendered height.
	 **/
	private parseRelatedInfoCard(pdf: PdfDocument, element: any, contentWidth): any {
		
		let contentHeight = 0;

		// Card title
		let title = element.querySelector('.card-header span').innerText;
		if (title != '') {
			contentHeight += 25;
		}

		// Card description
		const fontSize = 10;
		const spaceSize = 2;
		let descLines = [];
		let descElement = element.querySelector('.card-body>.card-text>p');
		if (descElement) {
			descLines = pdf.wordWrap(descElement.innerText, contentWidth);
			contentHeight += descLines.length * (fontSize + spaceSize);
		}

		// Card links
		let linkElements = element.querySelectorAll('a.list-group-item');
		let links = [];
		if (linkElements.length) {
			for (let linkIndex = 0; linkIndex < linkElements.length; linkIndex++) {
				
				let link = linkElements[linkIndex];
				let titleElement = link.querySelector('.link-group-title');
				let descElement = link.querySelector('p');

				let height = 5;
				height += fontSize;
				if (descElement)
					height += fontSize + 5;
				height += 5;
				
				// Get absolute link
				let absLink = document.createElement('a');
				absLink.href = link.getAttribute('href');
				
				links.push({ 
					title: titleElement.innerText,
					href: absLink.href,
					desc: descElement ? descElement.innerText : '',
					height: height
				})
				contentHeight += height;
			}
			contentHeight += 10;
		}

		if (descLines.length || links.length) {
			return { title: title, descLines: descLines, links: links, height: contentHeight };
		}

		return null;
	}

	/**
	 * Adds a row of boxes with related info.
	 **/
	private addRelatedInfo(pdf: PdfDocument, row: any): void {

		// Cards
		let cards = row.querySelectorAll('.card');
		const cardWidth = (pdf.pageWidth - pdf.margins.left * 2) / cards.length;
		const cardPadding = 20;
		const marginBottom = 40;
		const linkColor = '#0f58d6';

		// Find content and height
		let cardContent = [];
		let cardContentHeight = 0;

		for (let cardIndex = 0; cardIndex < cards.length; cardIndex++) {
			let content = this.parseRelatedInfoCard(pdf, cards[cardIndex], cardWidth - cardPadding * 2);
			if (content) {
				cardContent.push(content);
				cardContentHeight = Math.max(cardContentHeight, content.height);
			}
		}

		// No cards with renderable content, skip
		if (cardContent.length == 0) {
			return;
		}

		// Render
		const cardHeight = cardContentHeight + cardPadding * 2;
		pdf.pageBreak(cardHeight + 15)

		// Header
		let headerElement = row.parentNode.querySelector('h2'); // Preceding <h2>
		if (headerElement) {
			pdf.addText(headerElement.textContent, 12);
		}

		// Boxes
		for (let contentIndex = 0; contentIndex < cardContent.length; contentIndex++) {

			let contentY = cardPadding;
			let content = cardContent[contentIndex];
			pdf.addBox(pdf.cursorX, pdf.cursorY, cardWidth, cardHeight);

			// Title
			pdf.addTextAt(content.title, pdf.cursorX + cardPadding, pdf.cursorY + contentY, 12);
			contentY += 25;

			// Links
			if (content.links.length) {
				for (let linkIndex = 0; linkIndex < content.links.length; linkIndex++) {
					pdf.addLink(pdf.cursorX + cardPadding, pdf.cursorY + contentY,
							    cardWidth - cardPadding * 2, content.links[linkIndex].height, content.links[linkIndex].href);
					pdf.addLine(pdf.cursorX + cardPadding, pdf.cursorY + contentY + content.links[linkIndex].height,
								pdf.cursorX + cardWidth - cardPadding * 2, pdf.cursorY + contentY + content.links[linkIndex].height);
					pdf.addTextAt(content.links[linkIndex].title, pdf.cursorX + cardPadding + 5, pdf.cursorY + contentY + 5, 10, linkColor);
					pdf.addTextAt(content.links[linkIndex].desc, pdf.cursorX + cardPadding + 5, pdf.cursorY + contentY + 20, 10);
					contentY += content.links[linkIndex].height;
				}
				contentY += 10;
			}

			// Description
			if (content.descLines.length) {
				pdf.addTextAt(content.descLines, pdf.cursorX + cardPadding, pdf.cursorY + contentY, 10);
			}

			pdf.cursorX += cardWidth;
		}

		pdf.cursorX = pdf.margins.left;
		pdf.cursorY += cardHeight + marginBottom;
	}

	/**
	 * Returns the Terms & Conditions text in a Promise.
	 */
	private getTermsAndConditions(): Promise<any>
	{
		const pubPath = this.pubService.getPublicationPath();
		const pageUrl = `${pubPath}/footer/terms-and-conditions`;
		return new Promise((resolve, reject) => {
			this.dataService.getPageData(pageUrl).then(page => {
				// Get HTML from extracted data
				let html = this.utilService.extract(page, 'Regions', region => region.Name === 'main', 'Entities', [0], 'EmbeddedBodytext', [0], 'BodyText');
				
				// Fix spacing
				html = html.replace(/<br ? \/?>\n/g, "<br />");
				html = html.replace(/<br ? \/?>/g, "<br />\n");
				html = html.replace(/<\/strong>\n/g, "</strong>");
				html = html.replace(/<\/strong>/g, "</strong>\n");

				// Convert HTML to text
				let tempDiv = document.createElement('div');
				tempDiv.innerHTML = html;
				resolve(tempDiv.textContent);
			});
		});
	}

	/**
	 * Build the PDF using the data of the current product page and download it when finished.
	 */
	public download(): void {

		// Spacing
		const pdfMarginTop = 80;
		const pdfMarginBottom = 60;
		const pdfMarginLeft = 30;

		// Create document
		let pdf = new PdfDocument({
			top: pdfMarginTop,
			bottom: pdfMarginBottom,
			left: pdfMarginLeft
		});

		// Page header & footer images
		pdf.addPerPage((page: number) => {
		
			// Header
			pdf.addImageAt(this.headerFooterImg, 0, 0, pdf.pageWidth, 35);

			// Generated on
			let generatedText = this.printPageGeneratedFromLabel;
			generatedText = generatedText.replace(/\{site\}/, window.location.hostname);
			generatedText = generatedText.replace(/\{date\}/, this.utilService.getCurrentDate('-'));
			pdf.addTextAt(generatedText, pdf.pageWidth - pdf.margins.left, 14, 10, 'white', 'right');

			// Footer
			pdf.addImageAt(this.headerFooterImg, 0, pdf.pageHeight - 35, pdf.pageWidth, 35);

			// Page counter
			let pageCounterText = this.printPageNumberOfPagesLabel;
			pageCounterText = pageCounterText.replace(/\{page\}/, String(page));
			pageCounterText = pageCounterText.replace(/\{total\}/, String(pdf.getTotalPages()));
			pdf.addTextAt(pageCounterText, pdf.pageWidth - pdf.margins.left, pdf.pageHeight - 22, 10, 'white', 'right');
		});

		// Locations of top image and text
		const cadImageX = 60;
		const cadImageY = 60;
		const cadImageWidth = 200;
		const cadImageHeight = 200;
		const textX = 290;
		const textY = 80;
		const textMarginBottom = 60;

		// CAD image
		try {
			pdf.addImageAt(document.querySelector('.pdf-picture'), cadImageX, cadImageY, cadImageWidth, cadImageHeight);
		} catch (Error) {
			pdf.addImageAt(this.notFoundImg, cadImageX, cadImageY, cadImageWidth, cadImageHeight);
		}

		// Top info
		let title = document.querySelector('.pdf-title').textContent.trim();
		let category = document.querySelector('.pdf-category').textContent.trim();
		let description = document.querySelector('.pdf-description').textContent.trim();
		pdf.cursorX = textX;
		pdf.cursorY = textY;
		pdf.addText(title, 20);
		pdf.addText(category, 16);
		pdf.addText(description, 14);

		// Headline
		const headlineMinY = 220;
		const headlineMarginBottom = 10;

		pdf.cursorX = pdf.margins.left;
		pdf.cursorY = Math.max(headlineMinY, pdf.cursorY) + textMarginBottom;
		let headline = document.querySelector('.headline').textContent;
		pdf.addText(headline, 16);
		pdf.cursorY += headlineMarginBottom;

		// Add content blocks
		let pdfBlocks = document.querySelectorAll('content-block');
		for (let blockIndex = 0; blockIndex < pdfBlocks.length; blockIndex++) {

			let blockRows = pdfBlocks[blockIndex].querySelectorAll('.row');
			for (let rowIndex = 0; rowIndex < blockRows.length; rowIndex++) {

				// Appropriates
				let appr = blockRows[rowIndex].querySelector('.appropriates');
				if (appr) {
					this.addAppropriates(pdf, appr);
					continue;
				}

				// Full width table
				let children = blockRows[rowIndex].children;
				if (children.length == 1) {
					pdf.cursorX = pdf.margins.left;
					this.addTableColumn(pdf, children[0], pdf.pageWidth - pdf.margins.left * 2);

				// Tables and images at half width
				} else if (children.length == 2) {
					
					// Tables
					let tableStartCursorY = pdf.cursorY;
					let tableStartPage = pdf.getCurrentPage();
					pdf.cursorX = pdf.pageWidth / 2;
					this.addTableColumn(pdf, children[1], pdf.pageWidth / 2 - pdf.margins.left);
					let tableEndCursorY = pdf.cursorY;
					let tableEndPage = pdf.getCurrentPage();
					
					// Jump back to top
					pdf.setCurrentPage(tableStartPage);
					pdf.cursorY = tableStartCursorY;

					// Images
					this.addImageColumn(pdf, children[0]);

					// Set cursor to the end of either the table or the images, whichever comes last
					if (pdf.getCurrentPage() < tableEndPage) {
						pdf.setCurrentPage(tableEndPage);
						pdf.cursorY = tableEndCursorY;
					} else if (pdf.getCurrentPage() == tableEndPage) {
						pdf.cursorY = Math.max(pdf.cursorY, tableEndCursorY);
					}
				}
			}
		}

		// Add related info
		let infoCards = document.querySelectorAll('.card-group');
		for (let cardIndex = 0; cardIndex < infoCards.length; cardIndex++) {
			this.addRelatedInfo(pdf, infoCards[cardIndex]);
		}

		this.getTermsAndConditions().then(terms => {
			
			// Terms and conditions on new page
			pdf.addPage();
			pdf.setCurrentPage(pdf.getTotalPages());
			pdf.addTextAt(terms, pdf.margins.left, pdf.margins.top, 10);

			// Finally download
			let filename = title + '_' + this.utilService.getCurrentDate() + '.pdf';
			pdf.save(filename);
		});
	}
}

result-matching ""

    No results matching ""