# WEB打印,HTML转PDF工具。bookjs-eazy - 仓库地址: [GITEE](https://gitee.com/wuxue107/bookjs-eazy) | [GITHUB](https://github.com/wuxue107/bookjs-eazy) - 主要解决,HTML生成PDF,分页可控的问题,从此生成高品质PDF不在是困难的事 - 依赖js库:polyfill、jquery、lodash、bookjs-eazy - 优势: 1. 只需专注用H5构件你的PDF内容,而无需关心分页及内容截断问题,按规则自动分页。 2. 支持预览,所见即所得。支持WEB打印、页码/目录、自定义页眉页脚。 3. 前后端皆可生成PDF,前端打印另存为PDF,后端可配套使用chrome headless和wkhtmltopdf命令行PDF生成工具。 4. docker镜像。可快速构件你的在线PDF的打印生成服务 5. 兼容主流浏览器及移动端 - 缺陷提示: 1. 不支持现代js框架 VUE、React等单页面多路由场景,需要在html中用script标签直接引入,不能在import引入再经过编译 2. 不支持动态刷新,重新渲染需要刷新整个页面 3. PDF页面需要单独的html文件入口 4. 如果想嵌入应用网页内部,可使用iframe方式 # 预览案例(./dist) - eazy-1.html ![alt ](https://bookjs.zhouwuxue.com/static/js/bookjs/eazy-1-qrcode.png) - 另一个小说案例 eazy-2.html lodash模板:eazy-4.html vue模板:eazy-3.html ![alt ](https://bookjs.zhouwuxue.com/static/js/bookjs/eazy-2-qrcode.png) - 发票案例 - eazy-5.html - 注意,对于自定义纸张的页面,只有在web打印中只有在chrome“打印另存为PDF”或有安装并选择对应打印机和纸张时在能正确显示。否则有可能错乱。使用chrome headless和wkhtmltopdf不影响 - 表格: 合并单元格 - 参考实例:eazy-6.html # 使用docker快速体验(可以不使用docker,仅是提供web服务和在线生成下载PDF功能) - 下载或clone项目,命令行进入项目目录 - 运行 ./docker-start.sh 或 docker-start.bat - 即可通过浏览器 http://127.0.0.1:3000/eazy-1.html 访问demo,打印并制作PDF - 在下dist下可以尝试写自己的pdf页面。 # 使用方式: 渲染机制: 程序会检查全局变量window.bookConfig.start 的值。 直到此值为true时,才开始渲染将 #content-box 节点的内容渲染为PDF样式。 重要:如果你的页面是动态的,就先将默认值设为false,当内容准备好后,在将其设为true, 高度页面溢出检测原理: 页面内容节点.nop-page-content,是一个弹性高度的容器节点。 在向页面加入内容时会引起容器节点的高度变化。 计算页面的是否溢出,就时通过计算它高度得到的。 注意: 1. display: float, position: absolute; overflow样式的元素的插入不会页面容器高度变化。可能造成页面溢出而检测不到。 2. 因为 margin样式的元素 无法撑开.nop-page-content 大小,造成.nop-page-content位置偏移,很容易造成页面出现溢出的现象,所以控制相对位置尽量使用padding ## 配置页面参数: - 定义一个全局配置变量 bookConfig ```html ``` ## PDF内容设计 - 定义一个id为content-box节点内放入要插入到文档里的内容; - content-box下的每个一级子节点都需定义属性 data-op-type表示其在文档中的插入方式 其值含义如下: - 注意:不要限定容器节点(mix-box、.nop-fill-box、 table>td) 高度,影响溢出检测,出现未知结果 ``` block (常用): 块:(默认)如果当前页空间充足则整体插入,空间不足,则会整体插入到下一页 注意:这里的块,仅是内容不跨页。与css中的display无关,也就可以display: inline样式。 前面有用户问到这个问题。从而限制了他对PDF内容设计的思维。 例如:
...
* 使用在符合下列选择器规则的位置之一: #content-box> 下的一级节点 [data-op-type=mix-box] .nop-fill-box> 混合盒子容器节点下的一级节点 [data-op-type=table] tbody td> 表格的单元格的一级节点 text : 文本,跨页内容自动分割,节点内直接放入文本内容。(内部只能为文本,如果包含子节点,子节点标签将被删除) 例如:

long text...

* 使用在符合下列选择器规则的位置之一: #content-box> 下的一级节点 [data-op-type=mix-box] .nop-fill-box> 混合盒子容器节点下的一级节点 [data-op-type=table] tbody td> 表格的单元格的一级节点 new-page : 标记从新页开始插入 * 使用在符合下列选择器规则的位置之一: #content-box> 下的一级节点 [data-op-type=mix-box] .nop-fill-box> 混合盒子容器节点下的一级节点 [data-op-type=table] tbody>tr 表格的tbody下的tr节点,(与被标记到其他位置不同,被标记的tr节点会保留不会从页面删除) pendants : 页面部件列表(页眉/页脚/页标签/水印背景等) 部件:pendants内部的子节点,会自动标记class:nop-page-pendants,在其定义后的每个页面都会显示,直到下一个pendants出现。 部件nop-page-pendants包含css: {position: absolute}属性,相对页面纸张位置固定。 在页面设计时需要为部件节点设置css: left/right/top/bottom/width/height等属性来控制部件位置和大小。 * 使用在符合下列选择器规则的位置之一: #content-box> 下的一级节点 mix-box(常用) : 混合盒子:盒子内部class:nop-fill-box标记的节点的可以包含多个[data-op-type="text"],[data-op-type="block"]元素 盒子内的元素被超出一页时,会根据text/block的规则,自动分割到下一页,并会复制携带包裹元素的外部节点。 容器节点内的一级节点必须标记data-op-type属性,属性值: text或block text:允许跨页截断 block:(默认)不可跨页截断 * 使用在符合下列选择器规则的位置之一: #content-box> 下的一级节点 例如:下面的一段HTML,包含很长的内容,多到会超出几页的长度,那么bookjs-eazy会对其自动分页将会
布局1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBBBBBB 文章链接...
布局2
布局3
table : 对表格遇到分页时,出现的一些显示问题,做了些优化处理(注意:列一定要固定宽度),(参考:ezay-6 示例) 对于合并单元格:td上标记属性 data-split-repeat="true" ,在分页td里的文本会在在新页中也会显示。 td : td>直接子节点可以是[data-op-type="text"],[data-op-type="block"]元素 text:允许跨页截断 block:(默认)不可跨页截断 block-box : 块盒子(@deprecated 其功能已完全被mix-box替代):块盒子内部nop-fill-box标记的节点包含的多个块,盒子内的多个块被分割到多个页面时,都会复制包裹块的外部节点。 以下一个示例中的表格为例: table节点定义为块盒子 tbody节点定义为容纳块的容器节点(使用class: nop-fill-box标记) 这样在填充行tr时,当前页空间不足时,换页并复制外部table(除去nop-fill-box标记的部分)继续填充。这样表头就得到复用 text-box : 文本盒子(@deprecated 其功能已完全被mix-box替代):与块盒子类似,大文本内容跨多个页面时,会复制外部包裹文本的盒子的部分。 文本盒子节点, 大文本的容器节点需用 class : nop-fill-box标记 ``` - 注意:.nop-fill-box的所有父节点直到到data-op-type节点之间的节点不可以设置height、max-height样式,会影响页面溢出检测 - 使用样例 ```html
序号内容
1

long text1 ...

long text2 ...

...
...
ID姓名年龄
1张三12
表格尾部
第二章:文本盒子

第2章 文本盒子

1234566....(很长的文字)

第三章:混合盒子

第3章 混合盒子

red
green
blue
ABCDEFGHIJKLMNOPQRSTUVWXYZ...
``` # 在线体验 - 在线模板编辑 - 动手来试试: CodePen在线测试 # 使用示例 ## 页面背景图片 - 页面背景图片示例 ## 页面水印 - 页面水印示例 # 设计中的相关细节 ## 页面渲染完成前,事件回调 ```javascript /** * BOOK渲染完成前 book.before-complete **/ $(document).on('book.before-complete',function (e,info) { /** * info: * { * "PAGE_BEGIN_INDEX": 0, // 开始页码标记的页面序号 * "PAGE_END_INDEX": 2, // 结束页码标记的页面序号 * "TOTAL_PAGE": 3 // 总页数 * } **/ }); ``` ## 打印时不显示和强制打印背景 class: nop-no-print 被标记的节点在打印时不显示 class: nop-force-background 被标记的节点强制打印背景,在forcePrintBackground选项为false时可用 ## 奇偶页实现 - 在设置简易页面后,页面节点上会添加对应的class: nop-page-item-odd (奇数页)、 nop-page-item-even(偶数页) nop-page-item-pagenum-1(页编号) ## 文本、盒子被分割到不同页后,被差分部分特殊样式处理 - 同一段落文本,被分到下一页的文本部分节点,会被class: nop-link-last 进行标记。可以根据此class,进行缩进特殊处理 - text-box 、block-box、mix-box 内容被分割部分也同样会被class: nop-link-last 标记 ## 浏览器类型标记 - Book节点。上会标记浏览器类型。class: chrome、firefox、safari、ie、qq、wechat、wkhtmltopdf ## 打印和预览时样式差异处理 - Book节点。在不同模式下,会使用class: nop-book-preview(预览)、nop-book-print(打印) 进行标记 ## PDF的属性信息生成 - 可以在页面中加入meta标签,bookjs-eazy会将其生成到pdf的属性信息中 - 从1.12.0 版本开始,服务端生成有效。 ```html ``` # 生成PDF及配套PDF生成命令行工具的使用 ## 通过浏览器点击打印按钮,打印另存为PDF - 点击WEB打印按钮 打印选择“另存为PDF” ## 使用官网docker镜像,自建打印服务、点击直接下载PDF(服务端打印、推荐) - 可使用 ./docker-start.sh 进行快速部署 - 参考 : bookConfig.toolBar.serverPrint 服务端打印选项 - 可以配置值为 :true (和 {serverUrl : '/'}等效) 或 {serverUrl : '//your_screenshot_api_server_host[:WEB_PORT]/'} ```bash # 自己docker打印服务的命令 # ./docker-start.sh [WEB端口,默认3000] ./docker-start.sh # 运行打印服务 # 会以dist为根目录,创建一个web站点。可通过http://127.0.0.1:3000/eazy-1.html访问示例,并可使用服务端打印 # # 你可以在dist根目录下用bookjs-eazy创建book.html(参考示例:eazy-1.html)。 # 即可访问 http://127.0.0.1:3000/book.html 进行预览/打印下载 # 生成的pdf会存在dist/pdf/ 目录下。 ``` 详细内容见,wuxue107/screenshot-api-server项目 ## 使用官网打印服务 - 使用官网打印服务服务,同自建打印服务 ``` 指定配置bookConfig.toolBar.serverPrint.serverUrl值为: '//bookjs.zhouwuxue.com/' ``` - 使用官网的打印服务地址时,:用bookjs-eazy创建的页面必须外网访问。 - 页面需要不授权访问,或者 使用短期授权码机制(建议),携带在url上。只有在有授权码有效时间段内才访问你的页面 ## 命令行打印,使用chrome headless方式渲染 - 此插件适配了wkhtmltopdf和chrome headless。可使用本项目中配套封装的命令行工具,从后端生成精美PDF ```bash # 首次使用时,安装bin/html2pdf的依赖包 yarn install ``` ```bash # 安装过后,执行命令 # 示例: bin/html2pdf print --output eazy-2-1.pdf "https://bookjs.zhouwuxue.com/eazy-2.html" # # 命令行说明: # Usage: html2pdf print [options] # # Options: # -o --output [outputfile] 保存PDF文件的路径 (default: "output.pdf") # -t --timeout [type] 超时时间 (default: 60000) # -a --agent [agent] 指定转换引擎chrome-headless|puppeteer,默认:puppeteer (default: "puppeteer") # -d --printDelay [delay] 打印前等待延迟(毫秒) (default: 1000) # -c --checkJs [jsExpression] 检查是否渲染完成的js表达式 (default: "window.status === 'PDFComplete'") # "window.document.readyState === 'complete'" 这个表达式可以用作非bookjs-eazy构建的网页 # -h, --help display help for command # # ``` ## 命令行打印,使用wkhtmltopdf渲染(会更据h1-h6生成PDF书签),需自己去下载命令行,放入PATH的环境变量所在目录下 ```bash bin/pdf-a4-landscape "https://bookjs.zhouwuxue.com/eazy-2.html" eazy-2-2.pdf # # 在bin目录下,有数个同类脚本文件。 # # bin/pdf-[纸张]-[纸张方向] [预览的链接] [输出文件] # # 注意:如果使用wkhtmltopdf方式的自定义尺寸,不用担心,浏览器渲染完毕后,在Console上会输出wkhtmltopdf的PDF配套生成命令 ``` # 生成常见问题(踩坑备忘录) - 服务端打印失效: - 启动的打印服务(screenshot-api-server) 必须要能够访问到你要打印的HTML制作的PDF预览页面。 - 内容超出页面: - 一些如: display: float, position: absolute; overflow样式的元素可能不会页面容器高度变化。因而表现出超出页面。 - 因为 margin样式的元素 无法撑开.nop-page-content 大小,造成.nop-page-content位置偏移,很容易造成页面出现溢出的现象,所以控制相对位置尽量使用padding - 页面出现多余空白: - 不要手动对html、body、.nop-book、.nop-page、.nop-page-items、nop-page-item元素做任何的border/width/height/margin/padding等样式调整 - 每页都多出一个空白页 - 见bookConfig.pageFixedHeightOffset 选项,进行调节 - 字体无法显示: - 生成的PDF里全是框框或显示不出来,原因在于。在linux服务器环境下,通常没有安装所需字体。或使用web加载字体文件太大,加载超时 - iframe 嵌入网页失效:不能点击无法下载打印: - 需要在iframe上加入 sandbox="allow-downloads allow-top-navigation allow-scripts allow-modals" 属性 - 页面事件绑定失效: - 经过bookjs-eazy渲染后,: 如果失效,原绑定可能被分割到不同页面,请尝试在PDF渲染完成事件后处理事件绑定。 - 找不到wkhtmltopdf - 执行bin/pdf-xx-xx 相关命令,找不到wkhtmltopdf,需自己去下载wkhtmltopdf放置PATH目录下 - 使用data-op-type="table" 表格合并单元格分页显示不正确。建议: - 将表格布局写好 先使用data-op-type="block" 表格不写任何东西,使保持在一个页面里,看看不经过拆分的表格是否布局正确。 如果不正确,就是你自己table写的有问题,和bookjs-eazy的无关。 - 在将数据填充进去,改用data-op-type="table" ,此时如出现问题。可以在这里重现场景表格测试,保存并复制链接。提交issue - 页面卡死,CPU超高 - bookjs-eazy 不能经过了import引入和再编译 解决: 需要在html中通过script标签引入。 - 使用的block块元素,超出一页内容,即使换页也放置不下。 常见于“[data-op-type="table"] td>[data-op-type="block"]”中 td里的下元素如果未指定data-op-type也会默认视为block 请合理的拆分,block块元素大小,使其可以在一页内放置。 - 部件无法显示 - 页边距 bookConfig.padding = "0 0 0 0" 时,在火狐浏览器的打印预览中,空白页部件无法显示。 调整bookConfig.padding = "1px 0 0 0" 或者可以尝试在页面中补一个```  ``` 的空文本节点。 # QQ交流群 ![alt ](https://bookjs.zhouwuxue.com/static/js/bookjs/qq-group-1.png) - 仓库地址: [GITEE](https://gitee.com/wuxue107/bookjs-eazy) | [GITHUB](https://github.com/wuxue107/bookjs-eazy)