免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5283 | 回复: 10
打印 上一主题 下一主题

创建PDF 中文乱码 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-12-16 00:02 |只看该作者 |倒序浏览
本帖最后由 jjboy110 于 2013-12-16 00:04 编辑

大家好,

我在使用另一个产品的导出PDF功能时候发现带有中文的导出都成为了乱码,于是开始研究perl创建PDF文档的一些模块,发现在调用这些模块的时候perl对文字的处理使用了字体的概念,而且perl内置了大概14中字体,对于系统其他缺省中文字体无法识别(我认为),我了解到两个模块拥有创建PDF文件的能力,PDF::Reuse 和 PDF::API2,对于Reuse,我分析了源码,暂时没找到如何调用系统字体文件将PDF画出来的过程,我强制加了系统的中文字体名称,得到的PDF文件是无法打开的,所以应该需要找到别的方案;

perl的PDF模块应该还是有问题,不知道前人是否了解如何修改模块?
或者是谁做过类似的创建PDF的任务,是否碰到中文乱码的问题。
求教
代码如下:
  1. #!/usr/bin/perl
  2.     use PDF::Reuse;
  3.     prFile('myFile.pdf');
  4.     my @f1 = prFont('Times-Roman');     # Just setting a font
  5.     prFontSize(40);
  6.     prText(180, 790, "This is a heading黑\n");

  7.     prEnd();
复制代码

论坛徽章:
0
2 [报告]
发表于 2013-12-16 14:21 |只看该作者

论坛徽章:
0
3 [报告]
发表于 2013-12-17 00:05 |只看该作者
回复 2# 兰花仙子

谢谢回复,
我了解一点编码,我觉得这个代码在创建PDF的时候编码倒是没问题,在创建PDF文件的时候可能方式跟普通的文本文件方式不一样,需要涉及到文件字体,调用字体文件将需要写入PDF的字符画出来,但是对于中文没有找到相应的字体,所以出现了乱码;
我觉得应该模块设计的有些问题,可能需要在深层次的跟一跟。


   

论坛徽章:
7
巳蛇
日期:2014-04-10 08:54:57白羊座
日期:2014-04-22 20:06:262015年亚洲杯之沙特阿拉伯
日期:2015-02-10 14:18:532015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之吉达阿赫利
日期:2015-06-02 11:34:112015亚冠之武里南联
日期:2015-06-24 12:13:082015亚冠之阿尔纳斯尔
日期:2015-08-03 09:08:25
4 [报告]
发表于 2013-12-17 08:56 |只看该作者
回复 1# jjboy110


    我没用过PDF::Reuse,但是我对TeX系统这些还是比较熟的,对字体以及由字体引起的乱码有一定的了解,很显然,Times-Roman这个字体并不包含中文字符,所以你的输出是不可能正常显示中文的,请用包含中文字符的字体输出中文。

论坛徽章:
7
巳蛇
日期:2014-04-10 08:54:57白羊座
日期:2014-04-22 20:06:262015年亚洲杯之沙特阿拉伯
日期:2015-02-10 14:18:532015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之吉达阿赫利
日期:2015-06-02 11:34:112015亚冠之武里南联
日期:2015-06-24 12:13:082015亚冠之阿尔纳斯尔
日期:2015-08-03 09:08:25
5 [报告]
发表于 2013-12-17 09:36 |只看该作者
本帖最后由 Monox 于 2013-12-17 09:37 编辑

我实际去安装了一下这个模块,看了下文档,试验了一下,先引用文档的一段:
  1. prTTFont - select and embed a TrueType font

  2.   prTTFont ( "/path/to/font/file.ttf" )

  3. This function is equivalent to prFont except that rather than restricting you to the list of core built-in fonts, it allows you to select an external TrueType font file and have it embedded in your PDF document. Using TrueType fonts also enables the prText function to accept UTF-8 strings, which allows you to use characters outside the Mac-Roman/Win-ANSI character sets used by the built-in fonts.
复制代码
下面是我的试验代码,你可能得根据需要替换成你系统上的字体才行:
  1. use PDF::Reuse;
  2. use strict;
  3. use utf8;

  4. prFile('myFile.pdf');
  5. prTTFont('/usr/share/fonts/truetype/FZSongTi.ttf');
  6. prFontSize(20);
  7. prText(180, 790, "This is a heading with 中文");
  8. prEnd();
复制代码
注意,除了选择包含中文字符的字体外,utf utf8也是必要的

论坛徽章:
5
丑牛
日期:2014-01-21 08:26:26卯兔
日期:2014-03-11 06:37:43天秤座
日期:2014-03-25 08:52:52寅虎
日期:2014-04-19 11:39:48午马
日期:2014-08-06 03:56:58
6 [报告]
发表于 2013-12-17 15:29 |只看该作者

好厉害的!很赞啊{:2_172:}
回复 5# Monox


论坛徽章:
0
7 [报告]
发表于 2013-12-23 13:59 |只看该作者
回复 5# Monox

非常感谢,抱歉回复的晚了,赞一个先!~{:2_172:}

您对编码的了解的确相当深刻,我也尝试使用prTTFont函数,但是没有使用utf8编码。
我对使用的这个产品代码分析,发现的确在以前版本的代码上是使用过prTTFont函数来写入PDF文件,但是在最新版本的代码将这部分功能注释掉,可能是由于在前端调用的时候发现与不同版本系统字体兼容起来比较困难,最后使用的是prFont函数来获取系统字体。
就我的理解,如果使用prFont调用系统字体应该能够使用系统中所有字体,就这个函数的解释也是可以获取外部的字体,不清楚是我的调用方式不对还是这个函数对于其他字体无法调用所致。

   

论坛徽章:
7
巳蛇
日期:2014-04-10 08:54:57白羊座
日期:2014-04-22 20:06:262015年亚洲杯之沙特阿拉伯
日期:2015-02-10 14:18:532015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之吉达阿赫利
日期:2015-06-02 11:34:112015亚冠之武里南联
日期:2015-06-24 12:13:082015亚冠之阿尔纳斯尔
日期:2015-08-03 09:08:25
8 [报告]
发表于 2013-12-23 23:39 |只看该作者
回复 7# jjboy110

事实上,我回答你的问题的时候除大致看了一下文档以外,也看过模块的源代码。题外话,源代码的注释和很多变量名都不是英文,但是并不特别影响理解。
我再重新引用一遍我之前帖出的对文档的引用的一部分:
  1. This function is equivalent to prFont except that rather than restricting you to the list of core built-in fonts, it allows you to select an external TrueType font file and have it embedded in your PDF document.
复制代码
,这句说的很清楚,prFont 被限制(restricting you to) 只能使用内建(built-in)的那个字体列表。也就是说你不可以任意使用系统字体,而只能从很有限的那几个预定义的字体里选择,不过,另一方面,这个内建的列表其实就保存在一个哈希里边,你如果真要使用prFont的话可以对这个列表进行扩展,你可以看看代码看这个列表是如何创建的(其实就是给字体取个别名,然后在系统里找相应的字体,并进行加载,模块里有好几个帮助函数用来完成这项工作)。对于回答你现在的问题我是不打算再看一遍文档或代码了,你感兴趣可以研究下,不过对你这种情况你真的直接用prTTFont就好了,这是模块作者提供的接口,也就是很正常的用法,反而去poke模块内部函数之类的做法不是应该首要考虑的。

论坛徽章:
0
9 [报告]
发表于 2013-12-24 11:27 |只看该作者
回复 8# Monox


    是的,对内部字体的理解应该是当前运行系统存在的字体,不应该是perl这个模块包含的字体,从prFont的解释中能看出来这个函数的作用是调用系统存在的字体,至于为什么无法实现这个功能,可能还需要发现发现;prTTFont的作用应该是制定任意的字体文件,这种文件可以不放在系统的Fonts目录中,甚至在PDF文件中包含的字体也可以直接引用。

论坛徽章:
7
巳蛇
日期:2014-04-10 08:54:57白羊座
日期:2014-04-22 20:06:262015年亚洲杯之沙特阿拉伯
日期:2015-02-10 14:18:532015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之吉达阿赫利
日期:2015-06-02 11:34:112015亚冠之武里南联
日期:2015-06-24 12:13:082015亚冠之阿尔纳斯尔
日期:2015-08-03 09:08:25
10 [报告]
发表于 2013-12-24 14:50 |只看该作者
jjboy110 发表于 2013-12-24 11:27
对内部字体的理解应该是当前运行系统存在的字体,不应该是perl这个模块包含的字体,从prFont的解释中能看出来这个函数的作用是调用系统存在的字体
这个模块本身又没打包字体,也没动态生成字体,prFont调用的当然是系统存在的字体,不过,我说的很清楚了内部字体(准备翻译是内建字体列表里的字体)不等同与系统存在的字体。我都说除了我引用的那段文档以外,我有看过这个模块的代码,你不想相信我说的话坚持那样理解我也没有办法,因此这是我最后一次回复这个帖子。不过,我在下面会给出更充分的说明,至于你是否仍然不相信,那我就管不着也不想管了。
首先,我引用的那段文档用的是the list of core built-in fonts,注意是built-in不是system,是“内建”,不是“系统”,另外,我从来没说过是这个模块包含的字体,是说这个list,是由这个模块预定义的,而且我在上次的回贴中也说得很清楚,所谓预定义,也就是给字体取个别名,加载一下这个字体,然后把相关信息存在一个哈希里边,这个哈希就是实质上的the list of core built-in fonts,而那个预定义的list概念来说就是Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic, Courier, Courier-Bold, Courier-Oblique, Courier-BoldOblique, Helvetica, Helvetica-Bold, Helvetica-Oblique, Helvetica-BoldOblique or abbreviated TR, TB, TI, TBI, C, CB, CO, CBO, H, HB, HO, HBO. (Symbol and ZapfDingbats or abbreviated S, Z, also belong to the predefined fonts, but there is something with them that I really don't understand. You should print them first on a page, and then use other fonts, otherwise they are not displayed.)(这个list引用自文档,不保证和实际的代码实现完全一致)。
另外,我之所以提我看过代码,除了让自己的话更有说服力一点以外,更重要的是因为你说你自己写看过代码,所以我是想传达给你你如果还搞不清楚的话就自己去看看代码,这也是我不想再管的另一个原因。下面贴一部分那个模块的代码来说明问题:
  1. sub prFont
  2. {   my $nyFont = shift; # $nyFont 就是你提供给prFont的参数
  3.     my ($intnamn, $extnamn, $objektnr, $oldIntNamn, $oldExtNamn);
  4.    
  5.     if (! $pos)
  6.     {  errLog("No output file, you have to call prFile first");
  7.     }
  8.     $oldIntNamn = $aktuellFont[foINTNAMN];
  9.     $oldExtNamn = $aktuellFont[foEXTNAMN];
  10.     if ($nyFont)
  11.     {  ($intnamn, $extnamn, $objektnr) = findFont($nyFont);# $nyFont是在findFont里进行处理的
  12.     }
  13.     else
  14.     {   $intnamn = $aktuellFont[foINTNAMN];
  15.         $extnamn = $aktuellFont[foEXTNAMN];
  16.     }
  17.     if ($runfil)
  18.     {  $log .= "Font~$nyFont\n";
  19.     }
  20.     if (wantarray)
  21.     {  return ($intnamn, $extnamn, $oldIntNamn, $oldExtNamn, \%font);
  22.     }
  23.     else
  24.     {  return $intnamn;
  25.     }
  26. }
复制代码
  1. sub findFont()
  2. {  no warnings;
  3.    my $Font = shift || '';#这个是传进来的参数
  4.       
  5.    if (! (exists $fontSource{$Font}))        #  Fonten måste skapas 如果在 %fontSource里找不到的话
  6.    {  if (exists $stdFont{$Font})
  7.       {  $Font = $stdFont{$Font};} # 那么可以是%stdFont里的字体
  8.       else
  9.       {  $Font = $genFont; }                 # Helvetica sätts om inget annat finns,否则$Font就取$genFont,这个就是Helvetica,原注释应该想说这种意思,虽然用的不是英文说的,我上次看过$genFont的定义,你不相信的话可以自己在那个模块里找这个的定义,下面如果引用其他代码的时候有碰巧有$genFont的定义的话我会注释出来,要是没有我就不单独给你贴这个的定义了
  10.       if (! (exists $font{$Font})) # 如果这个字体不存在于 %font里(但是它要么在%stdFont里,要么就是Helvetica,不可能是其它)
  11.       {  $objNr++;
  12.          $fontNr++;
  13.          my $fontAbbr           = 'Ft' . $fontNr; #定义别名
  14.          my $fontObjekt         = "$objNr 0 obj<</Type/Font/Subtype/Type1" .
  15.                                "/BaseFont/$Font/Encoding/WinAnsiEncoding>>endobj\n";#加载Type1字体对应的encoding信息(这个只对Type1字体是必要的,对于我们现在常用的ttf和otf字体,也就是TrueType字体没用)
  16.          $font{$Font}[foINTNAMN]      = $fontAbbr; #加到%font里
  17.          $font{$Font}[foREFOBJ]       = $objNr;
  18.          $objRef{$fontAbbr}           = $objNr;
  19.          $fontSource{$Font}[foSOURCE] = 'Standard';#加到%fontSource里
  20.          $objekt[$objNr]              = $pos;
  21.          $pos += syswrite UTFIL, $fontObjekt;
  22.       }
  23.    }
  24.    else # 如果在%fontSource里
  25.    {  if (defined $font{$Font}[foREFOBJ])       # Finns redan i filen
  26.       {  ; }
  27.       else # 不在%font里,而foSOURCE是Standard的话,就做与上面相同的别名和encoding的定义
  28.       {  if ($fontSource{$Font}[foSOURCE] eq 'Standard')
  29.          {   $objNr++;
  30.              $fontNr++;
  31.              my $fontAbbr           = 'Ft' . $fontNr;
  32.              my $fontObjekt         = "$objNr 0 obj<</Type/Font/Subtype/Type1" .
  33.                                       "/BaseFont/$Font/Encoding/WinAnsiEncoding>>endobj\n";
  34.              $font{$Font}[foINTNAMN]    = $fontAbbr;
  35.              $font{$Font}[foREFOBJ]     = $objNr;
  36.              $objRef{$fontAbbr}         = $objNr;
  37.              $objekt[$objNr]            = $pos;
  38.              $pos += syswrite UTFIL, $fontObjekt;
  39.          }
  40.          else # 如果不是standard 就不加载相应的字体属性,但是相应的,这个字体必需是已经在当前处理的PDF文件里内嵌了的字体
  41.          {  my $fSource = $fontSource{$Font}[foSOURCE];
  42.             my $ri      = rindex($fSource, '_');
  43.             my $Source  = substr($fSource, 0, $ri);
  44.             my $Page    = substr($fSource, ($ri + 1));
  45.             
  46.             if (! $fontSource{$Font}[foORIGINALNR])
  47.             {  errLog("Couldn't find $Font, aborts");
  48.             }
  49.             else
  50.             {  my $namn = extractObject($Source, $Page,
  51.                                         $fontSource{$Font}[foORIGINALNR], 'Font');
  52.             }
  53.          }
  54.       }
  55.    }

  56.    $aktuellFont[foEXTNAMN]   = $Font;
  57.    $aktuellFont[foREFOBJ]    = $font{$Font}[foREFOBJ];
  58.    $aktuellFont[foINTNAMN]   = $font{$Font}[foINTNAMN];
  59.    $aktuellFont[foTYP]       = $font{$Font}[foTYP];
  60.    
  61.    $sidFont{$aktuellFont[foINTNAMN]} = $aktuellFont[foREFOBJ];
  62.    if (! $pos)
  63.    {  errLog("No output file, you have to call prFile first");
  64.    }

  65.    return ($aktuellFont[foINTNAMN], $aktuellFont[foEXTNAMN], $aktuellFont[foREFOBJ]);  
  66. }
复制代码

  1. our %stdFont = # 而 stdFont就是所谓的内建列表,(之前我还没说,事实上这些字体对PDF阅读器来说它知道如何去系统目录下找对应的系统字体,如果找不到的话会用相近的字体进行替换,所以=>右边给的其实是PDF文档认识的字体名而非系统内的字体的字体名,PDF文档认识的字体并不多)
  2.        ('Times-Roman'           => 'Times-Roman',
  3.         'Times-Bold'            => 'Times-Bold',
  4.         'Times-Italic'          => 'Times-Italic',
  5.         'Times-BoldItalic'      => 'Times-BoldItalic',
  6.         'Courier'               => 'Courier',
  7.         'Courier-Bold'          => 'Courier-Bold',
  8.         'Courier-Oblique'       => 'Courier-Oblique',
  9.         'Courier-BoldOblique'   => 'Courier-BoldOblique',
  10.         'Helvetica'             => 'Helvetica',
  11.         'Helvetica-Bold'        => 'Helvetica-Bold',
  12.         'Helvetica-Oblique'     => 'Helvetica-Oblique',
  13.         'Helvetica-BoldOblique' => 'Helvetica-BoldOblique',
  14.         'Symbol'                => 'Symbol',
  15.         'ZapfDingbats'          => 'ZapfDingbats',
  16.         'TR'  => 'Times-Roman',
  17.         'TB'  => 'Times-Bold',
  18.         'TI'  => 'Times-Italic',
  19.         'TBI' => 'Times-BoldItalic',
  20.         'C'   => 'Courier',
  21.         'CB'  => 'Courier-Bold',
  22.         'CO'  => 'Courier-Oblique',
  23.         'CBO' => 'Courier-BoldOblique',
  24.         'H'   => 'Helvetica',
  25.         'HB'  => 'Helvetica-Bold',
  26.         'HO'  => 'Helvetica-Oblique',
  27.         'HBO' => 'Helvetica-BoldOblique',
  28.         'S'   => 'Symbol',
  29.         'Z'   => 'ZapfDingbats');
复制代码
而%fontSource一开始是空的,就是通过调用上面的findFont()把标准字体在需要的时候慢慢加进去的。
好吧,我的解释就到这里,多的话不说了,回复这个帖子感觉真浪费时间,要不是因为我对现在的老板很不爽,也不会有闲功夫放下自己的工作来回这么无聊的贴(好吧,对于自己的回复被实质的当成了没有任何意义的东西而稍微激动了一下,其实也不是那么要紧)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP