PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/fpdf/ufpdf.php

https://github.com/pabloeuy/motte
PHP | 474 lines | 376 code | 33 blank | 65 comment | 99 complexity | 1afadcfd604b20ff64d4162522110733 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
  1. <?php
  2. /*******************************************************************************
  3. * Software: UFPDF, Unicode Free PDF generator *
  4. * Version: 0.1 *
  5. * based on FPDF 1.52 by Olivier PLATHEY *
  6. * Date: 2004-09-01 *
  7. * Author: Steven Wittens <steven@acko.net> *
  8. * License: GPL *
  9. * *
  10. * UFPDF is a modification of FPDF to support Unicode through UTF-8. *
  11. * *
  12. *******************************************************************************/
  13. if(!class_exists('UFPDF'))
  14. {
  15. define('UFPDF_VERSION','0.1');
  16. include_once 'fpdf.php';
  17. class UFPDF extends FPDF
  18. {
  19. /*******************************************************************************
  20. * *
  21. * Public methods *
  22. * *
  23. *******************************************************************************/
  24. function UFPDF($orientation='P',$unit='mm',$format='A4')
  25. {
  26. FPDF::FPDF($orientation, $unit, $format);
  27. }
  28. function GetStringWidth($s)
  29. {
  30. //Get width of a string in the current font
  31. $s = (string)$s;
  32. $codepoints=$this->utf8_to_codepoints($s);
  33. $cw=&$this->CurrentFont['cw'];
  34. $w=0;
  35. foreach($codepoints as $cp)
  36. $w+=$cw[$cp];
  37. return $w*$this->FontSize/1000;
  38. }
  39. function AddFont($family,$style='',$file='')
  40. {
  41. //Add a TrueType or Type1 font
  42. $family=strtolower($family);
  43. if($family=='arial')
  44. $family='helvetica';
  45. $style=strtoupper($style);
  46. if($style=='IB')
  47. $style='BI';
  48. if(isset($this->fonts[$family.$style]))
  49. $this->Error('Font already added: '.$family.' '.$style);
  50. if($file=='')
  51. $file=str_replace(' ','',$family).strtolower($style).'.php';
  52. if(defined('FPDF_FONTPATH'))
  53. $file=FPDF_FONTPATH.$file;
  54. include($file);
  55. if(!isset($name))
  56. $this->Error('Could not include font definition file');
  57. $i=count($this->fonts)+1;
  58. $this->fonts[$family.$style]=array('i'=>$i,'type'=>$type,'name'=>$name,'desc'=>$desc,'up'=>$up,'ut'=>$ut,'cw'=>$cw,'file'=>$file,'ctg'=>$ctg);
  59. if($file)
  60. {
  61. if($type=='TrueTypeUnicode')
  62. $this->FontFiles[$file]=array('length1'=>$originalsize);
  63. else
  64. $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2);
  65. }
  66. }
  67. function Text($x,$y,$txt)
  68. {
  69. //Output a string
  70. $s=sprintf('BT %.2f %.2f Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$this->_escapetext($txt));
  71. if($this->underline and $txt!='')
  72. $s.=' '.$this->_dounderline($x,$y,$this->GetStringWidth($txt),$txt);
  73. if($this->ColorFlag)
  74. $s='q '.$this->TextColor.' '.$s.' Q';
  75. $this->_out($s);
  76. }
  77. function AcceptPageBreak()
  78. {
  79. //Accept automatic page break or not
  80. return $this->AutoPageBreak;
  81. }
  82. function Cell($w,$h=0,$txt='',$border=0,$ln=0,$align='',$fill=0,$link='')
  83. {
  84. //Output a cell
  85. $k=$this->k;
  86. if($this->y+$h>$this->PageBreakTrigger and !$this->InFooter and $this->AcceptPageBreak())
  87. {
  88. //Automatic page break
  89. $x=$this->x;
  90. $ws=$this->ws;
  91. if($ws>0)
  92. {
  93. $this->ws=0;
  94. $this->_out('0 Tw');
  95. }
  96. $this->AddPage($this->CurOrientation);
  97. $this->x=$x;
  98. if($ws>0)
  99. {
  100. $this->ws=$ws;
  101. $this->_out(sprintf('%.3f Tw',$ws*$k));
  102. }
  103. }
  104. if($w==0)
  105. $w=$this->w-$this->rMargin-$this->x;
  106. $s='';
  107. if($fill==1 or $border==1)
  108. {
  109. if($fill==1)
  110. $op=($border==1) ? 'B' : 'f';
  111. else
  112. $op='S';
  113. $s=sprintf('%.2f %.2f %.2f %.2f re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
  114. }
  115. if(is_string($border))
  116. {
  117. $x=$this->x;
  118. $y=$this->y;
  119. if(is_int(strpos($border,'L')))
  120. $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
  121. if(is_int(strpos($border,'T')))
  122. $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
  123. if(is_int(strpos($border,'R')))
  124. $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
  125. if(is_int(strpos($border,'B')))
  126. $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
  127. }
  128. if($txt!='')
  129. {
  130. $width = $this->GetStringWidth($txt);
  131. if($align=='R')
  132. $dx=$w-$this->cMargin-$width;
  133. elseif($align=='C')
  134. $dx=($w-$width)/2;
  135. else
  136. $dx=$this->cMargin;
  137. if($this->ColorFlag)
  138. $s.='q '.$this->TextColor.' ';
  139. $txtstring=$this->_escapetext($txt);
  140. $s.=sprintf('BT %.2f %.2f Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txtstring);
  141. if($this->underline)
  142. $s.=' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$width,$txt);
  143. if($this->ColorFlag)
  144. $s.=' Q';
  145. if($link)
  146. $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$width,$this->FontSize,$link);
  147. }
  148. if($s)
  149. $this->_out($s);
  150. $this->lasth=$h;
  151. if($ln>0)
  152. {
  153. //Go to next line
  154. $this->y+=$h;
  155. if($ln==1)
  156. $this->x=$this->lMargin;
  157. }
  158. else
  159. $this->x+=$w;
  160. }
  161. /*******************************************************************************
  162. * *
  163. * Protected methods *
  164. * *
  165. *******************************************************************************/
  166. function _puttruetypeunicode($font) {
  167. //Type0 Font
  168. $this->_newobj();
  169. $this->_out('<</Type /Font');
  170. $this->_out('/Subtype /Type0');
  171. $this->_out('/BaseFont /'. $font['name'] .'-UCS');
  172. $this->_out('/Encoding /Identity-H');
  173. $this->_out('/DescendantFonts ['. ($this->n + 1) .' 0 R]');
  174. $this->_out('>>');
  175. $this->_out('endobj');
  176. //CIDFont
  177. $this->_newobj();
  178. $this->_out('<</Type /Font');
  179. $this->_out('/Subtype /CIDFontType2');
  180. $this->_out('/BaseFont /'. $font['name']);
  181. $this->_out('/CIDSystemInfo <</Registry (Adobe) /Ordering (UCS) /Supplement 0>>');
  182. $this->_out('/FontDescriptor '. ($this->n + 1) .' 0 R');
  183. $c = 0;
  184. foreach ($font['cw'] as $i => $w) {
  185. $widths .= $i .' ['. $w.'] ';
  186. }
  187. $this->_out('/W ['. $widths .']');
  188. $this->_out('/CIDToGIDMap '. ($this->n + 2) .' 0 R');
  189. $this->_out('>>');
  190. $this->_out('endobj');
  191. //Font descriptor
  192. $this->_newobj();
  193. $this->_out('<</Type /FontDescriptor');
  194. $this->_out('/FontName /'.$font['name']);
  195. foreach ($font['desc'] as $k => $v) {
  196. $s .= ' /'. $k .' '. $v;
  197. }
  198. if ($font['file']) {
  199. $s .= ' /FontFile2 '. $this->FontFiles[$font['file']]['n'] .' 0 R';
  200. }
  201. $this->_out($s);
  202. $this->_out('>>');
  203. $this->_out('endobj');
  204. //Embed CIDToGIDMap
  205. $this->_newobj();
  206. if(defined('FPDF_FONTPATH'))
  207. $file=FPDF_FONTPATH.$font['ctg'];
  208. else
  209. $file=$font['ctg'];
  210. $size=filesize($file);
  211. if(!$size)
  212. $this->Error('Font file not found');
  213. $this->_out('<</Length '.$size);
  214. if(substr($file,-2) == '.z')
  215. $this->_out('/Filter /FlateDecode');
  216. $this->_out('>>');
  217. $f = fopen($file,'rb');
  218. $this->_putstream(fread($f,$size));
  219. fclose($f);
  220. $this->_out('endobj');
  221. }
  222. function _dounderline($x,$y,$width,$txt)
  223. {
  224. //Underline text
  225. $up=$this->CurrentFont['up'];
  226. $ut=$this->CurrentFont['ut'];
  227. $w=$width+$this->ws*substr_count($txt,' ');
  228. return sprintf('%.2f %.2f %.2f %.2f re f',$x*$this->k,($this->h-($y-$up/1000*$this->FontSize))*$this->k,$w*$this->k,-$ut/1000*$this->FontSizePt);
  229. }
  230. function _textstring($s)
  231. {
  232. //Convert to UTF-16BE
  233. $s = $this->utf8_to_utf16be($s);
  234. //Escape necessary characters
  235. return '('. strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\')) .')';
  236. }
  237. function _escapetext($s)
  238. {
  239. //Convert to UTF-16BE
  240. $s = $this->utf8_to_utf16be($s, false);
  241. //Escape necessary characters
  242. return '('. strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\')) .')';
  243. }
  244. function _putinfo()
  245. {
  246. $this->_out('/Producer '.$this->_textstring('UFPDF '. UFPDF_VERSION));
  247. if(!empty($this->title))
  248. $this->_out('/Title '.$this->_textstring($this->title));
  249. if(!empty($this->subject))
  250. $this->_out('/Subject '.$this->_textstring($this->subject));
  251. if(!empty($this->author))
  252. $this->_out('/Author '.$this->_textstring($this->author));
  253. if(!empty($this->keywords))
  254. $this->_out('/Keywords '.$this->_textstring($this->keywords));
  255. if(!empty($this->creator))
  256. $this->_out('/Creator '.$this->_textstring($this->creator));
  257. $this->_out('/CreationDate '.$this->_textstring('D:'.date('YmdHis')));
  258. }
  259. // UTF-8 to UTF-16BE conversion.
  260. // Correctly handles all illegal UTF-8 sequences.
  261. function utf8_to_utf16be(&$txt, $bom = true) {
  262. $l = strlen($txt);
  263. $out = $bom ? "\xFE\xFF" : '';
  264. for ($i = 0; $i < $l; ++$i) {
  265. $c = ord($txt{$i});
  266. // ASCII
  267. if ($c < 0x80) {
  268. $out .= "\x00". $txt{$i};
  269. }
  270. // Lost continuation byte
  271. else if ($c < 0xC0) {
  272. $out .= "\xFF\xFD";
  273. continue;
  274. }
  275. // Multibyte sequence leading byte
  276. else {
  277. if ($c < 0xE0) {
  278. $s = 2;
  279. }
  280. else if ($c < 0xF0) {
  281. $s = 3;
  282. }
  283. else if ($c < 0xF8) {
  284. $s = 4;
  285. }
  286. // 5/6 byte sequences not possible for Unicode.
  287. else {
  288. $out .= "\xFF\xFD";
  289. while (ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; }
  290. continue;
  291. }
  292. $q = array($c);
  293. // Fetch rest of sequence
  294. while (ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; $q[] = ord($txt{$i}); }
  295. // Check length
  296. if (count($q) != $s) {
  297. $out .= "\xFF\xFD";
  298. continue;
  299. }
  300. switch ($s) {
  301. case 2:
  302. $cp = (($q[0] ^ 0xC0) << 6) | ($q[1] ^ 0x80);
  303. // Overlong sequence
  304. if ($cp < 0x80) {
  305. $out .= "\xFF\xFD";
  306. }
  307. else {
  308. $out .= chr($cp >> 8);
  309. $out .= chr($cp & 0xFF);
  310. }
  311. continue;
  312. case 3:
  313. $cp = (($q[0] ^ 0xE0) << 12) | (($q[1] ^ 0x80) << 6) | ($q[2] ^ 0x80);
  314. // Overlong sequence
  315. if ($cp < 0x800) {
  316. $out .= "\xFF\xFD";
  317. }
  318. // Check for UTF-8 encoded surrogates (caused by a bad UTF-8 encoder)
  319. else if ($c > 0xD800 && $c < 0xDFFF) {
  320. $out .= "\xFF\xFD";
  321. }
  322. else {
  323. $out .= chr($cp >> 8);
  324. $out .= chr($cp & 0xFF);
  325. }
  326. continue;
  327. case 4:
  328. $cp = (($q[0] ^ 0xF0) << 18) | (($q[1] ^ 0x80) << 12) | (($q[2] ^ 0x80) << 6) | ($q[3] ^ 0x80);
  329. // Overlong sequence
  330. if ($cp < 0x10000) {
  331. $out .= "\xFF\xFD";
  332. }
  333. // Outside of the Unicode range
  334. else if ($cp >= 0x10FFFF) {
  335. $out .= "\xFF\xFD";
  336. }
  337. else {
  338. // Use surrogates
  339. $cp -= 0x10000;
  340. $s1 = 0xD800 | ($cp >> 10);
  341. $s2 = 0xDC00 | ($cp & 0x3FF);
  342. $out .= chr($s1 >> 8);
  343. $out .= chr($s1 & 0xFF);
  344. $out .= chr($s2 >> 8);
  345. $out .= chr($s2 & 0xFF);
  346. }
  347. continue;
  348. }
  349. }
  350. }
  351. return $out;
  352. }
  353. // UTF-8 to codepoint array conversion.
  354. // Correctly handles all illegal UTF-8 sequences.
  355. function utf8_to_codepoints(&$txt) {
  356. $l = strlen($txt);
  357. $out = array();
  358. for ($i = 0; $i < $l; ++$i) {
  359. $c = ord($txt{$i});
  360. // ASCII
  361. if ($c < 0x80) {
  362. $out[] = ord($txt{$i});
  363. }
  364. // Lost continuation byte
  365. else if ($c < 0xC0) {
  366. $out[] = 0xFFFD;
  367. continue;
  368. }
  369. // Multibyte sequence leading byte
  370. else {
  371. if ($c < 0xE0) {
  372. $s = 2;
  373. }
  374. else if ($c < 0xF0) {
  375. $s = 3;
  376. }
  377. else if ($c < 0xF8) {
  378. $s = 4;
  379. }
  380. // 5/6 byte sequences not possible for Unicode.
  381. else {
  382. $out[] = 0xFFFD;
  383. while (ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; }
  384. continue;
  385. }
  386. $q = array($c);
  387. // Fetch rest of sequence
  388. while (ord($txt{$i + 1}) >= 0x80 && ord($txt{$i + 1}) < 0xC0) { ++$i; $q[] = ord($txt{$i}); }
  389. // Check length
  390. if (count($q) != $s) {
  391. $out[] = 0xFFFD;
  392. continue;
  393. }
  394. switch ($s) {
  395. case 2:
  396. $cp = (($q[0] ^ 0xC0) << 6) | ($q[1] ^ 0x80);
  397. // Overlong sequence
  398. if ($cp < 0x80) {
  399. $out[] = 0xFFFD;
  400. }
  401. else {
  402. $out[] = $cp;
  403. }
  404. continue;
  405. case 3:
  406. $cp = (($q[0] ^ 0xE0) << 12) | (($q[1] ^ 0x80) << 6) | ($q[2] ^ 0x80);
  407. // Overlong sequence
  408. if ($cp < 0x800) {
  409. $out[] = 0xFFFD;
  410. }
  411. // Check for UTF-8 encoded surrogates (caused by a bad UTF-8 encoder)
  412. else if ($c > 0xD800 && $c < 0xDFFF) {
  413. $out[] = 0xFFFD;
  414. }
  415. else {
  416. $out[] = $cp;
  417. }
  418. continue;
  419. case 4:
  420. $cp = (($q[0] ^ 0xF0) << 18) | (($q[1] ^ 0x80) << 12) | (($q[2] ^ 0x80) << 6) | ($q[3] ^ 0x80);
  421. // Overlong sequence
  422. if ($cp < 0x10000) {
  423. $out[] = 0xFFFD;
  424. }
  425. // Outside of the Unicode range
  426. else if ($cp >= 0x10FFFF) {
  427. $out[] = 0xFFFD;
  428. }
  429. else {
  430. $out[] = $cp;
  431. }
  432. continue;
  433. }
  434. }
  435. }
  436. return $out;
  437. }
  438. //End of class
  439. }
  440. }
  441. ?>