关于 C++ 打印 PDF 打印及 PDF 转图片、合并

原文: http://www.aqcoder.com/post/42

pdf(Portable Document Format 的简称,意为“便携式文档格式”),是由 Adobe Systems 用于与应用程序、操作系统、硬件无关的方式进行文件交换所发展出的文件格式。PDF 文件以 PostScript 语言图象模型为基础,无论在哪种打印机上都可保证精确的颜色和准确的打印效果,即 PDF 会忠实地再现原稿的每一个字符、颜色以及图象。

PDF 设计的初衷是为了解决多平台的打印问题,所以他不像 WORD 那样具有文档流结构,编辑方面没有 WORD 那样强大。

打印

需要打印 PDF 自然需要一个打印程序,可以打印 PDF 的程序有很多。Adobe 自家的有 PDF Reader、 Acrobat。商用的有金山 WPS、Foxit Reader。开源的有 xpdf reader 等。

当然,作为程序猿我们说的打印自然不是讨论如何使用这些软件打印 PDF。而是这么使用程序打印 PDF。

应用场景:第三方给你的系统提供了 PDF 格式的文件,在你的系统中有个打印按钮,点击之后去打印这个 PDF。

实现方法一

在客户端上安装一个 PDF 渲染程序(上面的几个 PDF 浏览器/编辑器程序都含有渲染功能),然后使用命令行调用打印。各种语言启动进程的方法这里就不赘述了。

显然,这种方法有个很大的缺点,要求客户机上必须安装一个渲染程序,或者吧他的程序包在你的程序包里,这样会有版权问题,还会使你的程序包变大。

实现方法二

讲方法二前,我们先来说说网传的一种方法

if 

这是 C++ 的实现网上还有一个 C# 的实现。

这种方法是行不通的 ,你会发现有的人在帖子里回复何以,有的人回复不可以。这是为什么呢。

因为打印机一般只识别打印格式,如 PostScript 格式,而 PDF 格式是在 PostScript 格式之上的。但是有些新型的打印机也会识别 PDF 格式。这就是为什么有的人测试不可以,有的人测试可以的原因。

现在我们来讨论方法二。直接发送 PDF 文件到打印机是行不通的,所以只能乖乖的使用打印机 GDI 接口,吧 PDF 渲染到打印机 DC 上。

所以使用这个方法的前提是需要一个 PDF 渲染器,pdfium 是 Google Chromium 的项目的一部分,也是一个 PDF 渲染器,我们可以使用 pdfium 解析 PDF 文件,并渲染到打印机 DC 上。

由于这种方法工程量有点大,所以我最终选择了第三种方法。

方法三

既然使用一个 PDF 渲染器工程量比较大,那么我们是否可以吧 PDF 文件变成图片呢。把图片渲染到打印机 DC 上就很容易了。

-----       ------        -------------
| pdf | --> | image | --> | print DC API |-----       ------        -------------

由于我们后端是用 JAVA 实现的,所以转化这一步自然也可以挪到后端去做,后端我们选择了 Apache PDFBox 作为转化库。PDFbox 转化为图片就很容易了:

public 

通过以上代码我们发现 PDFBox 也是一个 PDF 渲染器,但是客户端程序一般不会去用 JAVA 做,若果你的客户端程序是 JAVA 写的,哪可直接使用打印功能了。

那么在 C++ 里打印图片功能如何实现呢:

// data 为图片数据,这里我们使用图片 base64 数据,注意要去掉 base64 头部。

用 C# 实现打印图片的功能估计会更容易一些。

合并问题

有时候我们会遇到需要将多个 PDF 合并到一起,PDFbox 有合并的功能:

public 

假设有两份 PDF 文件 pdf1,pdf2 这两份 PDF 文件都只含有一页,并且内容只有一行文字:

图 2-1 pdf1

图 2-2 pdf1

合并以后将会得到一份两页的 PDF 文件。这是因为 PDF 并不是 流式文档结构 的。

文档结构

摘自百度百科

PDF文件结构主要可以分为四个部分:

首部

用文本编辑器打开的时候就可以看到:%PDF-1.4 这样的字眼,其中最后一位就是PDF文件格式版本号,软件的版本号总要比文件格式的版本号高1,比如说Read 5能打开的内容就是4。 文件体

里面由若干个的obj对象来组成,类似这种形式:

3 0 obj
<<
/Type /Pages
/Count 1
/Kids [4 0 R]
>>
endobj

第一个数字称为对象号,来唯一标识一个对象的,第二个是产生号,是用来表明它在被创建后的第几次修改,所有新创建的PDF文件的产生号应该都是0,即第一次被创建以后没有被修改过。上面的例子就说明该对象的对象号是3,而且创建后没有被修改过。

对象的内容应该是包含在<< 和>>之间的,最后以关键字endobj结束。

交叉引用表

用来索引各个obj 对象在文档中的位置,以实现随机访问,它的形式是:

xref
0 8
0000000000 65535f
0000000009 00000n
0000000074 00000 n
0000000120 00000 n
0000000179 00000 n
0000000322 00000 n
0000000415 00000 n
0000000445 00000 n

xref说明一个交叉引用表的开始,交叉引用表的第一行0 8 说明下面各行所描述的对象号是从0开始,并且有8个对象。

0000000000 65535f,一般每个PDF文件都是以这一行开始交叉应用表的,说明对象0的起始地址为0000000000,产生号(generation number)为65535,也是最大产生号,不可以再进行更改,而且最后对象的表示是f, 表明该对象为free, 这里,大家可以看到,其实这个对象可以看作是文件头。

0000000009 00000n就是表示对象1,0000000009是其偏移地址,00000为5位产生号(最大为65535),0表明该对象未被修改过, n表示该对象在使用,区别与自由对象(f),可以更改。

尾部

Trailer
<<
/Size 8
/Root 1 0 R
>>
startxref
553
%%EOF

trailer 说明文件尾 trailer对象的开始。

/Size 8说明该PDF文件的对象数目。

/Root 1 0 R说明根对象的对象号为1。

Startxref

553说明交叉引用表的偏移地址,从而可以找到PDF文档中所有的对象的相对地址,进而访问对象。

%%EOF为文件结束标志。

图片合并

那么我们怎么才能得到我们想要的合并的效果呢,这里有个很土的方法。使用绘制图片的方法,倒着遍历图片,发现像素不是 (255,255,255) 则记录位置,讲下一张图片从这个偏移点开始绘制。最终得到的效果如下:

图 2-3 合并图片

public 

介绍几个关于 PDF 的实用库

  • pdfium -- Chromium PDF 操作库
  • xpdf -- 开源的 PDF 浏览器
  • Poppler -- 基于 xpdf 的 PDF 渲染库

c++ byte转cbitmap_关于 C++ 打印 PDF 打印及 PDF 转图片、合并相关推荐

  1. C#通过调用WinApi打印PDF文档类,服务器PDF打印、IIS PDF打印

    其他网站下载来的类,可以用于Winform.Asp.Net,用于服务器端PDF或其他文件打印. 直接上代码: using System; using System.Collections.Generi ...

  2. API(15)——打印系统开发(52)——WinForm开发(62)——C#通过调用WinApi打印PDF文档类,服务器PDF打印、IIS PDF打印

    其他网站下载来的类,可以用于Winform.Asp.Net,用于服务器端PDF或其他文件打印. 直接上代码: using System; using System.Collections.Generi ...

  3. java实现pdf的生成下载打印,java生成pdf电子账单,java生成pdf合同模板

    最近公司要做个生成pdf电子账单凭证的功能,由于这个公司没有任何代码可借鉴,这个时候我就必须得自己搞明白具体的每一步应该怎么做,引什么jar包?用什么方式去实现?这篇博客中会给出从头到尾及其详细的代码 ...

  4. 条形码的打印,pdf打印条形码

    条形码的打印效果如下图: 想实现上面的这种打印输出(内容可以自己设定),大概需要三大步. 第一步:选择jar包(其实实现条形码可以有多个方式,这里讲述下我最熟悉的);采用barcode4j这个jar包 ...

  5. java实现保存合同模板_java实现pdf的生成下载打印,java生成pdf电子账单,java生成pdf合同模板...

    最近公司要做个生成pdf电子账单凭证的功能,由于这个公司没有任何代码可借鉴,这个时候我就必须得自己搞明白具体的每一步应该怎么做,引什么jar包?用什么方式去实现?这篇博客中会给出从头到尾及其详细的代码 ...

  6. ABAP程序打印Spool Request到PDF

    其实打印到pdf完全不需要程序,只要你装了pdf打印生成软件,打印的时候选择pdf也就行了. 本文的程序非我所做,不过也不知道从哪里拿到的了.在不经意间看到了,于是在机器上测试下,发现有些地方需要更正 ...

  7. java pdf打印_Java 打印PDF文档

    本文将介绍如何在Java应用程序中打印PDF文档.一般有以下三种常见打印方式:静默打印 显示打印对话框打印 打印PDF时自定义纸张大小 使用工具:Free Spire.PDF for Java 创建运 ...

  8. pdf打印显示spoolsv.exe-应用程序错误的原因和解决方法

    有两个PDF文件在我的电脑上网络打印机HP1020打印总出现"spoolsv应用程序错误",其他文档如word,excel等都正常,不得不停止print spooler服务后,删除 ...

  9. ASP.net的PDF打印(水晶报表)[摘]

    如何部署ASP.NET Crystal Report (1) CRRedist2005_x86.msi 報表套件 (必要安裝) 取得方法在安裝有VS2005的機器上 C:\Program Files\ ...

最新文章

  1. 有序回归(ordinal regression)
  2. 你在网上看到的0失误游戏视频,可以是用AI生成的丨Demo在线可玩
  3. mysql 大写 小写_Mysql大小写敏感的问题
  4. windows10下编译dllib报错: ERROR: Failed building wheel for dlib
  5. 除非得到你的首肯,否则别人无法伤害你——Leo网上答疑(20)
  6. Elasticsearch 的使用,看这一篇就够了!
  7. 服务器400_瓜分400万filecoin的入门条件:现货服务器
  8. 金融数据分析与挖掘实战 4.2 Matplotlib(二)
  9. 线程池中 corePoolSize, maximumPoolSize, poolSize理解
  10. hart协议c语言,简述HART协议命令和语言
  11. Python一个命令开启http下载服务器
  12. 【测试Demo】JavaAPI操作阿里云对象存储(OSS)
  13. flink流处理示例开发
  14. 语音识别到底是怎么一回事?学习门槛真的那么高么?
  15. 19.JavaScript学习笔记——数组
  16. 新经济专家陈宗建:山东将是全球新经济主战场山东新旧动能转换专访报道
  17. 基于Python的二维有限元声波方程正演计算
  18. 安卓自动操作软件 AUTO.JS 4.1.1 ALPHA2 免费版下载
  19. 2022-05-19 列式数据库-Clickhouse
  20. 【python数据分析】pandas数据合并

热门文章

  1. 40-400-035-运维-优化-pt-variable-advisor优化工具
  2. Spark杀死我们提交的application
  3. 95-190-740-源码-WindowFunction-窗口流侧的窗口函数(外部函数)
  4. 【Flink】Flink 使用 ParameterTool 进行传参
  5. Kudu : tablet=null, server=null, status=Timed out: can not complete before timeout: Batch
  6. 常用的排序算法总结(一)
  7. 热点和秒杀来临前要做的5件事
  8. OAuth2.0 原理流程及其单点登录和权限控制
  9. Java中json格式的字符串数组,list,json,map相互转换
  10. MySQL学习-排序与分组函数