from-data-to-pdf
Version:
This library converts html files, URL and character string from html files to PDF files or PDF buffer.
373 lines (328 loc) • 11.7 kB
text/typescript
import * as puppeteer from "puppeteer";
import { Browser } from "puppeteer";
import * as fs from "fs";
import * as pathModule from "path"
/**
* @author DOUAL Sofian
* @description Return a browser.
*
* @export
* @returns { Promise<Browser> }
*/
export async function initBrowser(): Promise<Browser> {
try {
// console.log('BROWSER IS INIT');
return await puppeteer.launch();
}
catch (error) {
throw new Error(error);
}
}
/**
* @author DOUAL Sofian
* @description Close given browser.
*
* @export
* @param { Browser } browser
* @returns { Promise<void> }
*/
export async function closeBrowser(browser: Browser): Promise<void> {
try {
// console.log('BROWSER IS CLOSED');
await browser.close();
return;
}
catch (error) {
throw new Error(error);
}
}
/**
* @author DOUAL Sofian
* @description If path is undefined, returns an array of PDFS buffer. Otherwise, return the path of the saved PDF files.
*
* @export
* @param { Browser } browser
* @param { FileBuffer[] } targets
* @param { string } [path]
* @returns {Promise<FileBuffer>}
*/
export async function generateBuffer(browser: Browser, targets: FileBuffer[], path?: string): Promise<FileBuffer[]> {
try {
const page = await browser.newPage();
const targetMax: number = targets.length;
for (let i = 0; i < targetMax; i++) {
if (!targets[i].name) {
if (targets[i].url) delete targets[i].url;
else if (targets[i].text) delete targets[i].text;
else if (targets[i].options) delete targets[i].options
targets[i]['error'] = new Error('Projects need name.')
continue;
}
if (targets[i].url) {
await page.goto(
targets[i].url as string,
{
waitUntil: 'networkidle0'
}
);
delete targets[i].url;
}
else if (targets[i].text) {
await page.setContent(
targets[i].text as string,
{
waitUntil: "networkidle0"
}
);
delete targets[i].text;
}
else {
continue;
}
// define the default pdf options.
if (!targets[i].options) {
targets[i]['options'] = {
printBackground: true,
preferCSSPageSize: true,
displayHeaderFooter: true
};
}
if (path) {
const pdfName = pathModule.join(path, (targets[i].name.split(' ').join('-') + Date.now().toString() + '.pdf'));
(targets[i].options as puppeteer.PDFOptions)['path'] = pdfName;
if (targets[i].url) delete targets[i].url;
else if (targets[i].text) delete targets[i].text;
await page.pdf(targets[i].options as puppeteer.PDFOptions);
targets[i]['pathOfsavedFile'] = pdfName;
delete targets[i].options;
}
else {
targets[i]['buffer'] = await page.pdf(targets[i].options as puppeteer.PDFOptions);
delete targets[i].options;
}
}
return targets;
}
catch (error) {
throw new Error(error);
}
}
/**
* @author DOUAL Sofian
* @description Save the buffer as a PDF file in <My project>/temp/generatedPDF/.
*
* @export
* @param { FileBuffer[] } targets
* @returns { Promise<FileBuffer[]> }
*/
export async function savePDF(targets: FileBuffer[]): Promise<FileBuffer[]> {
const targetMax = targets.length;
for (let i = 0; i < targetMax; i++) {
try {
if (!targets[i].name) throw new Error("Projects need name.");
let path: any= __filename.split('\\node_modules');
path = (path.length > 1 ? path[0] : path[0].split('\\dist')[0]) + '/temp/generatedPDF/';
const pdfName = path + targets[i].name.split(' ').join('-') + Date.now().toString() + '.pdf';
const pdf = await fs.promises.open(pdfName, 'a');
await pdf.appendFile(targets[i].buffer as Buffer);
pdf.close();
targets[i]['pathOfsavedFile'] = pdfName;
delete targets[i].buffer;
} catch (error) {
delete targets[i].buffer;
targets[i]['error'] = error;
continue;
}
}
return targets;
}
/**
* @author DOUAL Sofian
* @description Create folders ./temp, ./temp/target, ./temp/generatedPDF
*
* @returns { Promise<boolean> }
*/
export async function initDefaultFolder(): Promise<boolean> {
try {
let folderRoot: string = setPath();
let contentFolder = await fs.promises.readdir(folderRoot);
contentFolder = contentFolder.filter(x => x === 'temp');
const tempPath: string = setPath('./temp/');
const targetPath: string = setPath('./temp/target/)');
const pdfPath: string = setPath('./temp/generatedPDF/');
if (contentFolder.length === 0) {
await fs.promises.mkdir(tempPath);
await fs.promises.mkdir(targetPath);
await fs.promises.mkdir(pdfPath);
return false;
}
else {
const contentTemp = await fs.promises.readdir(tempPath);
let reload = false;
if (!contentTemp.includes('target')) {
await fs.promises.mkdir(targetPath);
reload = true;
}
if (!contentTemp.includes('generatedPDF')) {
await fs.promises.mkdir(pdfPath);
reload = true;
}
if (reload) return false;
return true;
}
} catch (error) {
throw new Error(error);
}
}
/**
* @author DOUAL Sofian
* @description Converts HTML files to string.
*
* @param { HTMLTarget[] } data
* @param { string } [path]
* @returns { Promise<FileBuffer[]> }
*/
export async function fromHtmlFileToString(data: HTMLTarget[], path?: string): Promise<FileBuffer[]> {
try {
const folderRoot: string = setPath(path ?? undefined);
const countProjects: number = data.length;
if (countProjects > 0) {
const res: FileBuffer[] = [];
for(let i = 0; i < countProjects; i++) {
if (data[i].projectName && data[i].fileName) {
let project: FileBuffer = {
name: data[i].projectName,
options: data[i].pdfOptions
};
if (folderRoot === setPath()) {
project['text'] = await fs.promises.readFile(
pathModule.join(folderRoot, '/temp/target/', data[i].fileName),
{encoding: 'utf-8'}
);
} else {
project['text'] = await fs.promises.readFile(
pathModule.join(folderRoot, data[i].fileName),
{encoding: 'utf-8'}
);
}
res.push(project);
delete data[i];
}
else {
let project: FileBuffer = {
name: data[i].projectName,
error: new Error('Projects need name && file name.')
};
res.push(project);
delete data[i];
}
}
return res;
} else {
throw 'No project detected.'
}
} catch (error) {
throw new Error(error);
}
}
// Scripts
/**
* @author DOUAL Sofian
* @description Carries out the process without to get local files.
*
* @export
* @param { FileBuffer[] } targets
* @param { boolean } save
* @param { path } [path]
* @returns { Promise<FileBuffer[]> }
*/
export async function getPdf(targets: FileBuffer[], save: boolean, path?: string): Promise<FileBuffer[]> {
const browser = await initBrowser();
if (save) {
let folderToSavePdf: string = setPath(path ?? undefined);
if (!path) {
await initDefaultFolder();
folderToSavePdf = pathModule.join(folderToSavePdf, '/temp/generatedPDF/');
}
targets = await generateBuffer(browser, targets, folderToSavePdf);
}
else {
targets = await generateBuffer(browser, targets);
}
await closeBrowser(browser);
return targets;
}
/**
* @author DOUAL Sofian
* @description Get html files and carries out the process.
*
* @export
* @param { HTMLTarget[] } files
* @param { boolean } save
* @param { Path } [path]
* @returns { Promise<FileBuffer[]> }
*/
export async function fromHtmlFileToPdf(files: HTMLTarget[], save: boolean, path?: Path): Promise<FileBuffer[]> {
let formatedFiles: FileBuffer[] = await fromHtmlFileToString(files, path?.toGetFiles ?? undefined);
const browser: Browser = await initBrowser();
if (save) {
let folderToSavePdf: string = setPath(path?.toSaveFiles ?? undefined);
if (!path?.toSaveFiles) {
await initDefaultFolder();
folderToSavePdf = pathModule.join(folderToSavePdf, '/temp/generatedPDF/');
}
formatedFiles = await generateBuffer(browser, formatedFiles, folderToSavePdf);
}
else {
formatedFiles = await generateBuffer(browser, formatedFiles);
}
await closeBrowser(browser);
return formatedFiles;
}
/**
* @author DOUAL Sofian
* @description It returns the path of the app directory if path param does not exists.
* Else if path param exists and it is a relative path, this method joins it with the path of app directory to finally returns the result as string.
* Else if path param exists and it is an absolute path, this methods return it.
*
* @param { string } [path]
* @returns { string }
*/
export function setPath(path?: string): string{
try {
if(path) {
if (pathModule.isAbsolute(path)) {
return path;
} else {
let dirPath: any= __filename.split('\\node_modules');
dirPath = (dirPath.length > 1 ? dirPath[0] : dirPath[0].split('\\dist')[0]);
return pathModule.join(dirPath, path);
}
} else {
let dirPath: any= __filename.split('\\node_modules');
dirPath = (dirPath.length > 1 ? dirPath[0] : dirPath[0].split('\\dist')[0]);
return dirPath;
}
} catch (error) {
throw new Error(error);
}
}
// MODELS
export interface HTMLTarget {
projectName: string;
fileName: string;
pdfOptions: puppeteer.PDFOptions;
}
export interface FileBuffer {
name: string;
url?: string;
text?: string;
buffer?: Buffer;
options?: puppeteer.PDFOptions;
pathOfsavedFile?: string;
error?: any;
}
export interface Path {
toGetFiles?: string;
toSaveFiles?: string;
}