问题:

使用Adobe Reader将一份pdf文件通过我的虚拟打印机输出后(输出的是中间文件,等同于EMF文件),查看的时候发现有时候是乱码。最简单的必现步骤:

1、使用Adobe Reader打开pdf文件,选择我的虚拟打印机打印(取消掉adobe打印高级选项中“作为图像打印”),生成中间文件。

2、此时可以通过工具查看这个中间文件(EMF),发现并没有乱码。

3、关闭刚才打印的Adobe Reader打开的Pdf文件,再次查看中间文件,这时候就乱码了。

分析:

根据上面的必现步骤,再测试使用FoxitReader、JisuPdf打开pdf文件然后选择我的虚拟打印机打印,都没有复现。此外,生成中间文件后,即使重新用Adobe Reader打开pdf,查看中间文件的时候,仍然是乱码。

根据上述现象,去对比使用JisuPdf和Adobe Reader执行打印后生成的中间文件的区别,发现存在比较大的区别。猜测Adobe Reader在打印输出的时候为不存在的字体创建了临时字体文件,所以Adobe Reader在没关闭的时候查看不会乱码,一旦关闭了就删掉了临时文件,所以就乱码了。

比如我第一次输出的是中文文件A,不关闭Adobe Reader,查看A正常。关闭Adobe Reader查看A乱码,再次打开Adobe Reader并打印出B,查看A乱码,B不乱码。由此说明,创建的这个临时字体文件,还是和对应的中间文件相关联的,并不是所有的都一样。

我分别比较上述A和B文件内容上的区别,发现其中一个区别就是EMR_EXTCREATEFONTINDIRECTW结构中的字段不一样,而且有个比较明显的字段内容lfFaceName不一样。

查看MSDN上对EMR_EXTCREATEFONTINDIRECTW的介绍,基本可以确定就是Adobe Reader在打印输出这个pdf文件的时候创建了临时字体文件,所以一旦Adobe Reader进程关闭了,就会删除临时字体文件导致中间文件乱码。这也说明了为什么直接输出到打印机时不会,而通过我的虚拟打印机输出中间文件,如果在同一台机器上不关闭Adobe Reader进程时将中间文件输出到真实打印机不会乱码,而在另一台机器上输出会乱码。

为了确认这个问题,我使用JisuPdf和FoxitReader再分别打印出中间文件C和D。用A和C、A和D比较,发现C和D中都不存在EXTCREATEFONTINDIRECTW这个记录,由此证明以上结论。

也就是说,我这个pdf文件中使用了非内嵌并且系统尚未安装的字体,pdf阅读器在打开的时候使用了相关的字体去替换显示。

进一步分析:

现在问题转变成,为什么Adobe Reader在打印时会调用CreateFontIndirect(生成EXTCREATEFONTINDIRECTW记录),而JisuPdf和FoxitReader却不会?

另外如果生成的EMF中有EXTCREATEFONTINDIRECTW记录,如何根据这个记录创建好需要的字体从而不乱码显示呢?

经过自己解析EMF中的记录进行绘制,发现是可以通过调用CreateFontIndirect函数去实现这条EXTCREATEFONTINDIRECTW记录的,但是仍然是乱码,也就是说无法找到实际能与之匹配上的字体。

解决方案:

问题拖了比较久,最终也没找到好的解决方案,目前采用的办法是对于这种情况,要么直接使用勾选上adobe reader的矢量图打印(这种方式比较万能,原理是将每页文档都转变成一张图片,相应的占据的空间也大,并且慢很多),要么就干脆换一个pdf阅读器,比如国产的foxit。

 

pdf打印乱码问题的更多相关文章

  1. 开源免费且稳定实用的.NET PDF打印组件itextSharp(.NET组件介绍之八)

    在这个.NET组件的介绍系列中,受到了很多园友的支持,一些园友(如:数据之巅. [秦时明月]等等这些大神 )也给我提出了对应的建议,我正在努力去改正,有不足之处还望大家多多包涵.在传播一些简单的知识的 ...

  2. AndroidStudio开发Java工程(解决java控制台中文打印乱码+导入jar包运行工程)

    这篇分享一点个人AS开发java工程经验,虽然有时候还是得打开eclipse来运行java项目,但能用AS的时候还是尽量用AS,毕竟一个字,爽~ 废话不多说,进入正题. 一.开发Java工程 你有两种 ...

  3. 解决idea控制台打印乱码问题

    idea控制台打印乱码,用起来总别扭,也是在网上搜索了一番,靠一点猜测解决了. 首先打开你自己的idea的安装目录下(即右键桌面图标,点击打开文件所在位置),然后找到idea.exe.vmoption ...

  4. 基于iTextSharp的PDF操作(PDF打印,PDF下载)

    基于iTextSharp的PDF操作(PDF打印,PDF下载) 准备 1. iTextSharp的简介 iTextSharp是一个移植于java平台的iText项目,被封装成c#的组件来用于C#生成P ...

  5. NetSuite实现pdf打印中的条形码的功能

    2020-11-27 提起NS,在程序员这一块应该不怎么被人知道,算是比较小众的一门技术了,毕竟Netsuite兴起的时间算不上早,进入中国的时间更晚,除了从事这一块的程序员,可能都没有见过,恰好我是 ...

  6. .Net下的PDF打印

    简单研究了一下.Net下的PDF打印,一路发现了很多小坑. 第三方组件 这里使用的解析PDF的组件是mupdf,特点和C#调用在 这里 有介绍. 实现的功能 支持页面大小.边距.打印机选择.打印机dp ...

  7. 驰骋CCFlow开源工作流程引擎如何设置PDF打印

    前言 经常有驰骋CCFlow爱好者朋友提问关于打印相关问题.在这篇博文中大家介绍一下工作流引擎CCFlow的HTML打印和PDF打印,针对Java版本和.NET版本有不同的操作步骤,包括开关设置.水印 ...

  8. Java 创建PDF打印小册子

    概述 PDF打印小册子是指将PDF格式文档在打印成刊物前需要提前进行的页面排版,以便在打印后装订成册.下面以Java代码展示如何来实现.这里调用Free Spire.PDF for Java中的Pdf ...

  9. libreoffice转换文件为pdf文件乱码问题解决办法

    最近系统需要一个office文件预览功能 解决方案为使用libreoffice将office文件转换为pdf文件,然后使用swftools将pdf文件转换为swf文件 最后在前台使用flexpaper ...

随机推荐

  1. DTD简单使用

    DTD:Document Type Definition DTD是一种简单的XML约束模式语言 DTD文档必须以utf-8或unicode编码 注释方式与HTML.XML文档相同 DTD文档的引用:紧 ...

  2. Android程序版本更新--通知栏更新下载安装(转)

    Android应用检查版本更新后,在通知栏下载,更新下载进度,下载完成自动安装,效果图如下: 检查当前版本号 AndroidManifest文件中的versionCode用来标识版本,在服务器放一个新 ...

  3. servlet中访问mysql无法包含中文的解决

    最近写servlet应用发现,如果我的sql语句中包含英文,访问数据库就失败,而我数据库的编码是utf8 -- UTF-8 Unicode,而我servlet的字符也已经转为UTF-8 ,还是不行. ...

  4. struts2学生信息管理系统篇章③

    package com.java1234.util; public class PageUtil { //传进来四个参数,tagetUtil是跳转链接的头部,totalNum是总个数,currentP ...

  5. JavaScript省市联动

    <html> <head> <title>JS省市二级联动菜单,整理收集.</title> </head> <body bgcolor ...

  6. [转] NSString / NSMutableString 字符串处理,常用代码

     原文 :  http://justcoding.iteye.com/blog/1405951 Objective-C 中核心处理字符串的类是 NSString 与 NSMutableString , ...

  7. PHP简单计算器

    工作之余,写着玩的. <?php $num1 = $_POST['num1']; $num2 = $_POST['num2']; $yusuan = $_POST['yusuan']; $jie ...

  8. 【USACO 3.1.6】邮票

    [描述] 已知一个N枚邮票的面值集合(如,{1分,3分})和一个上限K ——表示信封上能够贴K张邮票.计算从1到M的最大连续可贴出的邮资. 例如,假设有1分和3分的邮票:你最多可以贴5张邮票.很容易贴 ...

  9. 【POJ1753】Flip Game

    [题目大意] 有一个4x4规格的一个棋盘,现在有16个一面黑一面白的棋子分布在这个棋盘上. 翻转一个棋子能够使它以及它上下左右的四个棋子从黑变白,从白变黑. 现在问你至少要经过多少次操作才能够使得整个 ...

  10. JVM内存管理及垃圾回收

    一.JVM内存的构 Java虚拟机会将内存分为几个不同的管理区,这些区域各自有各自的用途,根据不同的特点,承担不同的任务以及在垃圾回收时运用不同的算法.总体分为下面几个部分: 程序计数器(Progra ...