前端打印生成pdf
以下打印均为使用a4纸格式生成pdf
1,空白页
1.1,图片前空白页
多张图片(大于3)连续出现,可能出现连续的图片被放在一起,置于下一页,上一页则会出现空白页(正常情况应该是上一页只有一张图,下一页顶部是两张图;但是,结果上一页留白,下一页顶部是三张图)
解决:
①,img 添加float样式
1.2,内容的最后位置出现空白页
解决:
①,调整页边距可能会导致出现空白页情况,故而可调整页边距可能解决
1.3,文字内容少或者以文字结尾,则后面出现空白页
(发现,如果内容部分是以图片结尾的,则不会出现额外的空白页现象)
解决:
①,向页面末尾添加`<img alt="" style="width:0;height:0">`,即使用空图展位
2,问题:
①,使用float容易出现,多张图片连续一起 出现在下一页(但是上一页还有空间至少可以排放一张图的),添加flex colomn布局可以解决,但是flex容易出现截断问题
2.1,图片截断
2.1.1,以下布局容易出现截断情况
①,图片用flex布局,容易出现截断情况
②,position绝对定位布局,也容易发生截断情况
解决:
①,使用float布局
②,使用table-row布局
③,使用下列图片样式
img { display: inline-block; page-break-inside: avoid; max-width: 100%; }
3,基于puppeteer.js搭建node服务器,生成pdf文件
需要自己搭建一套nodejs服务,专门用于生成pdf,页面需要使用html先绘制出来,通过html链接,puppeteer讲html生成对应pdf
// puppeteer 库会下载自带chrome,使用自带chrome启动并渲染 const puppeteer = require('puppeteer') const fs = require('fs') const sleep = async function (timeout) { return new Promise(function (resolve) { setTimeout(function () { resolve() }, timeout) }) } const html2pdf = async function ( reqParams, timeout, printDelay, checkPdfRenderCompleteJs ) { try { const { url: pageUrl, showMargin } = reqParams const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NDc4NTI4MzgsInVzZXJJZCI6IjEwMDAwMjE4NyJ9.oDZ6x1e5kZnv1TxFf0cuj03a0eToeLKUdeyMSmoIIbc' const whiteList = ['api.g2s.cn', 'orgapi.g2s.cn', 'aries-app.g2s.cn'] const browser = await puppeteer.launch({ headless: true, // slowMo: 350, // slow down by 250ms dumpio: true, devtools: false, timeout: timeout + 5, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu', '--disable-extensions', '--mute-audio', '–-no-first-run', '--ignore-certificate-errors', ], }) const page = await browser.newPage() await page.setViewport({ width: 750, height: 1000, }) // page.on('console', (m) => { // console.log('页面内日志***:' + m.text()) // }) page.setJavaScriptEnabled(true) // await page.setRequestInterception(true) // page.setExtraHTTPHeaders({ access_token: token }) // page.on('request', async function (req) { // if (req.resourceType() === ['xhr']) { // whiteList.forEach((v) => { // if (!req.url().includes(v)) { // req.headers({ access_token: token }) // } // }) // // console.log('xhr 请求') // } // // console.log('req:', { // // url: req.url(), // // resourceType: req.resourceType(), // // headers: req.headers(), // // }) // await req.continue() // }) // page.on('requestfinished', function (req) { // // console.log('requestfinished:', req) // }) const option = { // landscape : false, printBackground: true, format: 'a4', scale: 1, // paperWidth : '1mm', // paperHeight : '1mm', // Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means print all pages. pageRanges: '', title: '', // Whether to silently ignore invalid but successfully parsed page ranges, such as '3-2'. Defaults to false. ignoreInvalidPageRanges: false, // HTML template for the print header. Should be valid HTML markup with following classes used to inject printing values into them: // date: formatted print date // title: document title // url: document location // pageNumber: current page number // totalPages: total pages in the document // For example, <span class=title></span> would generate span containing the title. // Whether or not to prefer page size as defined by css. Defaults to false, in which case the content will be scaled to fit the paper size. preferCSSPageSize: true, // Allowed Values: ReturnAsBase64, ReturnAsStream transferMode: 'ReturnAsStream', } if (showMargin) { Object.assign(option, { margin: { top: '44px', bottom: '74px', left: '44px', right: '44px', }, displayHeaderFooter: true, headerTemplate: `<span class=""></span>`, footerTemplate: ` <style> section { width:100%; padding:0 44px; font-family: "宋体"; font-size: 12px; color: #333333; font-weight: 400; text-align: center; } </style> <section> 第 <span class="pageNumber"></span> 页 共 <span class="totalPages"></span> 页 </section>`, }) } // option.path = savePath // page.setViewport({ // width: 794, // height: 1123 // }) // const login = async function (token) { // const windowHandle = await page.evaluateHandle(() => { // console.log('localStorage', localStorage) // localStorage.setItem('shine-admin-web-zhishi-token', token) // }) // console.log('windowHandle', windowHandle) // } const waitPdfRenderComplete = async function (timeout) { let time = 0 return new Promise(function (resolve, reject) { const t = setInterval(function () { time += 500 page .evaluate( checkPdfRenderCompleteJs || 'window.document.readyState === "complete"' ) .then(function (isOk) { if (isOk) { clearInterval(t) resolve('html 解析完成-成功') } }) if (time > timeout) { setTimeout(function () { clearInterval(t) reject('html 解析完成-失败 timeout') }, 20) } }, 500) }) } // console.log('open url:' + pageUrl) // await login(token) await page.goto(pageUrl, { waitUntil: 'networkidle2' }) console.log('wait pdf render ...') const val = await waitPdfRenderComplete(timeout) console.log('html 解析完成', val) console.log('print delay:' + printDelay) await sleep(printDelay) // console.log('save pdf file:' + savePath) const buf = await page.pdf(option) await page.close() await browser.close() return buf } catch (error) { console.log('生成pdf错误:', error) // process.exit(1) throw error } } module.exports = html2pdf
4,基于pdfjs 生成pdf文件
打不死的小强