派大星

vuePress-theme-reco 派大星    2021 - 2022
派大星 派大星
主页
博客
  • 前端
  • JavaScript文章
  • 三级目录
  • 笔记
  • 学习笔记
  • 页面
  • CSS
  • HTML
  • 技术
  • 技术文档
  • GitHub技巧
  • 技术笔记
  • 实用工具
  • Nodejs
  • 博客搭建
  • 更多
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • JavaScript
  • 浏览器&网络
  • 前端框架
  • 资源工具
  • 持续集成
  • 小程序
  • 杂谈
  • React
  • Mysql
  • 面试题
  • uniapp
  • 《ES6 教程》笔记
  • 《Git》学习笔记
  • 《JavaScript教程》笔记
  • 《React》笔记
  • 核心概念
  • 高级指引
  • Hook
  • 案例演示
  • 《TypeScript 从零实现 axios》
  • 初识 TypeScript
  • TypeScript 常用语法
  • ts-axios 项目初始化
  • ts-axios 基础功能实现
  • ts-axios 异常情况处理
  • ts-axios 接口扩展
  • ts-axios 拦截器实现
  • ts-axios 配置化实现
  • ts-axios 取消功能实现
  • ts-axios 更多功能实现
  • ts-axios 单元测试
  • ts-axios 部署与发布
  • 《Vue》笔记
  • 基础
  • 组件
  • 过渡&动画
  • 工具
  • 规模化
  • 可复用性&组合
  • 其他
  • Vuex
标签
时间轴
关于
author-avatar

派大星

272

文章

69

标签

主页
博客
  • 前端
  • JavaScript文章
  • 三级目录
  • 笔记
  • 学习笔记
  • 页面
  • CSS
  • HTML
  • 技术
  • 技术文档
  • GitHub技巧
  • 技术笔记
  • 实用工具
  • Nodejs
  • 博客搭建
  • 更多
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • JavaScript
  • 浏览器&网络
  • 前端框架
  • 资源工具
  • 持续集成
  • 小程序
  • 杂谈
  • React
  • Mysql
  • 面试题
  • uniapp
  • 《ES6 教程》笔记
  • 《Git》学习笔记
  • 《JavaScript教程》笔记
  • 《React》笔记
  • 核心概念
  • 高级指引
  • Hook
  • 案例演示
  • 《TypeScript 从零实现 axios》
  • 初识 TypeScript
  • TypeScript 常用语法
  • ts-axios 项目初始化
  • ts-axios 基础功能实现
  • ts-axios 异常情况处理
  • ts-axios 接口扩展
  • ts-axios 拦截器实现
  • ts-axios 配置化实现
  • ts-axios 取消功能实现
  • ts-axios 更多功能实现
  • ts-axios 单元测试
  • ts-axios 部署与发布
  • 《Vue》笔记
  • 基础
  • 组件
  • 过渡&动画
  • 工具
  • 规模化
  • 可复用性&组合
  • 其他
  • Vuex
标签
时间轴
关于

web打印,一篇搞定

vuePress-theme-reco 派大星    2021 - 2022

web打印,一篇搞定

派大星 2021-07-07 掘金分享打印

对很多中后台的业务来讲,打印场景还是很常见的。针对个人遇到的,以及现有的一些方案对比,总结一波😎(文末有PPT版本,需要留言),希望能帮助同样头疼的你。废话不多说,开讲。

# 一、浏览器打印

# 1、优缺点

通过 window.print() 、document.execCommand('print’) 调用浏览器打印

不同浏览器的区别:在Safari和Chrome都会弹起打印预览的窗口,FireFox(老版本)和 IE 没有预览而是直接让你选择打印机

但是直接使用浏览器打印虽然省事,但是存在很多问题,无法满足我们的打印需求:

  • 1、打印的是整个网页,不能打印局部内容;
  • 2、打印不支持自定义分页行为,默认不支持批量打印;
  • 3、打印的时候样式有问题,所见非所得;
  • 4、打印可以准确识别的样式单位是绝对单位(如pt、mm、cm),对相对单位识别不同打印机可能会得到意想不到的结果;

# 2、问题来了

如何实现局部打印?🤔

# (1)innerHtml

简单粗暴,在你不需要考虑体验,项目里面用的也比较少时,可以试试看😆

function innerHtmlPrint(){
    // 缓存页面内容
    const bodyHtml = window.document.body.innerHTML;
    // 获取要打印的dom
    const printContentHtml = document.getElementById("print").innerHTML;
    // 替换页面内容
    window.document.body.innerHTML = printContentHtml;
    // 全局打印
    window.print();
    // 还原页面内容
    window.document.body.innerHTML = bodyHtml;
    // 页面事件会丢失,需要刷新
    window.location.reload();
}

# (2)iframe

稍微复杂点,需要将打印的内容添加到iframe里,然后打印整个iframe,需要注意:往iframe里写一些样式的时候,一些checkbox,radio要特殊处理。

这里我把项目中用到的iframe局部打印抽出来了,各位可以直接npm使用 vue-iframe-print (opens new window)😎😎😎 插件使用demo (opens new window)

image.png 如果你是vue项目,只需要安装后,在需要打印的dom上加上 v-print即可,主要实现原理如下(样式部分省略):

  • 创建iframe
  • 构建iframe docTyp、 head信息
  • 获取局部dom,插入iframe
  • window.print()
   function onIframePrint(printId) {
      const printContentHtml = document.getElementById("printId").innerHTML;
      const iframe = document.createElement("iframe");
      iframe.setAttribute(
        "style",
        "position:absolute;width:0px;height:0px;left:-500px;top:-500px;"
      );
      document.body.appendChild(iframe);
      iframe.contentDocument.write(printContentHtml);
      iframe.contentDocument.close();
      iframe.contentWindow.print();
      document.body.removeChild(iframe);
    },

# (3)canvas

将打印内容转为图片,一倍清晰度模糊,可以用2倍canvas。缺点:pdf需要下载,有的产品需求需要一键打印。html2canvas不支持ie,兼容性也是个问题

function print() {
  var target = document.getElementsByClassName("right-aside")[0];
  target.style.background = "#FFFFFF";

  html2canvas(target, {
    onrendered:function(canvas) {
        var contentWidth = canvas.width;
        var contentHeight = canvas.height;

    //一页pdf显示html页面生成的canvas高度;
    var pageHeight = contentWidth / 592.28 * 841.89;
    //未生成pdf的html页面高度
    var leftHeight = contentHeight;
    //页面偏移
    var position = 0;
    //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
    var imgWidth = 595.28;
    var imgHeight = 592.28/contentWidth * contentHeight;

    var pageData = canvas.toDataURL('image/jpeg', 1.0);

    var pdf = new jsPDF('', 'pt', 'a4');

    //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
    //当内容未超过pdf一页显示的范围,无需分页
    if (leftHeight < pageHeight) {
    pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
    } else {
        while(leftHeight > 0) {
            pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
            leftHeight -= pageHeight;
            position -= 841.89;
            //避免添加空白页
            if(leftHeight > 0) {
              pdf.addPage();
            }
        }
    }

    pdf.save("content.pdf");
}
  })
}

pdf转成图片,会失真,打印一定程度上会模糊,如果用的是针式打印机,打出来的东西基本不能看。👿 也是想了很久找的的解决办法(完美还原):😬😬😬

  <object
    type="application/pdf"
    data="./滴滴出行行程报销单A.pdf"
    width="100%"
    height="700"
  ></object>
  
  <embed 
     type="application/pdf" 
     src="./滴滴出行行程报销单A.pdf" 
     width="100%" 
     height="700px"
  />

# 二、插件打印

一般是通过项目里面嵌入脚本,或者安装本地插件来完成,优缺点也都很明显

# 1、优点

  • 功能强大,可以调用到系统底层的东西,比如获取系统打印机列表,设置默认打印机等
  • 可以实现无预览打印

# 2、缺点

  • 需要安装客户端,大多收费
  • 第三方插件,无技术支持,出现问题难以解决(版本问题,chrome84升级导致的证书问题)
  • 本地插件的方式基本只有window系统版本

我们项目目前在使用的插件是C-lodop,收费,现在用的注册码也不知道是谁在那搞得 🌚。

如果失效,意味着我们项目的所有打印基本废了。目前来看,好像是一次购买永久有效的,先不慌 🌚

# 3、一些插件

这里列出几个插,仅供参考:

  • C-lodop (opens new window):功能强大,兼容性不是很好,你想在保证清晰度的情况下,没办法写css3,画个圆角边框都不行🤧🤧🤧,依赖于系统IE版本,IE9以下,打出来的东西乱七八糟。。。建议使用之前,仔细研究下官网给的demo,这个插件的原理是,在页面嵌入一段js,和本地客户端通过webscoket进行通信
  • HttpPrinter (opens new window):和C-lodop差不多,可以去了解下
  • HiPrint (opens new window):不需要安装客户端,但是没有npm包,依赖于jQuery

# 4、打个嘴炮 😈😈😈

其实,大家要是有时间,可以自己用nw.js和electron.js自己撸一个,这位大佬自己就搞了一个:如何通过Nw.js纯前端实现调用热敏打印机打印小票? (opens new window)

# 三、打印样式

# 1、先看下基本的页面模型

# 2、@media print

可以控制打印时的样式,仅在打印生效,可以实现一些特殊需求。

# 3、@page

设置页面大小(A3,A4,A5)、边距(margin)、方向( auto、landscape、portrait)等。

# 4、page-break-xxx(划重点)

page-break-before( after ) 用于设置元素前( 后 )的分页行为,可取值:

   * auto默认值。如果必要则在元素前插入分页符。
   * always在元素前插入分页符。
   * avoid避免在元素前插入分页符。
   * left在元素之前足够的分页符,一直到一张空白的左页为止。
   * right在元素之前足够的分页符,一直到一张空白的右页为止。
   * inherit规定应该从父元素继承 page-break-before 属性的设置。

page-break-inside设置元素内部的分页行为。取值如下:

   * auto默认。如果必要则在元素内部插入分页符。
   * avoid避免在元素内部插入分页符。
   * inherit规定应该从父元素继承 page-break-inside 属性的设置。
 
orphans设置当元素内部发生分页时必须在页面底部保留的最少行数。

widows设置当元素内部发生分页时必须在页面顶部保留的最少行数。

# 四、云打印(node + ipp)

# 1、先科普下打印机类型 🤔🤔🤔

# (1)激光打印机

办公室常见的打印机,一般用打印普通文档材料。利用激光加热将墨粉固定在纸上,从而实现打印功能。平常的耗材是墨粉,使用的纸张是普通纸,一般是打印黑白色。打印速度快 后期耗材便宜

# (2)针式打印机

一般用于打印票据,或者需要按压打印的纸张。将色带上的墨水压在纸上,从而实现打印功能。平常的耗材是色带,使用的纸张是多联纸,比起其他两个分类针式打印机可以说是元老级别的,它是是市场上较早出现的种类。主要有9针、24针、72针、144针等多种针式打印机。其特点比较鲜明结构简单、技术成熟、性能价格比好、消耗费用低。

# (3)热敏打印机

使用专用纸张,靠高温显示需要打印的信息.主要用于打印小票.

# (4)喷墨打印机

一般用于打印彩色材料。将墨水喷射在纸上,从而实现打印功能。平常的耗材是墨水,使用的纸张是普通纸,一般可以打印彩色。(另外也有一个耗材是墨盒,有些机型不必频繁更换)

# 2、node + ipp

先了解下两个前缀概念:🤔🤔🤔

  • 互联网打印协议 (IPP;Internet Printing Protocol)是一个在互联网上打印的标准网络协议,它容许用户可以透过互联网作遥距打印及管理打印工作等工作。用户可以透过相关界面来控制打印品所使用的纸张种类、分辨率等各种参数。

  • 无头浏览器 指的是我们使用脚本来执行以上过程的浏览器,能模拟真实的浏览器使用场景。

不了解Puppeteer的可以看看这篇文章: 无头浏览器 Puppeteer 初探 - 蚂蚁金服 (opens new window)

# 3、云打印流程图

目前我们组的云打印已经落地,但是存在诸多限制,使用的还不多,感兴趣的可以参考另外一篇文章 Egg + Puppeteer 实现Html转PDF(已开源) (opens new window)

# 五、一些思考

1、无特殊要求的还是建议使用window.print() 局部打印可以使用我写的插件 vue-iframe-print (opens new window) https://github.com/alexwjj/vue-iframe-print 😎😎😎

2、看业务需求,如果是网络打印机,需要无痕打印,可以采用node+ipp的云打印,实现起来也不难。🕵

3、后端给的源文件,要求不高可以用pdfjs转canvas,要求比较高用<object>、<embed>标签加载后端给的文件流,完美还原源文件清晰度

4、pdf2htmlEX插件 (opens new window),pdf转html,4年前最后一次维护,可以尝试

也查了很多资料,目前web打印这块感觉还是window.print比较实用,也没找到什么开源项目。哪位大佬有没有的,推荐一下的

# 六、参考资料:

  • 由打印机TCP协议引起的网络通讯协议深入研究:从TCP到万能打印、云打印 (opens new window)
  • 无头浏览器 Puppeteer 初探 (opens new window)
  • 浏览器打印方案调研 (opens new window)
  • 前端一键打印解决方案 (opens new window)
  • PDF 能转成 HTML 吗?如何转换? (opens new window)

# 七、PPT,如有需要,可发邮箱

# 八、往期回顾

  • 🔥一篇够用的TypeScript总结 (opens new window)
  • 一名 vueCoder 总结的 React 基础 (opens new window) 180+ 👍🏿
  • Vue 转 React不完全指北 (opens new window) 600+ 👍🏿
  • 跳槽人速来,面经&资源分享 (opens new window) 1100+ 👍🏿
  • 一年半前端人的求职路 (opens new window) 300+ 👍🏿
  • vue2.x高阶问题,你能答多少 (opens new window) 400+ 👍🏿
  • 聊一聊前端性能优化 (opens new window) 1300+ 👍🏿
  • Egg + Puppeteer 实现Html转PDF(已开源) (opens new window) 50+ 👍🏿
  • web打印,一篇搞定 (opens new window) 15+ 👍🏿