UNPKG

@liascript/exporter

Version:
300 lines (272 loc) 8.43 kB
import * as helper from './helper' import * as COLOR from '../colorize' const path = require('path') import puppeteer from 'puppeteer' export function help() { console.log('') console.log(COLOR.heading('PDF settings:'), '\n') COLOR.info( 'PDF export generates printable documents from your LiaScript course using Puppeteer, a headless Chrome browser automation tool. This allows for high-quality rendering of all course elements including interactive content.' ) console.log('\nLearn more: https://pptr.dev/ \n') COLOR.command( null, '--pdf-stylesheet', ' Inject an local CSS for changing the appearance.' ) COLOR.command( null, '--pdf-theme', ' LiaScript themes: default, turquoise, blue, red, yellow' ) COLOR.command( null, '--pdf-timeout', ' Set an additional time horizon to wait until finished.' ) COLOR.command( null, '--pdf-preview', ' Open preview-browser (default false), print not possible' ) console.log('') console.log(COLOR.italic('The following are puppeteer specific settings.')) console.log( '\nLearn more:\n https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pagepdfoptions\n' ) COLOR.command( null, '--pdf-scale', ' Scale of the webpage rendering. Defaults to 1. Scale amount must be between 0.1 and 2.' ) COLOR.command( null, '--pdf-displayHeaderFooter', ' Display header and footer. Defaults to false.' ) COLOR.command( null, '--pdf-headerTemplate', ' HTML template for the print header, inject classes date, title, url, pageNumber, totalPages' ) COLOR.command( null, '--pdf-footerTemplate', ' HTML template for the print footer. Should use the same format as the headerTemplate' ) COLOR.command( null, '--pdf-printBackground', ' Print background graphics. Defaults to false' ) COLOR.command( null, '--pdf-landscape', ' Paper orientation. Defaults to false.' ) COLOR.command( null, '--pdf-pageRanges', ' Paper ranges to print, e.g., "1-5, 8, 11-13"' ) COLOR.command( null, '--pdf-format', ' Paper format. If set, takes priority over width or height options. Defaults to a4.' ) COLOR.command( null, '--pdf-width', ' Paper width, accepts values labeled with units.' ) COLOR.command( null, '--pdf-height', ' Paper height, accepts values labeled with units.' ) COLOR.command( null, '--pdf-margin-top', ' Top margin, accepts values labeled with units.' ) COLOR.command( null, '--pdf-margin-right', ' Right margin, accepts values labeled with units.' ) COLOR.command( null, '--pdf-margin-bottom', ' Bottom margin, accepts values labeled with units.' ) COLOR.command( null, '--pdf-margin-left', ' Left margin, accepts values labeled with units. ' ) COLOR.command( null, '--pdf-preferCSSPageSize', ' Give any CSS @page size declared in the page priority over what is declared in width and height or format options.' ) COLOR.command( null, '--pdf-omitBackground', ' Hides default white background and allows capturing screenshots with transparency. Defaults to true. ' ) } export async function exporter( argument: { input: string readme: string output: string format: string path: string key?: string // special cases for PDF 'pdf-preview'?: boolean 'pdf-scale'?: number 'pdf-displayHeaderFooter'?: string 'pdf-headerTemplate'?: string 'pdf-footerTemplate'?: string 'pdf-printBackground'?: boolean 'pdf-landscape'?: boolean 'pdf-format'?: string 'pdf-width'?: string | number 'pdf-height'?: string | number 'pdf-margin-top'?: string | number 'pdf-margin-bottom'?: string | number 'pdf-margin-right'?: string | number 'pdf-margin-left'?: string | number 'pdf-preferCSSPageSize'?: boolean 'pdf-omitBackground'?: boolean 'pdf-timeout'?: number 'pdf-stylesheet'?: string 'pdf-theme'?: string }, json ) { const dirname = helper.dirname() let url = `file://${dirname}/assets/pdf/index.html?` if (helper.isURL(argument.input)) { url += argument.input } else { url += 'file://' + path.resolve(argument.input) } const browser = await puppeteer.launch({ pipe: true, args: [ '--no-sandbox', '--disable-web-security', '--disable-features=IsolateOrigins', '--disable-site-isolation-trials', '--unhandled-rejections=strict', '--disable-features=BlockInsecurePrivateNetworkRequests', '--allow-file-access-from-files', '--enable-local-file-accesses', '--enable-features=ExperimentalJavaScript', ], headless: argument['pdf-preview'] ? false : 'new', // Try to use the system Chrome if available executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined, // Add this to use the installed browser if no executable path is provided channel: process.env.PUPPETEER_EXECUTABLE_PATH ? undefined : 'chrome', }) const page = await browser.newPage() console.warn( 'depending on the size of the course, this can take a while, please be patient...' ) // this handle the alert - boxes, so that these are not blocking page.on('dialog', async (dialog) => { console.log(dialog.type()) console.log(dialog.message()) await dialog.accept() }) try { await page.goto(url, { waitUntil: 'networkidle2', // remove timeout timeout: 0, }) } catch (e) { console.warn('pdf generation failed:', e) } if (argument['pdf-stylesheet']) { const href = path.resolve(dirname + '/../', argument['pdf-stylesheet']) await page.evaluate(async (href) => { const link = document.createElement('link') link.rel = 'stylesheet' link.href = href const promise = new Promise((resolve, reject) => { link.onload = resolve link.onerror = reject }) document.head.appendChild(link) await promise }, href) } if (argument['pdf-theme']) { await page.evaluate(async (theme) => { document.documentElement.classList.remove('lia-theme-default') document.documentElement.classList.add('lia-theme-' + theme) }, argument['pdf-theme']) } /* await page.evaluate(async () => { const style = document.createElement('style') style.type = 'text/css' const content = ` :root { --color-highlight: 2,255,0; --color-background: 122,122,122; --color-border: 0,0,0; --color-highlight-dark: 0,0,0; --color-highlight-menu: 0,0,0; --color-text: 0,0,255; --global-font-size: 1rem; --font-size-multiplier: 2; } ` style.appendChild(document.createTextNode(content)) const promise = new Promise((resolve, reject) => { style.onload = resolve style.onerror = reject }) document.head.appendChild(style) await promise }) console.warn(argument) */ if (!argument['pdf-preview']) { await helper.sleep(argument['pdf-timeout'] || 30000) await toPDF(argument, browser, page) } } async function toPDF(argument: any, browser: any, page: any) { try { await page.emulateMediaType('screen') await page.pdf({ path: argument.output + '.pdf', format: argument['pdf-format'] || 'a4', printBackground: argument['pdf-printBackground'] || true, displayHeaderFooter: argument['pdf-displayHeaderFooter'] || false, margin: { top: argument['pdf-margin-top'] || 80, bottom: argument['pdf-margin-bottom'] || 80, left: argument['pdf-margin-left'] || 30, right: argument['pdf-margin-right'] || 30, }, scale: argument['pdf-scale'] || 1, headerTemplate: argument['pdf-headerTemplate'], footerTemplate: argument['pdf-footerTemplate'] || '', landscape: argument['pdf-landscape'] || false, width: argument['pdf-width'] || '', height: argument['pdf-height'] || '', //preferCSSPageSize: argument['pdf-preferCSSPageSize'] || '', omitBackground: argument['pdf-omitBackground'] || false, }) } catch (e) { console.warn('failed to print to pdf', e) } await browser.close() }