PDF,好东西,就是麻烦,以前鼓捣ThingkingRock的时候打过交道,感觉挺复杂,现在捣鼓GLPI时又遇到了,GLPI用的是ezpdf。
先说点闲话,有些比较偏门的东西是真难找啊,比如今天要说的,算不上是学术问题,也没有企业级的支持(如果你愿意打电话咨询GLPI官方又懂法语我没话说),只能在网上大海捞针的找,一方面这是很辛苦的,查阅各类资料几十篇(前提还是你得能找到),写下来也就寥寥百余字,所以我一般愿意把我翻到的资料列在后面作为参考,或许能为别人省点力气;另一方面就是搜索引擎的功劳了,记得还没有google和百度的时候,只有一个yahoo分类目录,要搜问题就得到几个大型论坛比如CSDN里去翻,信息量就窄多了,另外搜索引擎的质量在这里也起到了很大的作用,信息重复率高不高、能否最快速度找到原创内容、信息关联度、是否有用等因素都关系到用户花费时间的多少,这也是我很少用百度的原因之一,还有就是感觉百度的英文资料差太多了。────谨以此纪念我几乎24小时的连续工作以及疲惫的眼睛和脖子。
EZPDF
EZPDF一般是不支持unicode多字节编码的,不过还是作了一些尝试,毕竟是GLPI内置的,搞定了用着方便。首先EZPDF使用afm字体,要得到afm字体,需要用到ttf2tex,在pdfTeX包里:
$ aptitude install ttf2tex |
$ aptitude install ttf2tex
转换字体,这次我不用宋体了,用于打印的,还是仿宋看着舒服,用了个方正仿宋简体:
$ ttf2afm -e T1-WGL4.enc -o fzfangsong.afm fzfangsong.TTF |
$ ttf2afm -e T1-WGL4.enc -o fzfangsong.afm fzfangsong.TTF
试了试不能用,然后用afm2font处理:
$ php5 afm2font.php fzfangsong |
$ php5 afm2font.php fzfangsong
得到php_fzfangsong.font等文件,但这些文件无论怎么套上ezpdf都是不行,什么文字都没了,pdf文件中倒是显示了正确的字体名:
fzfangsong
Type1
Not embedded
再在网上翻资料,简直就是钻到TeX用户堆儿里去了,忽然发现windows字体应该是TrueType字体,而ezpdf使用的难道是Type1字体,两者之间还需要转换?终于查到基于texlive2008的中文绿色免安装tex系统中有打包的字体,这里面有给TeX用的Type1字体,就是大了点,“dottexlive2008.tar.bz2 仅包含全部字体包和相关宏包”一共是685M,拉下来,好在网速还算很快。把里面的afm字体单独解压出来:
$ tar xjvf /big1/dottexlive2008.tar.bz2 .texlive2008/texmf-var/fonts/afm/
$ cd .texlive2008/texmf-var/fonts/afm/cjk/gbkfs
$ cat gbkfs??.afm > gbkfs.afm |
$ tar xjvf /big1/dottexlive2008.tar.bz2 .texlive2008/texmf-var/fonts/afm/
$ cd .texlive2008/texmf-var/fonts/afm/cjk/gbkfs
$ cat gbkfs??.afm > gbkfs.afm
拿这个gbkfs.afm配置到ezpdf里,能打出字来了,但中文还是问号,估计是因为数据是utf-8编码,而字体是gbk编码的?修改class.ezpdf.php
,在function ezProcessText
的第一行加上(参考:ezpdf打印德文的处理):
$text = mb_convert_encoding($text, 'gbk', 'utf-8'); |
$text = mb_convert_encoding($text, 'gbk', 'utf-8');
还是问号,难道字体要嵌入pdf才行?
结果又查到一篇Choosing a PHP PDF generation library for Dokeos,说ezpdf根本、确实、100%就不支持utf-8编码,合着白折腾了。
到ezpdf官网确认一下,最近的新闻是17 June 2006
的,比上面那篇文章还晚,看来ezpdf确实是没法用了。用tcpdf吧,不过在glpi的roadmap里,换上tcpdf是要在0.80实现的,前面还有100+ ticket没完成,也是遥遥无期啊,只好自己动手,丰衣足食了。
TCPDF
从官网下载tcpdf放到php的include路径下,对照字体设置方法来准备字体:
$ cd tcpdf/fonts
# Simsun字体好像还不是truetype(嵌入了点阵字体),转换有些问题,先拿方正字体来用,正好方正仿宋简体打印用更好看
#$ ln -s /big2/fonts/xpfonts/simsun.ttf
# 注意文件名转为小写
$ ln -s /big2/fonts/fzfont/fzfangsong.TTF fzfangsong.ttf
$ utils/ttf2ufm -a -F fzfangsong.ttf
# 生成了3个新文件
$ ls
fzfangsong.ufm fzfangsong.t1a fzfangsong.afm
# 我选的这个方正字体应该是TrueTypeUnicode字体,用TrueType转的不显字
# 文中有说明,在ttf ufm后面还可以跟参数,这里的false代表不将字体嵌入PDF文档,
# 那样会使pdf文件大小增大为自身+字体文件大小,很恐怖的
$ php5 -q utils/makefont.php fzfangsong.ttf fzfangsong.ufm false
# 生成可用的文件了
$ls
fzfangsong.z fzfangsong.php |
$ cd tcpdf/fonts
# Simsun字体好像还不是truetype(嵌入了点阵字体),转换有些问题,先拿方正字体来用,正好方正仿宋简体打印用更好看
#$ ln -s /big2/fonts/xpfonts/simsun.ttf
# 注意文件名转为小写
$ ln -s /big2/fonts/fzfont/fzfangsong.TTF fzfangsong.ttf
$ utils/ttf2ufm -a -F fzfangsong.ttf
# 生成了3个新文件
$ ls
fzfangsong.ufm fzfangsong.t1a fzfangsong.afm
# 我选的这个方正字体应该是TrueTypeUnicode字体,用TrueType转的不显字
# 文中有说明,在ttf ufm后面还可以跟参数,这里的false代表不将字体嵌入PDF文档,
# 那样会使pdf文件大小增大为自身+字体文件大小,很恐怖的
$ php5 -q utils/makefont.php fzfangsong.ttf fzfangsong.ufm false
# 生成可用的文件了
$ls
fzfangsong.z fzfangsong.php
现在弄个简单的例子,能够用tcpdf输出pdf文件了,里面要有这么一句:
$pdf->SetFont('fzfangsong', '', 16); |
$pdf->SetFont('fzfangsong', '', 16);
现在文件尺寸还是太大,因为字体依然是全嵌入的(如果是embedded subset更好,不过TCPDF现在还做不到)。按照那篇文章修改fzfangsong.php文件:
//$type='TrueTypeUnicode';
$type='cidfont0';
...
//$dw=600;
$dw=1000;
...
//$enc='';
$diff='';
//$file='fzfangsong.z';
//$ctg='fzfangsong.ctg.z';
$originalsize=3548232;
// Chinese Simplified
$enc='UniGB-UTF16-H';
$cidinfo=array('Registry'=>'Adobe', 'Ordering'=>'GB1','Supplement'=>2);
include(dirname(__FILE__).'/uni2cid_ag15.php'); |
//$type='TrueTypeUnicode';
$type='cidfont0';
...
//$dw=600;
$dw=1000;
...
//$enc='';
$diff='';
//$file='fzfangsong.z';
//$ctg='fzfangsong.ctg.z';
$originalsize=3548232;
// Chinese Simplified
$enc='UniGB-UTF16-H';
$cidinfo=array('Registry'=>'Adobe', 'Ordering'=>'GB1','Supplement'=>2);
include(dirname(__FILE__).'/uni2cid_ag15.php');
现在生成的pdf文件尺寸倒是很小了,但用eivnce看文字全部是空白,这是在Linux下看,跑到windows下看正常,而且完美,gb和big5编码都能显示。又用其它一些字体试了试,都是windows正常但linux下看不了,应该是刚才作的步骤正确,但与平台配合起来还欠点什么。
仔细对比了上面生成的两种pdf文件,以及另外一个用openoffice.org生成的pdf文件(这个应该比较标准吧),发现还是openoffice.org生成的文件又小、效果又好,用的是“已嵌入子集/embedded subset”方式,
最终查到,我基本上已经作好了,问题出在evince身上,安装poppler-data
包(evince用这个来处理中文)后,完美解决,字体为非嵌入not embedded方式。生成的pdf文件非常小,用windows和evince浏览也都正常。混合gb2312和big5编码的内容也没问题:evince下中文出西欧字符不出,windows下Adobe Reader只出gb2312的字,Foxit Reader全部中文和西欧字符完美。而在全内嵌字体的情况下,用evince查看big5字符都是小黑块。
hack GLPI
接下来是大工程,把ezpdf换成tcpdf。修改inc/export.function.php
的function displaySearchFooter
函数,把case PDF_OUTPUT_LANDSCAPE
和case PDF_OUTPUT_PORTRAIT
两部分的内容都换成TCPDF的处理,源数据还借用原来的$PDF_HEADER
,$PDF_ARRAY
。除了纸张参数不一样,两部分的处理是相同的,下面是一个简单的例子:
// --------
require_once('tcpdf/tcpdf.php');
global $PDF_HEADER,$PDF_ARRAY;
// create new PDF document
$pdf = new TCPDF('L', PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// set document information
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Fwolf');
$pdf->SetTitle($title);
$pdf->SetSubject('GLPI Export');
$pdf->SetKeywords('TCPDF, PDF, GLPI, export, fwolf, work, asset');
// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// remove default header/footer
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(true);
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
//set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP - 10, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
//set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
//set some language-dependent strings
$pdf->setLanguageArray($l);
// ---------------------------------------------------------
// add a page
$pdf->AddPage();
// Column width, by conteng max length
$w_max = $pdf->getPageWidth() - PDF_MARGIN_LEFT - PDF_MARGIN_RIGHT;
$w = array();
foreach($PDF_ARRAY as $row) {
foreach ($row as $i => $cell) {
// Chinese utf8 2 words length=6, should be 4 here
$cell = trim($cell);
$l = mb_strwidth($cell, 'utf-8');
// FZFangSong is not fixed font, so add some to en chars
// eg: 哈哈abc
// strlen = 6 + 3 = 9
// mbstrlen = 5
// mbstrwidth = 7
$l += ($l + mb_strlen($cell, 'utf-8') - strlen($cell))
* 0.3;
if (!isset($w[$i])) {
$w[$i] = $l;
}
elseif ($w[$i] < $l) {
$w[$i] = $l;
}
}
}
// Compare with max width, maybe below 0
$i = $w_max - array_sum($w);
// Add back spaces left to each width
$i = $i / count($w);
foreach ($w as $k => $v) {
$w[$k] = $v + $i;
}
// Table title
$pdf->SetFont('fzfangsong', '', 14);
$pdf->Write(0, '设备一览表', 0, 0, 'C', 1);
// set font
$pdf->SetFont('fzfangsong', '', 10);
// Table header
$pdf->SetFillColor(208, 220, 255);
$pdf->SetTextColor(0);
foreach ($PDF_HEADER as $i => $head) {
$pdf->Cell($w[$i], 7, $head, 1, 0, 'C', 1);
}
$pdf->Ln();
// Rows
$pdf->SetFillColor(224, 235, 255);
$pdf->SetTextColor(0);
$fill = 0;
foreach($PDF_ARRAY as $row) {
foreach ($row as $i => $cell) {
$pdf->Cell($w[$i], 6, $cell, 'LR', 0, 'L', $fill);
}
$pdf->Ln();
$fill=!$fill;
}
$pdf->Cell(array_sum($w), 0, '', 'T');
// ---------------------------------------------------------
//Close and output PDF document
$pdf->Output('glpi.pdf', 'I');
return;
break; |
// --------
require_once('tcpdf/tcpdf.php');
global $PDF_HEADER,$PDF_ARRAY;
// create new PDF document
$pdf = new TCPDF('L', PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// set document information
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Fwolf');
$pdf->SetTitle($title);
$pdf->SetSubject('GLPI Export');
$pdf->SetKeywords('TCPDF, PDF, GLPI, export, fwolf, work, asset');
// set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// remove default header/footer
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(true);
// set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
//set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP - 10, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
//set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
//set some language-dependent strings
$pdf->setLanguageArray($l);
// ---------------------------------------------------------
// add a page
$pdf->AddPage();
// Column width, by conteng max length
$w_max = $pdf->getPageWidth() - PDF_MARGIN_LEFT - PDF_MARGIN_RIGHT;
$w = array();
foreach($PDF_ARRAY as $row) {
foreach ($row as $i => $cell) {
// Chinese utf8 2 words length=6, should be 4 here
$cell = trim($cell);
$l = mb_strwidth($cell, 'utf-8');
// FZFangSong is not fixed font, so add some to en chars
// eg: 哈哈abc
// strlen = 6 + 3 = 9
// mbstrlen = 5
// mbstrwidth = 7
$l += ($l + mb_strlen($cell, 'utf-8') - strlen($cell))
* 0.3;
if (!isset($w[$i])) {
$w[$i] = $l;
}
elseif ($w[$i] < $l) {
$w[$i] = $l;
}
}
}
// Compare with max width, maybe below 0
$i = $w_max - array_sum($w);
// Add back spaces left to each width
$i = $i / count($w);
foreach ($w as $k => $v) {
$w[$k] = $v + $i;
}
// Table title
$pdf->SetFont('fzfangsong', '', 14);
$pdf->Write(0, '设备一览表', 0, 0, 'C', 1);
// set font
$pdf->SetFont('fzfangsong', '', 10);
// Table header
$pdf->SetFillColor(208, 220, 255);
$pdf->SetTextColor(0);
foreach ($PDF_HEADER as $i => $head) {
$pdf->Cell($w[$i], 7, $head, 1, 0, 'C', 1);
}
$pdf->Ln();
// Rows
$pdf->SetFillColor(224, 235, 255);
$pdf->SetTextColor(0);
$fill = 0;
foreach($PDF_ARRAY as $row) {
foreach ($row as $i => $cell) {
$pdf->Cell($w[$i], 6, $cell, 'LR', 0, 'L', $fill);
}
$pdf->Ln();
$fill=!$fill;
}
$pdf->Cell(array_sum($w), 0, '', 'T');
// ---------------------------------------------------------
//Close and output PDF document
$pdf->Output('glpi.pdf', 'I');
return;
break;
总体上这个例子是照着官网Colored Tables例子来的。
最后,把这个文件中所有的utf8_decode处理都去掉,完成。
参考
Update @ 2009-11-06
Print to pdf 的时候,Historical 中的 Comments 如何显示?默认的不好,可以考虑换成 diff 信息,如果又不愿用 pear 里的 xdiff,可以考虑它的前身──用 php 直接实现的 PHP inline diff。