用PHP创建PDF中文文档

  • A+
所属分类:PHP编程

我使用的是FPDF(www.fpdf.org),下载了fpdf类库后,还要使用下面的中文类库才能支持中文,但只能使用一种中文字体(华文仿宋)。为此我烦恼了很长时间,现在终于搞定了,将TrueType字体转化为pt1字体使用:

下面是在FPDF上找的一个中文类库:

  1. <?php
  2. require('fpdf.php');
  3. $Big5_widths=array(' '=>250,'!'=>250,'"'=>408,'#'=>668,'$'=>490,'%'=>875,'&'=>698,'''=>250,
  4. '('=>240,')'=>240,'*'=>417,'+'=>667,','=>250,'-'=>313,'.'=>250,'/'=>520,'0'=>500,'1'=>500,
  5. '2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>250,';'=>250,
  6. '<'=>667,'='=>667,'>'=>667,'?'=>396,'@'=>921,'A'=>677,'B'=>615,'C'=>719,'D'=>760,'E'=>625,
  7. 'F'=>552,'G'=>771,'H'=>802,'I'=>354,'J'=>354,'K'=>781,'L'=>604,'M'=>927,'N'=>750,'O'=>823,
  8. 'P'=>563,'Q'=>823,'R'=>729,'S'=>542,'T'=>698,'U'=>771,'V'=>729,'W'=>948,'X'=>771,'Y'=>677,
  9. 'Z'=>635,'['=>344,''=>520,']'=>344,'^'=>469,'_'=>500,'`'=>250,'a'=>469,'b'=>521,'c'=>427,
  10. 'd'=>521,'e'=>438,'f'=>271,'g'=>469,'h'=>531,'i'=>250,'j'=>250,'k'=>458,'l'=>240,'m'=>802,
  11. 'n'=>531,'o'=>500,'p'=>521,'q'=>521,'r'=>365,'s'=>333,'t'=>292,'u'=>521,'v'=>458,'w'=>677,
  12. 'x'=>479,'y'=>458,'z'=>427,'{'=>480,'|'=>496,'}'=>480,'~'=>667);
  13. $GB_widths=array(' '=>207,'!'=>270,'"'=>342,'#'=>467,'$'=>462,'%'=>797,'&'=>710,'''=>239,
  14. '('=>374,')'=>374,'*'=>423,'+'=>605,','=>238,'-'=>375,'.'=>238,'/'=>334,'0'=>462,'1'=>462,
  15. '2'=>462,'3'=>462,'4'=>462,'5'=>462,'6'=>462,'7'=>462,'8'=>462,'9'=>462,':'=>238,';'=>238,
  16. '<'=>605,'='=>605,'>'=>605,'?'=>344,'@'=>748,'A'=>684,'B'=>560,'C'=>695,'D'=>739,'E'=>563,
  17. 'F'=>511,'G'=>729,'H'=>793,'I'=>318,'J'=>312,'K'=>666,'L'=>526,'M'=>896,'N'=>758,'O'=>772,
  18. 'P'=>544,'Q'=>772,'R'=>628,'S'=>465,'T'=>607,'U'=>753,'V'=>711,'W'=>972,'X'=>647,'Y'=>620,
  19. 'Z'=>607,'['=>374,''=>333,']'=>374,'^'=>606,'_'=>500,'`'=>239,'a'=>417,'b'=>503,'c'=>427,
  20. 'd'=>529,'e'=>415,'f'=>264,'g'=>444,'h'=>518,'i'=>241,'j'=>230,'k'=>495,'l'=>228,'m'=>793,
  21. 'n'=>527,'o'=>524,'p'=>524,'q'=>504,'r'=>338,'s'=>336,'t'=>277,'u'=>517,'v'=>450,'w'=>652,
  22. 'x'=>466,'y'=>452,'z'=>407,'{'=>370,'|'=>258,'}'=>370,'~'=>605);
  23. class PDF_Chinese extends FPDF
  24. {
  25. function AddCIDFont($family,$style,$name,$cw,$CMap,$registry)
  26. {
  27. $i=count($this->fonts)+1;
  28. $fontkey=strtolower($family).strtoupper($style);
  29. $this->fonts[$fontkey]=array('i'=>$i,'type'=>'Type0','name'=>$name,'up'=>-120,'ut'=>40,'cw'=>$cw,'CMap'=>$CMap,'registry'=>$registry);
  30. }
  31. function AddBig5Font($family='Big5')
  32. {
  33. $cw=$GLOBALS['Big5_widths'];
  34. $name='MSungStd-Light-Acro';
  35. $CMap='ETenms-B5-H';
  36. $registry=array('ordering'=>'CNS1','supplement'=>0);
  37. $this->AddCIDFont($family,'',$name,$cw,$CMap,$registry);
  38. $this->AddCIDFont($family,'B',$name.',Bold',$cw,$CMap,$registry);
  39. $this->AddCIDFont($family,'I',$name.',Italic',$cw,$CMap,$registry);
  40. $this->AddCIDFont($family,'BI',$name.',BoldItalic',$cw,$CMap,$registry);
  41. }
  42. function AddGBFont($family='GB')
  43. {
  44. $cw=$GLOBALS['GB_widths'];
  45. $name='STSongStd-Light-Acro';
  46. $CMap='GBKp-EUC-H';
  47. $registry=array('ordering'=>'GB1','supplement'=>2);
  48. $this->AddCIDFont($family,'',$name,$cw,$CMap,$registry);
  49. $this->AddCIDFont($family,'B',$name.',Bold',$cw,$CMap,$registry);
  50. $this->AddCIDFont($family,'I',$name.',Italic',$cw,$CMap,$registry);
  51. $this->AddCIDFont($family,'BI',$name.',BoldItalic',$cw,$CMap,$registry);
  52. }
  53. function GetStringWidth($s)
  54. {
  55. if($this->CurrentFont['type']=='Type0')
  56. return $this->GetMBStringWidth($s);
  57. else
  58. return parent::GetStringWidth($s);
  59. }
  60. function GetMBStringWidth($s)
  61. {
  62. //Multi-byte version of GetStringWidth()
  63. $l=0;
  64. $cw=&$this->CurrentFont['cw'];
  65. $nb=strlen($s);
  66. $i=0;
  67. while($i<$nb)
  68. {
  69. $c=$s[$i];
  70. if(ord($c)<128)
  71. {
  72. $l+=$cw[$c];
  73. $i++;
  74. }
  75. else
  76. {
  77. $l+=1000;
  78. $i+=2;
  79. }
  80. }
  81. return $l*$this->FontSize/1000;
  82. }
  83. function MultiCell($w,$h,$txt,$border=0,$align='L',$fill=0)
  84. {
  85. if($this->CurrentFont['type']=='Type0')
  86. $this->MBMultiCell($w,$h,$txt,$border,$align,$fill);
  87. else
  88. parent::MultiCell($w,$h,$txt,$border,$align,$fill);
  89. }
  90. function MBMultiCell($w,$h,$txt,$border=0,$align='L',$fill=0)
  91. {
  92. //Multi-byte version of MultiCell()
  93. $cw=&$this->CurrentFont['cw'];
  94. if($w==0)
  95. $w=$this->w-$this->rMargin-$this->x;
  96. $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
  97. $s=str_replace("\r",'',$txt);
  98. $nb=strlen($s);
  99. if($nb>0 and $s[$nb-1]=="\n"
  100. $nb--;
  101. $b=0;
  102. if($border)
  103. {
  104. if($border==1)
  105. {
  106. $border='LTRB';
  107. $b='LRT';
  108. $b2='LR';
  109. }
  110. else
  111. {
  112. $b2='';
  113. if(is_int(strpos($border,'L')))
  114. $b2.='L';
  115. if(is_int(strpos($border,'R')))
  116. $b2.='R';
  117. $b=is_int(strpos($border,'T')) ? $b2.'T' : $b2;
  118. }
  119. }
  120. $sep=-1;
  121. $i=0;
  122. $j=0;
  123. $l=0;
  124. $ns=0;
  125. $nl=1;
  126. while($i<$nb)
  127. {
  128. //Get next character
  129. $c=$s[$i];
  130. //Check if ASCII or MB
  131. $ascii=(ord($c)<128);
  132. if($c=="\n"
  133. {
  134. //Explicit line break
  135. if($this->ws>0)
  136. {
  137. $this->ws=0;
  138. $this->_out('0 Tw');
  139. }
  140. $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
  141. $i++;
  142. $sep=-1;
  143. $j=$i;
  144. $l=0;
  145. $ns=0;
  146. $nl++;
  147. if($border and $nl==2)
  148. $b=$b2;
  149. continue;
  150. }
  151. if(!$ascii)
  152. {
  153. $sep=$i;
  154. $ls=$l;
  155. }
  156. elseif($c==' ')
  157. {
  158. $sep=$i;
  159. $ls=$l;
  160. $ns++;
  161. }
  162. $l+=$ascii ? $cw[$c] : 1000;
  163. if($l>$wmax)
  164. {
  165. //Automatic line break
  166. if($sep==-1 or $i==$j)
  167. {
  168. if($i==$j)
  169. $i+=$ascii ? 1 : 2;
  170. if($this->ws>0)
  171. {
  172. $this->ws=0;
  173. $this->_out('0 Tw');
  174. }
  175. $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
  176. }
  177. else
  178. {
  179. if($align=='J')
  180. {
  181. if($s[$sep]==' ')
  182. $ns--;
  183. if($s[$i-1]==' ')
  184. {
  185. $ns--;
  186. $ls-=$cw[' '];
  187. }
  188. $this->ws=($ns>0) ? ($wmax-$ls)/1000*$this->FontSize/$ns : 0;
  189. $this->_out(sprintf('%.3f Tw',$this->ws*$this->k));
  190. }
  191. $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
  192. $i=($s[$sep]==' ') ? $sep+1 : $sep;
  193. }
  194. $sep=-1;
  195. $j=$i;
  196. $l=0;
  197. $ns=0;
  198. $nl++;
  199. if($border and $nl==2)
  200. $b=$b2;
  201. }
  202. else
  203. $i+=$ascii ? 1 : 2;
  204. }
  205. //Last chunk
  206. if($this->ws>0)
  207. {
  208. $this->ws=0;
  209. $this->_out('0 Tw');
  210. }
  211. if($border and is_int(strpos($border,'B')))
  212. $b.='B';
  213. $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
  214. $this->x=$this->lMargin;
  215. }
  216. function Write($h,$txt,$link='')
  217. {
  218. if($this->CurrentFont['type']=='Type0')
  219. $this->MBWrite($h,$txt,$link);
  220. else
  221. parent::Write($h,$txt,$link);
  222. }
  223. function MBWrite($h,$txt,$link)
  224. {
  225. //Multi-byte version of Write()
  226. $cw=&$this->CurrentFont['cw'];
  227. $w=$this->w-$this->rMargin-$this->x;
  228. $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
  229. $s=str_replace("\r",'',$txt);
  230. $nb=strlen($s);
  231. $sep=-1;
  232. $i=0;
  233. $j=0;
  234. $l=0;
  235. $nl=1;
  236. while($i<$nb)
  237. {
  238. //Get next character
  239. $c=$s[$i];
  240. //Check if ASCII or MB
  241. $ascii=(ord($c)<128);
  242. if($c=="\n"
  243. {
  244. //Explicit line break
  245. $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
  246. $i++;
  247. $sep=-1;
  248. $j=$i;
  249. $l=0;
  250. if($nl==1)
  251. {
  252. $this->x=$this->lMargin;
  253. $w=$this->w-$this->rMargin-$this->x;
  254. $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
  255. }
  256. $nl++;
  257. continue;
  258. }
  259. if(!$ascii or $c==' ')
  260. $sep=$i;
  261. $l+=$ascii ? $cw[$c] : 1000;
  262. if($l>$wmax)
  263. {
  264. //Automatic line break
  265. if($sep==-1 or $i==$j)
  266. {
  267. if($this->x>$this->lMargin)
  268. {
  269. //Move to next line
  270. $this->x=$this->lMargin;
  271. $this->y+=$h;
  272. $w=$this->w-$this->rMargin-$this->x;
  273. $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
  274. $i++;
  275. $nl++;
  276. continue;
  277. }
  278. if($i==$j)
  279. $i+=$ascii ? 1 : 2;
  280. $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
  281. }
  282. else
  283. {
  284. $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link);
  285. $i=($s[$sep]==' ') ? $sep+1 : $sep;
  286. }
  287. $sep=-1;
  288. $j=$i;
  289. $l=0;
  290. if($nl==1)
  291. {
  292. $this->x=$this->lMargin;
  293. $w=$this->w-$this->rMargin-$this->x;
  294. $wmax=($w-2*$this->cMargin)*1000/$this->FontSize;
  295. }
  296. $nl++;
  297. }
  298. else
  299. $i+=$ascii ? 1 : 2;
  300. }
  301. //Last chunk
  302. if($i!=$j)
  303. $this->Cell($l/1000*$this->FontSize,$h,substr($s,$j,$i-$j),0,0,'',0,$link);
  304. }
  305. function _putfonts()
  306. {
  307. $nf=$this->n;
  308. foreach($this->diffs as $diff)
  309. {
  310. //Encodings
  311. $this->_newobj();
  312. $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
  313. $this->_out('endobj');
  314. }
  315. $mqr=get_magic_quotes_runtime();
  316. set_magic_quotes_runtime(0);
  317. foreach($this->FontFiles as $file=>$info)
  318. {
  319. //Font file embedding
  320. $this->_newobj();
  321. $this->FontFiles[$file]['n']=$this->n;
  322. if(defined('FPDF_FONTPATH'))
  323. $file=FPDF_FONTPATH.$file;
  324. $size=filesize($file);
  325. if(!$size)
  326. $this->Error('Font file not found');
  327. $this->_out('<</Length '.$size);
  328. if(substr($file,-2)=='.z')
  329. $this->_out('/Filter /FlateDecode');
  330. $this->_out('/Length1 '.$info['length1']);
  331. if(isset($info['length2']))
  332. $this->_out('/Length2 '.$info['length2'].' /Length3 0');
  333. $this->_out('>>');
  334. $f=fopen($file,'rb');
  335. $this->_putstream(fread($f,$size));
  336. fclose($f);
  337. $this->_out('endobj');
  338. }
  339. set_magic_quotes_runtime($mqr);
  340. foreach($this->fonts as $k=>$font)
  341. {
  342. //Font objects
  343. $this->_newobj();
  344. $this->fonts[$k]['n']=$this->n;
  345. $this->_out('<</Type /Font');
  346. if($font['type']=='Type0')
  347. $this->_putType0($font);
  348. else
  349. {
  350. $name=$font['name'];
  351. $this->_out('/BaseFont /'.$name);
  352. if($font['type']=='core')
  353. {
  354. //Standard font
  355. $this->_out('/Subtype /Type1');
  356. if($name!='Symbol' and $name!='ZapfDingbats')
  357. $this->_out('/Encoding /WinAnsiEncoding');
  358. }
  359. else
  360. {
  361. //Additional font
  362. $this->_out('/Subtype /'.$font['type']);
  363. $this->_out('/FirstChar 32');
  364. $this->_out('/LastChar 255');
  365. $this->_out('/Widths '.($this->n+1).' 0 R');
  366. $this->_out('/FontDescriptor '.($this->n+2).' 0 R');
  367. if($font['enc'])
  368. {
  369. if(isset($font['diff']))
  370. $this->_out('/Encoding '.($nf+$font['diff']).' 0 R');
  371. else
  372. $this->_out('/Encoding /WinAnsiEncoding');
  373. }
  374. }
  375. $this->_out('>>');
  376. $this->_out('endobj');
  377. if($font['type']!='core')
  378. {
  379. //Widths
  380. $this->_newobj();
  381. $cw=&$font['cw'];
  382. $s='[';
  383. for($i=32;$i<=255;$i++)
  384. $s.=$cw[chr($i)].' ';
  385. $this->_out($s.']');
  386. $this->_out('endobj');
  387. //Descriptor
  388. $this->_newobj();
  389. $s='<</Type /FontDescriptor /FontName /'.$name;
  390. foreach($font['desc'] as $k=>$v)
  391. $s.=' /'.$k.' '.$v;
  392. $file=$font['file'];
  393. if($file)
  394. $s.=' /FontFile'.($font['type']=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
  395. $this->_out($s.'>>');
  396. $this->_out('endobj');
  397. }
  398. }
  399. }
  400. }
  401. function _putType0($font)
  402. {
  403. //Type0
  404. $this->_out('/Subtype /Type0');
  405. $this->_out('/BaseFont /'.$font['name'].'-'.$font['CMap']);
  406. $this->_out('/Encoding /'.$font['CMap']);
  407. $this->_out('/DescendantFonts ['.($this->n+1).' 0 R]');
  408. $this->_out('>>');
  409. $this->_out('endobj');
  410. //CIDFont
  411. $this->_newobj();
  412. $this->_out('<</Type /Font');
  413. $this->_out('/Subtype /CIDFontType0');
  414. $this->_out('/BaseFont /'.$font['name']);
  415. $this->_out('/CIDSystemInfo <</Registry (Adobe) /Ordering ('.$font['registry']['ordering'].') /Supplement '.$font['registry']['supplement'].'>>');
  416. $this->_out('/FontDescriptor '.($this->n+1).' 0 R');
  417. $W='/W [1 [';
  418. foreach($font['cw'] as $w)
  419. $W.=$w.' ';
  420. $this->_out($W.']]');
  421. $this->_out('>>');
  422. $this->_out('endobj');
  423. //Font descriptor
  424. $this->_newobj();
  425. $this->_out('<</Type /FontDescriptor');
  426. $this->_out('/FontName /'.$font['name']);
  427. $this->_out('/Flags 6');
  428. $this->_out('/FontBBox [0 0 1000 1000]');
  429. $this->_out('/ItalicAngle 0');
  430. $this->_out('/Ascent 1000');
  431. $this->_out('/Descent 0');
  432. $this->_out('/CapHeight 1000');
  433. $this->_out('/StemV 10');
  434. $this->_out('>>');
  435. $this->_out('endobj');
  436. }
  437. }
  438. ?>
  439. 将以上代码存为chinese.php即可引用。但用它只能得到一种字体。为了支持所有中文字体,可用ttf2pt1程序将TrueType字体转化pt1字体,一个一个地转(具体方法在FPDF的教程中有详细说明)。为了支持其他中文字体,养分要修改上面的chinese.php,如下:
  440. 1: Replace the following line in the AddGBFont() method:
  441. function AddGBFont($family='GB',$name='STSongStd-Light-Acro')
  442. {
  443. $cw=$GLOBALS['GB_widths'];
  444. // $name='STSongStd-Light-Acro'; 
  445. $CMap='GBKp-EUC-H';
  446. ........
  447. 2: This is a Sample.
  448. <?php
  449. require('chinese.php');
  450. $pdf=new PDF_Chinese();
  451. $pdf->AddGBFont('simsun','宋体');
  452. $pdf->AddGBFont('simhei','黑体');
  453. $pdf->AddGBFont('simkai','楷体_GB2312');
  454. $pdf->AddGBFont('sinfang','仿宋_GB2312'');
  455. $pdf->Open();
  456. $pdf->AddPage();
  457. $pdf->SetFont('simsun','',20);
  458. $pdf->Write(10,'简体中文汉字‘);
  459. $pdf->SetFont('simhei','',20);
  460. $pdf->Write(10,'简体中文汉字');
  461. $pdf->SetFont('simkai','',20);
  462. $pdf->Write(10,'简体中文汉字‘);
  463. $pdf->SetFont('sinfang','',20);
  464. $pdf->Write(10,'简体中文汉字');
  465. $pdf->Output();
  466. ?>