Convert HTML to Print preview PDF on angularjs and typescript, using SCSS

by @https://github.com/Michaelzvu
Html example to integrate the directive
<ng-print print-element-id=”$ctrl.printElementId”
on-before-print=”$ctrl.onBeforePrint()”></ng-print><div id="{{$ctrl.printElementId}}" ng-if="$ctrl.isReady">
....your print content...
</div>
scss part
@media print {
@page {
size: A4 landscape;
size: auto; /* auto is the initial value */
/* this affects the margin in the printer settings */
margin: 25mm 25mm 25mm 25mm;
}
body {
overflow: visible;
/* this affects the margin on the content before sending to printer */
margin: 0px;
}
avr-dimension-view-header td {
box-sizing: border-box;
}
}
Type script part
import { OnInit, Component, Output, Input } from 'angular-ts-decorators';
import './ngPrint.scss';
const autobind = libavrui.decorators.autobind;
/**
* @ngdoc component
* @param NgPrint
* @internal
*/
@Component({
selector: 'ng-print',
styles: [require('./ngPrint.scss')]
})
export class NgPrint implements OnInit {
@Input() printElementId: string;
@Input() bodyClass: string;
@Input() copyStyles: boolean;
@Input() content: () => void;
@Input() pageStyle: string;
@Output() onBeforePrint: () => void;
@Output() onAfterPrint: () => void;
private linkTotal = 0;
private linksLoaded: any = [];
private linksErrored: any = [];
/*@ngInject*/
constructor(private $scope: ng.IScope,
private $timeout: ng.ITimeoutService) {}
public ngOnInit() {
this.copyStyles = this.copyStyles || true;
this.bodyClass = this.bodyClass || '';
this.$scope.$on('ng-print.printPage', this.printPageChange);
}
@autobind
private printPageChange() {
if (!this.printElementId) {
throw new Error(`ng-print directive - print-element-id attribute is missing`);
}
const elemToPrint = document.getElementById(this.printElementId);
if (!elemToPrint) {
throw new Error(`ng-print directive - there isn't an element with the id: ${this.printElementId}`);
}
this.$timeout(() => this.handlePrint(elemToPrint));
}
private removeWindow(target: any) {
setTimeout(() => {
target.parentNode.removeChild(target);
}, 500);
}
private triggerPrint(target: any) {
if (this.onBeforePrint) {
this.onBeforePrint();
}
setTimeout(() => {
target.contentWindow.focus();
target.contentWindow.print();
this.removeWindow(target);
if (this.onAfterPrint) {
this.onAfterPrint();
}
}, 500);
}
private handlePrint(elemToPrint: any) {
const printWindow = document.createElement('iframe');
printWindow.style.position = 'absolute';
printWindow.style.top = '-1000px';
printWindow.style.left = '-1000px';
// TODO: Uncomment this line if you are going to use canvas on your page
// const contentNodes: any = elemToPrint;
const linkNodes = document.querySelectorAll('link[rel="stylesheet"]');
this.linkTotal = linkNodes.length || 0;
this.linksLoaded = [];
this.linksErrored = [];
const markLoaded = (linkNode: any, loaded: any) => {
if (loaded) {
this.linksLoaded.push(linkNode);
} else {
console.error(`'ng-print' was unable to load a link. It may be invalid.
'ng-print' will continue attempting
to print the page. The link the errored was:`, linkNode); // eslint-disable-line no-console
this.linksErrored.push(linkNode);
}
// We may have errors, but attempt to print anyways - maybe they are trivial and the user will
// be ok ignoring them
if (this.linksLoaded.length + this.linksErrored.length === this.linkTotal) {
this.triggerPrint(printWindow);
}
};
printWindow.onload = () => {
/* IE11 support */
if (window.navigator && window.navigator.userAgent.indexOf('Trident/7.0') > -1) {
printWindow.onload = null;
}
const domDoc = printWindow.contentDocument || printWindow.contentWindow.document;
// TODO: Uncomment this line if you are going to use canvas on your page
// const srcCanvasEls = [...contentNodes.querySelectorAll('canvas')];
domDoc.open();
domDoc.write(elemToPrint.outerHTML);
domDoc.close();
/* remove date/time from top */
const defaultPageStyle = this.pageStyle === undefined
? '@page { size: auto; margin: 0mm; } @media print { body { -webkit-print-color-adjust: exact; } }'
: this.pageStyle;
const styleEl = domDoc.createElement('style');
styleEl.appendChild(domDoc.createTextNode(defaultPageStyle));
domDoc.head.appendChild(styleEl);
if (this.bodyClass.length) {
domDoc.body.classList.add(this.bodyClass);
}
// TODO: Microsoft Edge doing problems with this line
// TODO: Uncomment this line if you are going to use canvas on your page
// const canvasEls: any = domDoc.querySelectorAll('canvas');
// [...canvasEls].forEach((node, index) => {
// node.getContext('2d').drawImage(srcCanvasEls[index], 0, 0);
// });
if (this.copyStyles !== false) {
const headEls: any = document.querySelectorAll('style, link[rel="stylesheet"]');
[...headEls].forEach((node, index) => {
if (node.tagName === 'STYLE') {
const newHeadEl = domDoc.createElement(node.tagName);
if (node.sheet) {
let styleCSS = '';
for (const rule of node.sheet.cssRules) {
styleCSS += `${rule.cssText}\r\n`;
}
newHeadEl.setAttribute('id', `ng-print-${index}`);
newHeadEl.appendChild(domDoc.createTextNode(styleCSS));
domDoc.head.appendChild(newHeadEl);
}
} else {
const attributes = [...node.attributes];
const hrefAttr = attributes.filter(attr => attr.nodeName === 'href');
const hasHref = hrefAttr.length ? !!hrefAttr[0].nodeValue : false;
// Many browsers will do all sorts of weird things if they encounter an empty `href`
// tag (which is invalid HTML). Some will attempt to load the current page. Some will
// attempt to load the page's parent directory. These problems can cause
// `ng-print` to stop without any error being thrown. To avoid such problems we
// simply do not attempt to load these links.
if (hasHref) {
const newHeadEl = domDoc.createElement(node.tagName);
attributes.forEach((attr) => {
newHeadEl.setAttribute(attr.nodeName, attr.nodeValue);
});
newHeadEl.onload = markLoaded.bind(null, newHeadEl, true);
newHeadEl.onerror = markLoaded.bind(null, newHeadEl, false);
domDoc.head.appendChild(newHeadEl);
} else {
console.warn(`'ng-print' encountered a <link> tag with an empty 'href' attribute.
In addition to being invalid HTML, this can cause problems in many browsers,
and so the <link> was not loaded. The <link> is:`, node); // eslint-disable-line no-console
markLoaded(node, true); // `true` because we've already shown a warning for this
}
}
});
}
if (this.linkTotal === 0 || this.copyStyles === false) {
this.triggerPrint(printWindow);
}
};
document.body.appendChild(printWindow);
}
}