PageRenderTime 59ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/views/shared/items/tfpdf.php

https://github.com/mjlassila/plugin-PDFOutput
PHP | 2302 lines | 2201 code | 36 blank | 65 comment | 116 complexity | 9169622ef85a9b4ec854aea329c4d99c MD5 | raw file
  1. <?php
  2. /*******************************************************************************
  3. * tFPDF (based on FPDF 1.7) *
  4. * *
  5. * Version: 1.24 *
  6. * Date: 2011-09-24 *
  7. * Author: Ian Back <ianb@bpm1.com> *
  8. * License: LGPL *
  9. *******************************************************************************/
  10. define('tFPDF_VERSION','1.24');
  11. class tFPDF
  12. {
  13. var $unifontSubset;
  14. var $page; // current page number
  15. var $n; // current object number
  16. var $offsets; // array of object offsets
  17. var $buffer; // buffer holding in-memory PDF
  18. var $pages; // array containing pages
  19. var $state; // current document state
  20. var $compress; // compression flag
  21. var $k; // scale factor (number of points in user unit)
  22. var $DefOrientation; // default orientation
  23. var $CurOrientation; // current orientation
  24. var $StdPageSizes; // standard page sizes
  25. var $DefPageSize; // default page size
  26. var $CurPageSize; // current page size
  27. var $PageSizes; // used for pages with non default sizes or orientations
  28. var $wPt, $hPt; // dimensions of current page in points
  29. var $w, $h; // dimensions of current page in user unit
  30. var $lMargin; // left margin
  31. var $tMargin; // top margin
  32. var $rMargin; // right margin
  33. var $bMargin; // page break margin
  34. var $cMargin; // cell margin
  35. var $x, $y; // current position in user unit
  36. var $lasth; // height of last printed cell
  37. var $LineWidth; // line width in user unit
  38. var $fontpath; // path containing fonts
  39. var $CoreFonts; // array of core font names
  40. var $fonts; // array of used fonts
  41. var $FontFiles; // array of font files
  42. var $diffs; // array of encoding differences
  43. var $FontFamily; // current font family
  44. var $FontStyle; // current font style
  45. var $underline; // underlining flag
  46. var $CurrentFont; // current font info
  47. var $FontSizePt; // current font size in points
  48. var $FontSize; // current font size in user unit
  49. var $DrawColor; // commands for drawing color
  50. var $FillColor; // commands for filling color
  51. var $TextColor; // commands for text color
  52. var $ColorFlag; // indicates whether fill and text colors are different
  53. var $ws; // word spacing
  54. var $images; // array of used images
  55. var $PageLinks; // array of links in pages
  56. var $links; // array of internal links
  57. var $AutoPageBreak; // automatic page breaking
  58. var $PageBreakTrigger; // threshold used to trigger page breaks
  59. var $InHeader; // flag set when processing header
  60. var $InFooter; // flag set when processing footer
  61. var $ZoomMode; // zoom display mode
  62. var $LayoutMode; // layout display mode
  63. var $title; // title
  64. var $subject; // subject
  65. var $author; // author
  66. var $keywords; // keywords
  67. var $creator; // creator
  68. var $AliasNbPages; // alias for total number of pages
  69. var $PDFVersion; // PDF version number
  70. /*******************************************************************************
  71. * *
  72. * Public methods *
  73. * *
  74. *******************************************************************************/
  75. function tFPDF($orientation='P', $unit='mm', $size='A4')
  76. {
  77. // Some checks
  78. $this->_dochecks();
  79. // Initialization of properties
  80. $this->page = 0;
  81. $this->n = 2;
  82. $this->buffer = '';
  83. $this->pages = array();
  84. $this->PageSizes = array();
  85. $this->state = 0;
  86. $this->fonts = array();
  87. $this->FontFiles = array();
  88. $this->diffs = array();
  89. $this->images = array();
  90. $this->links = array();
  91. $this->InHeader = false;
  92. $this->InFooter = false;
  93. $this->lasth = 0;
  94. $this->FontFamily = '';
  95. $this->FontStyle = '';
  96. $this->FontSizePt = 12;
  97. $this->underline = false;
  98. $this->DrawColor = '0 G';
  99. $this->FillColor = '0 g';
  100. $this->TextColor = '0 g';
  101. $this->ColorFlag = false;
  102. $this->ws = 0;
  103. // Font path
  104. if(defined('FPDF_FONTPATH'))
  105. {
  106. $this->fontpath = FPDF_FONTPATH;
  107. if(substr($this->fontpath,-1)!='/' && substr($this->fontpath,-1)!='\\')
  108. $this->fontpath .= '/';
  109. }
  110. elseif(is_dir(dirname(__FILE__).'/font'))
  111. $this->fontpath = dirname(__FILE__).'/font/';
  112. else
  113. $this->fontpath = '';
  114. // Core fonts
  115. $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats');
  116. // Scale factor
  117. if($unit=='pt')
  118. $this->k = 1;
  119. elseif($unit=='mm')
  120. $this->k = 72/25.4;
  121. elseif($unit=='cm')
  122. $this->k = 72/2.54;
  123. elseif($unit=='in')
  124. $this->k = 72;
  125. else
  126. $this->Error('Incorrect unit: '.$unit);
  127. // Page sizes
  128. $this->StdPageSizes = array('a3'=>array(841.89,1190.55), 'a4'=>array(595.28,841.89), 'a5'=>array(420.94,595.28),
  129. 'letter'=>array(612,792), 'legal'=>array(612,1008));
  130. $size = $this->_getpagesize($size);
  131. $this->DefPageSize = $size;
  132. $this->CurPageSize = $size;
  133. // Page orientation
  134. $orientation = strtolower($orientation);
  135. if($orientation=='p' || $orientation=='portrait')
  136. {
  137. $this->DefOrientation = 'P';
  138. $this->w = $size[0];
  139. $this->h = $size[1];
  140. }
  141. elseif($orientation=='l' || $orientation=='landscape')
  142. {
  143. $this->DefOrientation = 'L';
  144. $this->w = $size[1];
  145. $this->h = $size[0];
  146. }
  147. else
  148. $this->Error('Incorrect orientation: '.$orientation);
  149. $this->CurOrientation = $this->DefOrientation;
  150. $this->wPt = $this->w*$this->k;
  151. $this->hPt = $this->h*$this->k;
  152. // Page margins (1 cm)
  153. $margin = 28.35/$this->k;
  154. $this->SetMargins($margin,$margin);
  155. // Interior cell margin (1 mm)
  156. $this->cMargin = $margin/10;
  157. // Line width (0.2 mm)
  158. $this->LineWidth = .567/$this->k;
  159. // Automatic page break
  160. $this->SetAutoPageBreak(true,2*$margin);
  161. // Default display mode
  162. $this->SetDisplayMode('default');
  163. // Enable compression
  164. $this->SetCompression(true);
  165. // Set default PDF version number
  166. $this->PDFVersion = '1.3';
  167. }
  168. function SetMargins($left, $top, $right=null)
  169. {
  170. // Set left, top and right margins
  171. $this->lMargin = $left;
  172. $this->tMargin = $top;
  173. if($right===null)
  174. $right = $left;
  175. $this->rMargin = $right;
  176. }
  177. function SetLeftMargin($margin)
  178. {
  179. // Set left margin
  180. $this->lMargin = $margin;
  181. if($this->page>0 && $this->x<$margin)
  182. $this->x = $margin;
  183. }
  184. function SetTopMargin($margin)
  185. {
  186. // Set top margin
  187. $this->tMargin = $margin;
  188. }
  189. function SetRightMargin($margin)
  190. {
  191. // Set right margin
  192. $this->rMargin = $margin;
  193. }
  194. function SetAutoPageBreak($auto, $margin=0)
  195. {
  196. // Set auto page break mode and triggering margin
  197. $this->AutoPageBreak = $auto;
  198. $this->bMargin = $margin;
  199. $this->PageBreakTrigger = $this->h-$margin;
  200. }
  201. function SetDisplayMode($zoom, $layout='default')
  202. {
  203. // Set display mode in viewer
  204. if($zoom=='fullpage' || $zoom=='fullwidth' || $zoom=='real' || $zoom=='default' || !is_string($zoom))
  205. $this->ZoomMode = $zoom;
  206. else
  207. $this->Error('Incorrect zoom display mode: '.$zoom);
  208. if($layout=='single' || $layout=='continuous' || $layout=='two' || $layout=='default')
  209. $this->LayoutMode = $layout;
  210. else
  211. $this->Error('Incorrect layout display mode: '.$layout);
  212. }
  213. function SetCompression($compress)
  214. {
  215. // Set page compression
  216. if(function_exists('gzcompress'))
  217. $this->compress = $compress;
  218. else
  219. $this->compress = false;
  220. }
  221. function SetTitle($title, $isUTF8=false)
  222. {
  223. // Title of document
  224. if($isUTF8)
  225. $title = $this->_UTF8toUTF16($title);
  226. $this->title = $title;
  227. }
  228. function SetSubject($subject, $isUTF8=false)
  229. {
  230. // Subject of document
  231. if($isUTF8)
  232. $subject = $this->_UTF8toUTF16($subject);
  233. $this->subject = $subject;
  234. }
  235. function SetAuthor($author, $isUTF8=false)
  236. {
  237. // Author of document
  238. if($isUTF8)
  239. $author = $this->_UTF8toUTF16($author);
  240. $this->author = $author;
  241. }
  242. function SetKeywords($keywords, $isUTF8=false)
  243. {
  244. // Keywords of document
  245. if($isUTF8)
  246. $keywords = $this->_UTF8toUTF16($keywords);
  247. $this->keywords = $keywords;
  248. }
  249. function SetCreator($creator, $isUTF8=false)
  250. {
  251. // Creator of document
  252. if($isUTF8)
  253. $creator = $this->_UTF8toUTF16($creator);
  254. $this->creator = $creator;
  255. }
  256. function AliasNbPages($alias='{nb}')
  257. {
  258. // Define an alias for total number of pages
  259. $this->AliasNbPages = $alias;
  260. }
  261. function Error($msg)
  262. {
  263. // Fatal error
  264. die('<b>FPDF error:</b> '.$msg);
  265. }
  266. function Open()
  267. {
  268. // Begin document
  269. $this->state = 1;
  270. }
  271. function Close()
  272. {
  273. // Terminate document
  274. if($this->state==3)
  275. return;
  276. if($this->page==0)
  277. $this->AddPage();
  278. // Page footer
  279. $this->InFooter = true;
  280. $this->Footer();
  281. $this->InFooter = false;
  282. // Close page
  283. $this->_endpage();
  284. // Close document
  285. $this->_enddoc();
  286. }
  287. function AddPage($orientation='', $size='')
  288. {
  289. // Start a new page
  290. if($this->state==0)
  291. $this->Open();
  292. $family = $this->FontFamily;
  293. $style = $this->FontStyle.($this->underline ? 'U' : '');
  294. $fontsize = $this->FontSizePt;
  295. $lw = $this->LineWidth;
  296. $dc = $this->DrawColor;
  297. $fc = $this->FillColor;
  298. $tc = $this->TextColor;
  299. $cf = $this->ColorFlag;
  300. if($this->page>0)
  301. {
  302. // Page footer
  303. $this->InFooter = true;
  304. $this->Footer();
  305. $this->InFooter = false;
  306. // Close page
  307. $this->_endpage();
  308. }
  309. // Start new page
  310. $this->_beginpage($orientation,$size);
  311. // Set line cap style to square
  312. $this->_out('2 J');
  313. // Set line width
  314. $this->LineWidth = $lw;
  315. $this->_out(sprintf('%.2F w',$lw*$this->k));
  316. // Set font
  317. if($family)
  318. $this->SetFont($family,$style,$fontsize);
  319. // Set colors
  320. $this->DrawColor = $dc;
  321. if($dc!='0 G')
  322. $this->_out($dc);
  323. $this->FillColor = $fc;
  324. if($fc!='0 g')
  325. $this->_out($fc);
  326. $this->TextColor = $tc;
  327. $this->ColorFlag = $cf;
  328. // Page header
  329. $this->InHeader = true;
  330. $this->Header();
  331. $this->InHeader = false;
  332. // Restore line width
  333. if($this->LineWidth!=$lw)
  334. {
  335. $this->LineWidth = $lw;
  336. $this->_out(sprintf('%.2F w',$lw*$this->k));
  337. }
  338. // Restore font
  339. if($family)
  340. $this->SetFont($family,$style,$fontsize);
  341. // Restore colors
  342. if($this->DrawColor!=$dc)
  343. {
  344. $this->DrawColor = $dc;
  345. $this->_out($dc);
  346. }
  347. if($this->FillColor!=$fc)
  348. {
  349. $this->FillColor = $fc;
  350. $this->_out($fc);
  351. }
  352. $this->TextColor = $tc;
  353. $this->ColorFlag = $cf;
  354. }
  355. function Header()
  356. {
  357. // To be implemented in your own inherited class
  358. }
  359. function Footer()
  360. {
  361. // To be implemented in your own inherited class
  362. }
  363. function PageNo()
  364. {
  365. // Get current page number
  366. return $this->page;
  367. }
  368. function SetDrawColor($r, $g=null, $b=null)
  369. {
  370. // Set color for all stroking operations
  371. if(($r==0 && $g==0 && $b==0) || $g===null)
  372. $this->DrawColor = sprintf('%.3F G',$r/255);
  373. else
  374. $this->DrawColor = sprintf('%.3F %.3F %.3F RG',$r/255,$g/255,$b/255);
  375. if($this->page>0)
  376. $this->_out($this->DrawColor);
  377. }
  378. function SetFillColor($r, $g=null, $b=null)
  379. {
  380. // Set color for all filling operations
  381. if(($r==0 && $g==0 && $b==0) || $g===null)
  382. $this->FillColor = sprintf('%.3F g',$r/255);
  383. else
  384. $this->FillColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
  385. $this->ColorFlag = ($this->FillColor!=$this->TextColor);
  386. if($this->page>0)
  387. $this->_out($this->FillColor);
  388. }
  389. function SetTextColor($r, $g=null, $b=null)
  390. {
  391. // Set color for text
  392. if(($r==0 && $g==0 && $b==0) || $g===null)
  393. $this->TextColor = sprintf('%.3F g',$r/255);
  394. else
  395. $this->TextColor = sprintf('%.3F %.3F %.3F rg',$r/255,$g/255,$b/255);
  396. $this->ColorFlag = ($this->FillColor!=$this->TextColor);
  397. }
  398. function GetStringWidth($s)
  399. {
  400. // Get width of a string in the current font
  401. $s = (string)$s;
  402. $cw = &$this->CurrentFont['cw'];
  403. $w=0;
  404. if ($this->unifontSubset) {
  405. $unicode = $this->UTF8StringToArray($s);
  406. foreach($unicode as $char) {
  407. if (isset($cw[$char])) { $w += (ord($cw[2*$char])<<8) + ord($cw[2*$char+1]); }
  408. else if($char>0 && $char<128 && isset($cw[chr($char)])) { $w += $cw[chr($char)]; }
  409. else if(isset($this->CurrentFont['desc']['MissingWidth'])) { $w += $this->CurrentFont['desc']['MissingWidth']; }
  410. else if(isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; }
  411. else { $w += 500; }
  412. }
  413. }
  414. else {
  415. $l = strlen($s);
  416. for($i=0;$i<$l;$i++)
  417. $w += $cw[$s[$i]];
  418. }
  419. return $w*$this->FontSize/1000;
  420. }
  421. function SetLineWidth($width)
  422. {
  423. // Set line width
  424. $this->LineWidth = $width;
  425. if($this->page>0)
  426. $this->_out(sprintf('%.2F w',$width*$this->k));
  427. }
  428. function Line($x1, $y1, $x2, $y2)
  429. {
  430. // Draw a line
  431. $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S',$x1*$this->k,($this->h-$y1)*$this->k,$x2*$this->k,($this->h-$y2)*$this->k));
  432. }
  433. function Rect($x, $y, $w, $h, $style='')
  434. {
  435. // Draw a rectangle
  436. if($style=='F')
  437. $op = 'f';
  438. elseif($style=='FD' || $style=='DF')
  439. $op = 'B';
  440. else
  441. $op = 'S';
  442. $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op));
  443. }
  444. function AddFont($family, $style='', $file='', $uni=false)
  445. {
  446. // Add a TrueType, OpenType or Type1 font
  447. $family = strtolower($family);
  448. $style = strtoupper($style);
  449. if($style=='IB')
  450. $style='BI';
  451. if($file=='') {
  452. if ($uni) {
  453. $file = str_replace(' ','',$family).strtolower($style).'.ttf';
  454. }
  455. else {
  456. $file = str_replace(' ','',$family).strtolower($style).'.php';
  457. }
  458. }
  459. $fontkey = $family.$style;
  460. if(isset($this->fonts[$fontkey]))
  461. return;
  462. if ($uni) {
  463. if (defined("_SYSTEM_TTFONTS") && file_exists(_SYSTEM_TTFONTS.$file )) { $ttffilename = _SYSTEM_TTFONTS.$file ; }
  464. else { $ttffilename = $this->_getfontpath().'unifont/'.$file ; }
  465. $unifilename = $this->_getfontpath().'unifont/'.strtolower(substr($file ,0,(strpos($file ,'.'))));
  466. $name = '';
  467. $originalsize = 0;
  468. $ttfstat = stat($ttffilename);
  469. if (file_exists($unifilename.'.mtx.php')) {
  470. include($unifilename.'.mtx.php');
  471. }
  472. if (!isset($type) || !isset($name) || $originalsize != $ttfstat['size']) {
  473. $ttffile = $ttffilename;
  474. require_once($this->_getfontpath().'unifont/ttfonts.php');
  475. $ttf = new TTFontFile();
  476. $ttf->getMetrics($ttffile);
  477. $cw = $ttf->charWidths;
  478. $name = preg_replace('/[ ()]/','',$ttf->fullName);
  479. $desc= array('Ascent'=>round($ttf->ascent),
  480. 'Descent'=>round($ttf->descent),
  481. 'CapHeight'=>round($ttf->capHeight),
  482. 'Flags'=>$ttf->flags,
  483. 'FontBBox'=>'['.round($ttf->bbox[0])." ".round($ttf->bbox[1])." ".round($ttf->bbox[2])." ".round($ttf->bbox[3]).']',
  484. 'ItalicAngle'=>$ttf->italicAngle,
  485. 'StemV'=>round($ttf->stemV),
  486. 'MissingWidth'=>round($ttf->defaultWidth));
  487. $up = round($ttf->underlinePosition);
  488. $ut = round($ttf->underlineThickness);
  489. $originalsize = $ttfstat['size']+0;
  490. $type = 'TTF';
  491. // Generate metrics .php file
  492. $s='<?php'."\n";
  493. $s.='$name=\''.$name."';\n";
  494. $s.='$type=\''.$type."';\n";
  495. $s.='$desc='.var_export($desc,true).";\n";
  496. $s.='$up='.$up.";\n";
  497. $s.='$ut='.$ut.";\n";
  498. $s.='$ttffile=\''.$ttffile."';\n";
  499. $s.='$originalsize='.$originalsize.";\n";
  500. $s.='$fontkey=\''.$fontkey."';\n";
  501. $s.="?>";
  502. if (is_writable(dirname($this->_getfontpath().'unifont/'.'x'))) {
  503. $fh = fopen($unifilename.'.mtx.php',"w");
  504. fwrite($fh,$s,strlen($s));
  505. fclose($fh);
  506. $fh = fopen($unifilename.'.cw.dat',"wb");
  507. fwrite($fh,$cw,strlen($cw));
  508. fclose($fh);
  509. @unlink($unifilename.'.cw127.php');
  510. }
  511. unset($ttf);
  512. }
  513. else {
  514. $cw = @file_get_contents($unifilename.'.cw.dat');
  515. }
  516. $i = count($this->fonts)+1;
  517. if(!empty($this->AliasNbPages))
  518. $sbarr = range(0,57);
  519. else
  520. $sbarr = range(0,32);
  521. $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'ttffile'=>$ttffile, 'fontkey'=>$fontkey, 'subset'=>$sbarr, 'unifilename'=>$unifilename);
  522. $this->FontFiles[$fontkey]=array('length1'=>$originalsize, 'type'=>"TTF", 'ttffile'=>$ttffile);
  523. $this->FontFiles[$file]=array('type'=>"TTF");
  524. unset($cw);
  525. }
  526. else {
  527. $info = $this->_loadfont($file);
  528. $info['i'] = count($this->fonts)+1;
  529. if(!empty($info['diff']))
  530. {
  531. // Search existing encodings
  532. $n = array_search($info['diff'],$this->diffs);
  533. if(!$n)
  534. {
  535. $n = count($this->diffs)+1;
  536. $this->diffs[$n] = $info['diff'];
  537. }
  538. $info['diffn'] = $n;
  539. }
  540. if(!empty($info['file']))
  541. {
  542. // Embedded font
  543. if($info['type']=='TrueType')
  544. $this->FontFiles[$info['file']] = array('length1'=>$info['originalsize']);
  545. else
  546. $this->FontFiles[$info['file']] = array('length1'=>$info['size1'], 'length2'=>$info['size2']);
  547. }
  548. $this->fonts[$fontkey] = $info;
  549. }
  550. }
  551. function SetFont($family, $style='', $size=0)
  552. {
  553. // Select a font; size given in points
  554. if($family=='')
  555. $family = $this->FontFamily;
  556. else
  557. $family = strtolower($family);
  558. $style = strtoupper($style);
  559. if(strpos($style,'U')!==false)
  560. {
  561. $this->underline = true;
  562. $style = str_replace('U','',$style);
  563. }
  564. else
  565. $this->underline = false;
  566. if($style=='IB')
  567. $style = 'BI';
  568. if($size==0)
  569. $size = $this->FontSizePt;
  570. // Test if font is already selected
  571. if($this->FontFamily==$family && $this->FontStyle==$style && $this->FontSizePt==$size)
  572. return;
  573. // Test if font is already loaded
  574. $fontkey = $family.$style;
  575. if(!isset($this->fonts[$fontkey]))
  576. {
  577. // Test if one of the core fonts
  578. if($family=='arial')
  579. $family = 'helvetica';
  580. if(in_array($family,$this->CoreFonts))
  581. {
  582. if($family=='symbol' || $family=='zapfdingbats')
  583. $style = '';
  584. $fontkey = $family.$style;
  585. if(!isset($this->fonts[$fontkey]))
  586. $this->AddFont($family,$style);
  587. }
  588. else
  589. $this->Error('Undefined font: '.$family.' '.$style);
  590. }
  591. // Select it
  592. $this->FontFamily = $family;
  593. $this->FontStyle = $style;
  594. $this->FontSizePt = $size;
  595. $this->FontSize = $size/$this->k;
  596. $this->CurrentFont = &$this->fonts[$fontkey];
  597. if ($this->fonts[$fontkey]['type']=='TTF') { $this->unifontSubset = true; }
  598. else { $this->unifontSubset = false; }
  599. if($this->page>0)
  600. $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
  601. }
  602. function SetFontSize($size)
  603. {
  604. // Set font size in points
  605. if($this->FontSizePt==$size)
  606. return;
  607. $this->FontSizePt = $size;
  608. $this->FontSize = $size/$this->k;
  609. if($this->page>0)
  610. $this->_out(sprintf('BT /F%d %.2F Tf ET',$this->CurrentFont['i'],$this->FontSizePt));
  611. }
  612. function AddLink()
  613. {
  614. // Create a new internal link
  615. $n = count($this->links)+1;
  616. $this->links[$n] = array(0, 0);
  617. return $n;
  618. }
  619. function SetLink($link, $y=0, $page=-1)
  620. {
  621. // Set destination of internal link
  622. if($y==-1)
  623. $y = $this->y;
  624. if($page==-1)
  625. $page = $this->page;
  626. $this->links[$link] = array($page, $y);
  627. }
  628. function Link($x, $y, $w, $h, $link)
  629. {
  630. // Put a link on the page
  631. $this->PageLinks[$this->page][] = array($x*$this->k, $this->hPt-$y*$this->k, $w*$this->k, $h*$this->k, $link);
  632. }
  633. function Text($x, $y, $txt)
  634. {
  635. // Output a string
  636. if ($this->unifontSubset)
  637. {
  638. $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
  639. foreach($this->UTF8StringToArray($txt) as $uni)
  640. $this->CurrentFont['subset'][$uni] = $uni;
  641. }
  642. else
  643. $txt2 = '('.$this->_escape($txt).')';
  644. $s = sprintf('BT %.2F %.2F Td %s Tj ET',$x*$this->k,($this->h-$y)*$this->k,$txt2);
  645. if($this->underline && $txt!='')
  646. $s .= ' '.$this->_dounderline($x,$y,$txt);
  647. if($this->ColorFlag)
  648. $s = 'q '.$this->TextColor.' '.$s.' Q';
  649. $this->_out($s);
  650. }
  651. function AcceptPageBreak()
  652. {
  653. // Accept automatic page break or not
  654. return $this->AutoPageBreak;
  655. }
  656. function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
  657. {
  658. // Output a cell
  659. $k = $this->k;
  660. if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
  661. {
  662. // Automatic page break
  663. $x = $this->x;
  664. $ws = $this->ws;
  665. if($ws>0)
  666. {
  667. $this->ws = 0;
  668. $this->_out('0 Tw');
  669. }
  670. $this->AddPage($this->CurOrientation,$this->CurPageSize);
  671. $this->x = $x;
  672. if($ws>0)
  673. {
  674. $this->ws = $ws;
  675. $this->_out(sprintf('%.3F Tw',$ws*$k));
  676. }
  677. }
  678. if($w==0)
  679. $w = $this->w-$this->rMargin-$this->x;
  680. $s = '';
  681. if($fill || $border==1)
  682. {
  683. if($fill)
  684. $op = ($border==1) ? 'B' : 'f';
  685. else
  686. $op = 'S';
  687. $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
  688. }
  689. if(is_string($border))
  690. {
  691. $x = $this->x;
  692. $y = $this->y;
  693. if(strpos($border,'L')!==false)
  694. $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
  695. if(strpos($border,'T')!==false)
  696. $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
  697. if(strpos($border,'R')!==false)
  698. $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
  699. if(strpos($border,'B')!==false)
  700. $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
  701. }
  702. if($txt!=='')
  703. {
  704. if($align=='R')
  705. $dx = $w-$this->cMargin-$this->GetStringWidth($txt);
  706. elseif($align=='C')
  707. $dx = ($w-$this->GetStringWidth($txt))/2;
  708. else
  709. $dx = $this->cMargin;
  710. if($this->ColorFlag)
  711. $s .= 'q '.$this->TextColor.' ';
  712. // If multibyte, Tw has no effect - do word spacing using an adjustment before each space
  713. if ($this->ws && $this->unifontSubset) {
  714. foreach($this->UTF8StringToArray($txt) as $uni)
  715. $this->CurrentFont['subset'][$uni] = $uni;
  716. $space = $this->_escape($this->UTF8ToUTF16BE(' ', false));
  717. $s .= sprintf('BT 0 Tw %.2F %.2F Td [',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k);
  718. $t = explode(' ',$txt);
  719. $numt = count($t);
  720. for($i=0;$i<$numt;$i++) {
  721. $tx = $t[$i];
  722. $tx = '('.$this->_escape($this->UTF8ToUTF16BE($tx, false)).')';
  723. $s .= sprintf('%s ',$tx);
  724. if (($i+1)<$numt) {
  725. $adj = -($this->ws*$this->k)*1000/$this->FontSizePt;
  726. $s .= sprintf('%d(%s) ',$adj,$space);
  727. }
  728. }
  729. $s .= '] TJ';
  730. $s .= ' ET';
  731. }
  732. else {
  733. if ($this->unifontSubset)
  734. {
  735. $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
  736. foreach($this->UTF8StringToArray($txt) as $uni)
  737. $this->CurrentFont['subset'][$uni] = $uni;
  738. }
  739. else
  740. $txt2='('.str_replace(')','\\)',str_replace('(','\\(',str_replace('\\','\\\\',$txt))).')';
  741. $s .= sprintf('BT %.2F %.2F Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
  742. }
  743. if($this->underline)
  744. $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
  745. if($this->ColorFlag)
  746. $s .= ' Q';
  747. if($link)
  748. $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
  749. }
  750. if($s)
  751. $this->_out($s);
  752. $this->lasth = $h;
  753. if($ln>0)
  754. {
  755. // Go to next line
  756. $this->y += $h;
  757. if($ln==1)
  758. $this->x = $this->lMargin;
  759. }
  760. else
  761. $this->x += $w;
  762. }
  763. function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false)
  764. {
  765. // Output text with automatic or explicit line breaks
  766. $cw = &$this->CurrentFont['cw'];
  767. if($w==0)
  768. $w = $this->w-$this->rMargin-$this->x;
  769. $wmax = ($w-2*$this->cMargin);
  770. $s = str_replace("\r",'',$txt);
  771. if ($this->unifontSubset) {
  772. $nb=mb_strlen($s, 'utf-8');
  773. while($nb>0 && mb_substr($s,$nb-1,1,'utf-8')=="\n") $nb--;
  774. }
  775. else {
  776. $nb = strlen($s);
  777. if($nb>0 && $s[$nb-1]=="\n")
  778. $nb--;
  779. }
  780. $b = 0;
  781. if($border)
  782. {
  783. if($border==1)
  784. {
  785. $border = 'LTRB';
  786. $b = 'LRT';
  787. $b2 = 'LR';
  788. }
  789. else
  790. {
  791. $b2 = '';
  792. if(strpos($border,'L')!==false)
  793. $b2 .= 'L';
  794. if(strpos($border,'R')!==false)
  795. $b2 .= 'R';
  796. $b = (strpos($border,'T')!==false) ? $b2.'T' : $b2;
  797. }
  798. }
  799. $sep = -1;
  800. $i = 0;
  801. $j = 0;
  802. $l = 0;
  803. $ns = 0;
  804. $nl = 1;
  805. while($i<$nb)
  806. {
  807. // Get next character
  808. if ($this->unifontSubset) {
  809. $c = mb_substr($s,$i,1,'UTF-8');
  810. }
  811. else {
  812. $c=$s[$i];
  813. }
  814. if($c=="\n")
  815. {
  816. // Explicit line break
  817. if($this->ws>0)
  818. {
  819. $this->ws = 0;
  820. $this->_out('0 Tw');
  821. }
  822. if ($this->unifontSubset) {
  823. $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill);
  824. }
  825. else {
  826. $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
  827. }
  828. $i++;
  829. $sep = -1;
  830. $j = $i;
  831. $l = 0;
  832. $ns = 0;
  833. $nl++;
  834. if($border && $nl==2)
  835. $b = $b2;
  836. continue;
  837. }
  838. if($c==' ')
  839. {
  840. $sep = $i;
  841. $ls = $l;
  842. $ns++;
  843. }
  844. if ($this->unifontSubset) { $l += $this->GetStringWidth($c); }
  845. else { $l += $cw[$c]*$this->FontSize/1000; }
  846. if($l>$wmax)
  847. {
  848. // Automatic line break
  849. if($sep==-1)
  850. {
  851. if($i==$j)
  852. $i++;
  853. if($this->ws>0)
  854. {
  855. $this->ws = 0;
  856. $this->_out('0 Tw');
  857. }
  858. if ($this->unifontSubset) {
  859. $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill);
  860. }
  861. else {
  862. $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
  863. }
  864. }
  865. else
  866. {
  867. if($align=='J')
  868. {
  869. $this->ws = ($ns>1) ? ($wmax-$ls)/($ns-1) : 0;
  870. $this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
  871. }
  872. if ($this->unifontSubset) {
  873. $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),$b,2,$align,$fill);
  874. }
  875. else {
  876. $this->Cell($w,$h,substr($s,$j,$sep-$j),$b,2,$align,$fill);
  877. }
  878. $i = $sep+1;
  879. }
  880. $sep = -1;
  881. $j = $i;
  882. $l = 0;
  883. $ns = 0;
  884. $nl++;
  885. if($border && $nl==2)
  886. $b = $b2;
  887. }
  888. else
  889. $i++;
  890. }
  891. // Last chunk
  892. if($this->ws>0)
  893. {
  894. $this->ws = 0;
  895. $this->_out('0 Tw');
  896. }
  897. if($border && strpos($border,'B')!==false)
  898. $b .= 'B';
  899. if ($this->unifontSubset) {
  900. $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),$b,2,$align,$fill);
  901. }
  902. else {
  903. $this->Cell($w,$h,substr($s,$j,$i-$j),$b,2,$align,$fill);
  904. }
  905. $this->x = $this->lMargin;
  906. }
  907. function Write($h, $txt, $link='')
  908. {
  909. // Output text in flowing mode
  910. $cw = &$this->CurrentFont['cw'];
  911. $w = $this->w-$this->rMargin-$this->x;
  912. $wmax = ($w-2*$this->cMargin);
  913. $s = str_replace("\r",'',$txt);
  914. if ($this->unifontSubset) {
  915. $nb = mb_strlen($s, 'UTF-8');
  916. if($nb==1 && $s==" ") {
  917. $this->x += $this->GetStringWidth($s);
  918. return;
  919. }
  920. }
  921. else {
  922. $nb = strlen($s);
  923. }
  924. $sep = -1;
  925. $i = 0;
  926. $j = 0;
  927. $l = 0;
  928. $nl = 1;
  929. while($i<$nb)
  930. {
  931. // Get next character
  932. if ($this->unifontSubset) {
  933. $c = mb_substr($s,$i,1,'UTF-8');
  934. }
  935. else {
  936. $c = $s[$i];
  937. }
  938. if($c=="\n")
  939. {
  940. // Explicit line break
  941. if ($this->unifontSubset) {
  942. $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link);
  943. }
  944. else {
  945. $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
  946. }
  947. $i++;
  948. $sep = -1;
  949. $j = $i;
  950. $l = 0;
  951. if($nl==1)
  952. {
  953. $this->x = $this->lMargin;
  954. $w = $this->w-$this->rMargin-$this->x;
  955. $wmax = ($w-2*$this->cMargin);
  956. }
  957. $nl++;
  958. continue;
  959. }
  960. if($c==' ')
  961. $sep = $i;
  962. if ($this->unifontSubset) { $l += $this->GetStringWidth($c); }
  963. else { $l += $cw[$c]*$this->FontSize/1000; }
  964. if($l>$wmax)
  965. {
  966. // Automatic line break
  967. if($sep==-1)
  968. {
  969. if($this->x>$this->lMargin)
  970. {
  971. // Move to next line
  972. $this->x = $this->lMargin;
  973. $this->y += $h;
  974. $w = $this->w-$this->rMargin-$this->x;
  975. $wmax = ($w-2*$this->cMargin);
  976. $i++;
  977. $nl++;
  978. continue;
  979. }
  980. if($i==$j)
  981. $i++;
  982. if ($this->unifontSubset) {
  983. $this->Cell($w,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,2,'',0,$link);
  984. }
  985. else {
  986. $this->Cell($w,$h,substr($s,$j,$i-$j),0,2,'',0,$link);
  987. }
  988. }
  989. else
  990. {
  991. if ($this->unifontSubset) {
  992. $this->Cell($w,$h,mb_substr($s,$j,$sep-$j,'UTF-8'),0,2,'',0,$link);
  993. }
  994. else {
  995. $this->Cell($w,$h,substr($s,$j,$sep-$j),0,2,'',0,$link);
  996. }
  997. $i = $sep+1;
  998. }
  999. $sep = -1;
  1000. $j = $i;
  1001. $l = 0;
  1002. if($nl==1)
  1003. {
  1004. $this->x = $this->lMargin;
  1005. $w = $this->w-$this->rMargin-$this->x;
  1006. $wmax = ($w-2*$this->cMargin);
  1007. }
  1008. $nl++;
  1009. }
  1010. else
  1011. $i++;
  1012. }
  1013. // Last chunk
  1014. if($i!=$j) {
  1015. if ($this->unifontSubset) {
  1016. $this->Cell($l,$h,mb_substr($s,$j,$i-$j,'UTF-8'),0,0,'',0,$link);
  1017. }
  1018. else {
  1019. $this->Cell($l,$h,substr($s,$j),0,0,'',0,$link);
  1020. }
  1021. }
  1022. }
  1023. function Ln($h=null)
  1024. {
  1025. // Line feed; default value is last cell height
  1026. $this->x = $this->lMargin;
  1027. if($h===null)
  1028. $this->y += $this->lasth;
  1029. else
  1030. $this->y += $h;
  1031. }
  1032. function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='')
  1033. {
  1034. // Put an image on the page
  1035. if(!isset($this->images[$file]))
  1036. {
  1037. // First use of this image, get info
  1038. if($type=='')
  1039. {
  1040. $pos = strrpos($file,'.');
  1041. if(!$pos)
  1042. $this->Error('Image file has no extension and no type was specified: '.$file);
  1043. $type = substr($file,$pos+1);
  1044. }
  1045. $type = strtolower($type);
  1046. if($type=='jpeg')
  1047. $type = 'jpg';
  1048. $mtd = '_parse'.$type;
  1049. if(!method_exists($this,$mtd))
  1050. $this->Error('Unsupported image type: '.$type);
  1051. $info = $this->$mtd($file);
  1052. $info['i'] = count($this->images)+1;
  1053. $this->images[$file] = $info;
  1054. }
  1055. else
  1056. $info = $this->images[$file];
  1057. // Automatic width and height calculation if needed
  1058. if($w==0 && $h==0)
  1059. {
  1060. // Put image at 96 dpi
  1061. $w = -96;
  1062. $h = -96;
  1063. }
  1064. if($w<0)
  1065. $w = -$info['w']*72/$w/$this->k;
  1066. if($h<0)
  1067. $h = -$info['h']*72/$h/$this->k;
  1068. if($w==0)
  1069. $w = $h*$info['w']/$info['h'];
  1070. if($h==0)
  1071. $h = $w*$info['h']/$info['w'];
  1072. // Flowing mode
  1073. if($y===null)
  1074. {
  1075. if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
  1076. {
  1077. // Automatic page break
  1078. $x2 = $this->x;
  1079. $this->AddPage($this->CurOrientation,$this->CurPageSize);
  1080. $this->x = $x2;
  1081. }
  1082. $y = $this->y;
  1083. $this->y += $h;
  1084. }
  1085. if($x===null)
  1086. $x = $this->x;
  1087. $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q',$w*$this->k,$h*$this->k,$x*$this->k,($this->h-($y+$h))*$this->k,$info['i']));
  1088. if($link)
  1089. $this->Link($x,$y,$w,$h,$link);
  1090. }
  1091. function GetX()
  1092. {
  1093. // Get x position
  1094. return $this->x;
  1095. }
  1096. function SetX($x)
  1097. {
  1098. // Set x position
  1099. if($x>=0)
  1100. $this->x = $x;
  1101. else
  1102. $this->x = $this->w+$x;
  1103. }
  1104. function GetY()
  1105. {
  1106. // Get y position
  1107. return $this->y;
  1108. }
  1109. function SetY($y)
  1110. {
  1111. // Set y position and reset x
  1112. $this->x = $this->lMargin;
  1113. if($y>=0)
  1114. $this->y = $y;
  1115. else
  1116. $this->y = $this->h+$y;
  1117. }
  1118. function SetXY($x, $y)
  1119. {
  1120. // Set x and y positions
  1121. $this->SetY($y);
  1122. $this->SetX($x);
  1123. }
  1124. function Output($name='', $dest='')
  1125. {
  1126. // Output PDF to some destination
  1127. if($this->state<3)
  1128. $this->Close();
  1129. $dest = strtoupper($dest);
  1130. if($dest=='')
  1131. {
  1132. if($name=='')
  1133. {
  1134. $name = 'doc.pdf';
  1135. $dest = 'I';
  1136. }
  1137. else
  1138. $dest = 'F';
  1139. }
  1140. switch($dest)
  1141. {
  1142. case 'I':
  1143. // Send to standard output
  1144. $this->_checkoutput();
  1145. if(PHP_SAPI!='cli')
  1146. {
  1147. // We send to a browser
  1148. header('Content-Type: application/pdf');
  1149. header('Content-Disposition: inline; filename="'.$name.'"');
  1150. header('Cache-Control: private, max-age=0, must-revalidate');
  1151. header('Pragma: public');
  1152. }
  1153. echo $this->buffer;
  1154. break;
  1155. case 'D':
  1156. // Download file
  1157. $this->_checkoutput();
  1158. header('Content-Type: application/x-download');
  1159. header('Content-Disposition: attachment; filename="'.$name.'"');
  1160. header('Cache-Control: private, max-age=0, must-revalidate');
  1161. header('Pragma: public');
  1162. echo $this->buffer;
  1163. break;
  1164. case 'F':
  1165. // Save to local file
  1166. $f = fopen($name,'wb');
  1167. if(!$f)
  1168. $this->Error('Unable to create output file: '.$name);
  1169. fwrite($f,$this->buffer,strlen($this->buffer));
  1170. fclose($f);
  1171. break;
  1172. case 'S':
  1173. // Return as a string
  1174. return $this->buffer;
  1175. default:
  1176. $this->Error('Incorrect output destination: '.$dest);
  1177. }
  1178. return '';
  1179. }
  1180. /*******************************************************************************
  1181. * *
  1182. * Protected methods *
  1183. * *
  1184. *******************************************************************************/
  1185. function _dochecks()
  1186. {
  1187. // Check availability of %F
  1188. if(sprintf('%.1F',1.0)!='1.0')
  1189. $this->Error('This version of PHP is not supported');
  1190. // Check availability of mbstring
  1191. if(!function_exists('mb_strlen'))
  1192. $this->Error('mbstring extension is not available');
  1193. // Check mbstring overloading
  1194. if(ini_get('mbstring.func_overload') & 2)
  1195. $this->Error('mbstring overloading must be disabled');
  1196. // Ensure runtime magic quotes are disabled
  1197. if(get_magic_quotes_runtime())
  1198. @set_magic_quotes_runtime(0);
  1199. }
  1200. function _getfontpath()
  1201. {
  1202. return $this->fontpath;
  1203. }
  1204. function _checkoutput()
  1205. {
  1206. if(PHP_SAPI!='cli')
  1207. {
  1208. if(headers_sent($file,$line))
  1209. $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)");
  1210. }
  1211. if(ob_get_length())
  1212. {
  1213. // The output buffer is not empty
  1214. if(preg_match('/^(\xEF\xBB\xBF)?\s*$/',ob_get_contents()))
  1215. {
  1216. // It contains only a UTF-8 BOM and/or whitespace, let's clean it
  1217. ob_clean();
  1218. }
  1219. else
  1220. $this->Error("Some data has already been output, can't send PDF file");
  1221. }
  1222. }
  1223. function _getpagesize($size)
  1224. {
  1225. if(is_string($size))
  1226. {
  1227. $size = strtolower($size);
  1228. if(!isset($this->StdPageSizes[$size]))
  1229. $this->Error('Unknown page size: '.$size);
  1230. $a = $this->StdPageSizes[$size];
  1231. return array($a[0]/$this->k, $a[1]/$this->k);
  1232. }
  1233. else
  1234. {
  1235. if($size[0]>$size[1])
  1236. return array($size[1], $size[0]);
  1237. else
  1238. return $size;
  1239. }
  1240. }
  1241. function _beginpage($orientation, $size)
  1242. {
  1243. $this->page++;
  1244. $this->pages[$this->page] = '';
  1245. $this->state = 2;
  1246. $this->x = $this->lMargin;
  1247. $this->y = $this->tMargin;
  1248. $this->FontFamily = '';
  1249. // Check page size and orientation
  1250. if($orientation=='')
  1251. $orientation = $this->DefOrientation;
  1252. else
  1253. $orientation = strtoupper($orientation[0]);
  1254. if($size=='')
  1255. $size = $this->DefPageSize;
  1256. else
  1257. $size = $this->_getpagesize($size);
  1258. if($orientation!=$this->CurOrientation || $size[0]!=$this->CurPageSize[0] || $size[1]!=$this->CurPageSize[1])
  1259. {
  1260. // New size or orientation
  1261. if($orientation=='P')
  1262. {
  1263. $this->w = $size[0];
  1264. $this->h = $size[1];
  1265. }
  1266. else
  1267. {
  1268. $this->w = $size[1];
  1269. $this->h = $size[0];
  1270. }
  1271. $this->wPt = $this->w*$this->k;
  1272. $this->hPt = $this->h*$this->k;
  1273. $this->PageBreakTrigger = $this->h-$this->bMargin;
  1274. $this->CurOrientation = $orientation;
  1275. $this->CurPageSize = $size;
  1276. }
  1277. if($orientation!=$this->DefOrientation || $size[0]!=$this->DefPageSize[0] || $size[1]!=$this->DefPageSize[1])
  1278. $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
  1279. }
  1280. function _endpage()
  1281. {
  1282. $this->state = 1;
  1283. }
  1284. function _loadfont($font)
  1285. {
  1286. // Load a font definition file from the font directory
  1287. include($this->fontpath.$font);
  1288. $a = get_defined_vars();
  1289. if(!isset($a['name']))
  1290. $this->Error('Could not include font definition file');
  1291. return $a;
  1292. }
  1293. function _escape($s)
  1294. {
  1295. // Escape special characters in strings
  1296. $s = str_replace('\\','\\\\',$s);
  1297. $s = str_replace('(','\\(',$s);
  1298. $s = str_replace(')','\\)',$s);
  1299. $s = str_replace("\r",'\\r',$s);
  1300. return $s;
  1301. }
  1302. function _textstring($s)
  1303. {
  1304. // Format a text string
  1305. return '('.$this->_escape($s).')';
  1306. }
  1307. function _UTF8toUTF16($s)
  1308. {
  1309. // Convert UTF-8 to UTF-16BE with BOM
  1310. $res = "\xFE\xFF";
  1311. $nb = strlen($s);
  1312. $i = 0;
  1313. while($i<$nb)
  1314. {
  1315. $c1 = ord($s[$i++]);
  1316. if($c1>=224)
  1317. {
  1318. // 3-byte character
  1319. $c2 = ord($s[$i++]);
  1320. $c3 = ord($s[$i++]);
  1321. $res .= chr((($c1 & 0x0F)<<4) + (($c2 & 0x3C)>>2));
  1322. $res .= chr((($c2 & 0x03)<<6) + ($c3 & 0x3F));
  1323. }
  1324. elseif($c1>=192)
  1325. {
  1326. // 2-byte character
  1327. $c2 = ord($s[$i++]);
  1328. $res .= chr(($c1 & 0x1C)>>2);
  1329. $res .= chr((($c1 & 0x03)<<6) + ($c2 & 0x3F));
  1330. }
  1331. else
  1332. {
  1333. // Single-byte character
  1334. $res .= "\0".chr($c1);
  1335. }
  1336. }
  1337. return $res;
  1338. }
  1339. function _dounderline($x, $y, $txt)
  1340. {
  1341. // Underline text
  1342. $up = $this->CurrentFont['up'];
  1343. $ut = $this->CurrentFont['ut'];
  1344. $w = $this->GetStringWidth($txt)+$this->ws*substr_count($txt,' ');
  1345. 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);
  1346. }
  1347. function _parsejpg($file)
  1348. {
  1349. // Extract info from a JPEG file
  1350. $a = getimagesize($file);
  1351. if(!$a)
  1352. $this->Error('Missing or incorrect image file: '.$file);
  1353. if($a[2]!=2)
  1354. $this->Error('Not a JPEG file: '.$file);
  1355. if(!isset($a['channels']) || $a['channels']==3)
  1356. $colspace = 'DeviceRGB';
  1357. elseif($a['channels']==4)
  1358. $colspace = 'DeviceCMYK';
  1359. else
  1360. $colspace = 'DeviceGray';
  1361. $bpc = isset($a['bits']) ? $a['bits'] : 8;
  1362. $data = file_get_contents($file);
  1363. return array('w'=>$a[0], 'h'=>$a[1], 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'DCTDecode', 'data'=>$data);
  1364. }
  1365. function _parsepng($file)
  1366. {
  1367. // Extract info from a PNG file
  1368. $f = fopen($file,'rb');
  1369. if(!$f)
  1370. $this->Error('Can\'t open image file: '.$file);
  1371. $info = $this->_parsepngstream($f,$file);
  1372. fclose($f);
  1373. return $info;
  1374. }
  1375. function _parsepngstream($f, $file)
  1376. {
  1377. // Check signature
  1378. if($this->_readstream($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10))
  1379. $this->Error('Not a PNG file: '.$file);
  1380. // Read header chunk
  1381. $this->_readstream($f,4);
  1382. if($this->_readstream($f,4)!='IHDR')
  1383. $this->Error('Incorrect PNG file: '.$file);
  1384. $w = $this->_readint($f);
  1385. $h = $this->_readint($f);
  1386. $bpc = ord($this->_readstream($f,1));
  1387. if($bpc>8)
  1388. $this->Error('16-bit depth not supported: '.$file);
  1389. $ct = ord($this->_readstream($f,1));
  1390. if($ct==0 || $ct==4)
  1391. $colspace = 'DeviceGray';
  1392. elseif($ct==2 || $ct==6)
  1393. $colspace = 'DeviceRGB';
  1394. elseif($ct==3)
  1395. $colspace = 'Indexed';
  1396. else
  1397. $this->Error('Unknown color type: '.$file);
  1398. if(ord($this->_readstream($f,1))!=0)
  1399. $this->Error('Unknown compression method: '.$file);
  1400. if(ord($this->_readstream($f,1))!=0)
  1401. $this->Error('Unknown filter method: '.$file);
  1402. if(ord($this->_readstream($f,1))!=0)
  1403. $this->Error('Interlacing not supported: '.$file);
  1404. $this->_readstream($f,4);
  1405. $dp = '/Predictor 15 /Colors '.($colspace=='DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w;
  1406. // Scan chunks looking for palette, transparency and image data
  1407. $pal = '';
  1408. $trns = '';
  1409. $data = '';
  1410. do
  1411. {
  1412. $n = $this->_readint($f);
  1413. $type = $this->_readstream($f,4);
  1414. if($type=='PLTE')
  1415. {
  1416. // Read palette
  1417. $pal = $this->_readstream($f,$n);
  1418. $this->_readstream($f,4);
  1419. }
  1420. elseif($type=='tRNS')
  1421. {
  1422. // Read transparency info
  1423. $t = $this->_readstream($f,$n);
  1424. if($ct==0)
  1425. $trns = array(ord(substr($t,1,1)));
  1426. elseif($ct==2)
  1427. $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
  1428. else
  1429. {
  1430. $pos = strpos($t,chr(0));
  1431. if($pos!==false)
  1432. $trns = array($pos);
  1433. }
  1434. $this->_readstream($f,4);
  1435. }
  1436. elseif($type=='IDAT')
  1437. {
  1438. // Read image data block
  1439. $data .= $this->_readstream($f,$n);
  1440. $this->_readstream($f,4);
  1441. }
  1442. elseif($type=='IEND')
  1443. break;
  1444. else
  1445. $this->_readstream($f,$n+4);
  1446. }
  1447. while($n);
  1448. if($colspace=='Indexed' && empty($pal))
  1449. $this->Error('Missing palette in '.$file);
  1450. $info = array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'dp'=>$dp, 'pal'=>$pal, 'trns'=>$trns);
  1451. if($ct>=4)
  1452. {
  1453. // Extract alpha channel
  1454. if(!function_exists('gzuncompress'))
  1455. $this->Error('Zlib not available, can\'t handle alpha channel: '.$file);
  1456. $data = gzuncompress($data);
  1457. $color = '';
  1458. $alpha = '';
  1459. if($ct==4)
  1460. {
  1461. // Gray image
  1462. $len = 2*$w;
  1463. for($i=0;$i<$h;$i++)
  1464. {
  1465. $pos = (1+$len)*$i;
  1466. $color .= $data[$pos];
  1467. $alpha .= $data[$pos];
  1468. $line = substr($data,$pos+1,$len);
  1469. $color .= preg_replace('/(.)./s','$1',$line);
  1470. $alpha .= preg_replace('/.(.)/s','$1',$line);
  1471. }
  1472. }
  1473. else
  1474. {
  1475. // RGB image
  1476. $len = 4*$w;
  1477. for($i=0;$i<$h;$i++)
  1478. {
  1479. $pos = (1+$len)*$i;
  1480. $color .= $data[$pos];
  1481. $alpha .= $data[$pos];
  1482. $line = substr($data,$pos+1,$len);
  1483. $color .= preg_replace('/(.{3})./s','$1',$line);
  1484. $alpha .= preg_replace('/.{3}(.)/s','$1',$line);
  1485. }
  1486. }
  1487. unset($data);
  1488. $data = gzcompress($color);
  1489. $info['smask'] = gzcompress($alpha);
  1490. if($this->PDFVersion<'1.4')
  1491. $this->PDFVersion = '1.4';
  1492. }
  1493. $info['data'] = $data;
  1494. return $info;
  1495. }
  1496. function _readstream($f, $n)
  1497. {
  1498. // Read n bytes from stream
  1499. $res = '';
  1500. while($n>0 && !feof($f))
  1501. {
  1502. $s = fread($f,$n);
  1503. if($s===false)
  1504. $this->Error('Error while reading stream');
  1505. $n -= strlen($s);
  1506. $res .= $s;
  1507. }
  1508. if($n>0)
  1509. $this->Error('Unexpected end of stream');
  1510. return $res;
  1511. }
  1512. function _readint($f)
  1513. {
  1514. // Read a 4-byte integer from stream
  1515. $a = unpack('Ni',$this->_readstream($f,4));
  1516. return $a['i'];
  1517. }
  1518. function _parsegif($file)
  1519. {
  1520. // Extract info from a GIF file (via PNG conversion)
  1521. if(!function_exists('imagepng'))
  1522. $this->Error('GD extension is required for GIF support');
  1523. if(!function_exists('imagecreatefromgif'))
  1524. $this->Error('GD has no GIF read support');
  1525. $im = imagecreatefromgif($file);
  1526. if(!$im)
  1527. $this->Error('Missing or incorrect image file: '.$file);
  1528. imageinterlace($im,0);
  1529. $f = @fopen('php://temp','rb+');
  1530. if($f)
  1531. {
  1532. // Perform conversion in memory
  1533. ob_start();
  1534. imagepng($im);
  1535. $data = ob_get_clean();
  1536. imagedestroy($im);
  1537. fwrite($f,$data);
  1538. rewind($f);
  1539. $info = $this->_parsepngstream($f,$file);
  1540. fclose($f);
  1541. }
  1542. else
  1543. {
  1544. // Use temporary file
  1545. $tmp = tempnam('.','gif');
  1546. if(!$tmp)
  1547. $this->Error('Unable to create a temporary file');
  1548. if(!imagepng($im,$tmp))
  1549. $this->Error('Error while saving to temporary file');
  1550. imagedestroy($im);
  1551. $info = $this->_parsepng($tmp);
  1552. unlink($tmp);
  1553. }
  1554. return $info;
  1555. }
  1556. function _newobj()
  1557. {
  1558. // Begin a new object
  1559. $this->n++;
  1560. $this->offsets[$this->n] = strlen($this->buffer);
  1561. $this->_out($this->n.' 0 obj');
  1562. }
  1563. function _putstream($s)
  1564. {
  1565. $this->_out('stream');
  1566. $this->_out($s);
  1567. $this->_out('endstream');
  1568. }
  1569. function _out($s)
  1570. {
  1571. // Add a line to the document
  1572. if($this->state==2)
  1573. $this->pages[$this->page] .= $s."\n";
  1574. else
  1575. $this->buffer .= $s."\n";
  1576. }
  1577. function _putpages()
  1578. {
  1579. $nb = $this->page;
  1580. if(!empty($this->AliasNbPages))
  1581. {
  1582. // Replace number of pages in fonts using subsets
  1583. $alias = $this->UTF8ToUTF16BE($this->AliasNbPages, false);
  1584. $r = $this->UTF8ToUTF16BE("$nb", false);
  1585. for($n=1;$n<=$nb;$n++)
  1586. $this->pages[$n] = str_replace($alias,$r,$this->pages[$n]);
  1587. // Now repeat for no pages in non-subset fonts
  1588. for($n=1;$n<=$nb;$n++)
  1589. $this->pages[$n] = str_replace($this->AliasNbPages,$nb,$this->pages[$n]);
  1590. }
  1591. if($this->DefOrientation=='P')
  1592. {
  1593. $wPt = $this->DefPageSize[0]*$this->k;
  1594. $hPt = $this->DefPageSize[1]*$this->k;
  1595. }
  1596. else
  1597. {
  1598. $wPt = $this->DefPageSize[1]*$this->k;
  1599. $hPt = $this->DefPageSize[0]*$this->k;
  1600. }
  1601. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  1602. for($n=1;$n<=$nb;$n++)
  1603. {
  1604. // Page
  1605. $this->_newobj();
  1606. $this->_out('<</Type /Page');
  1607. $this->_out('/Parent 1 0 R');
  1608. if(isset($this->PageSizes[$n]))
  1609. $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->PageSizes[$n][0],$this->PageSizes[$n][1]));
  1610. $this->_out('/Resources 2 0 R');
  1611. if(isset($this->PageLinks[$n]))
  1612. {
  1613. // Links
  1614. $annots = '/Annots [';
  1615. foreach($this->PageLinks[$n] as $pl)
  1616. {
  1617. $rect = sprintf('%.2F %.2F %.2F %.2F',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]);
  1618. $annots .= '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
  1619. if(is_string($pl[4]))
  1620. $annots .= '/A <</S /URI /URI '.$this->_textstring($pl[4]).'>>>>';
  1621. else
  1622. {
  1623. $l = $this->links[$pl[4]];
  1624. $h = isset($this->PageSizes[$l[0]]) ? $this->PageSizes[$l[0]][1] : $hPt;
  1625. $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>',1+2*$l[0],$h-$l[1]*$this->k);
  1626. }
  1627. }
  1628. $this->_out($annots.']');
  1629. }
  1630. if($this->PDFVersion>'1.3')
  1631. $this->_out('/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>');
  1632. $this->_out('/Contents '.($this->n+1).' 0 R>>');
  1633. $this->_out('endobj');
  1634. // Page content
  1635. $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
  1636. $this->_newobj();
  1637. $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
  1638. $this->_putstream($p);
  1639. $this->_out('endobj');
  1640. }
  1641. // Pages root
  1642. $this->offsets[1] = strlen($this->buffer);
  1643. $this->_out('1 0 obj');
  1644. $this->_out('<</Type /Pages');
  1645. $kids = '/Kids [';
  1646. for($i=0;$i<$nb;$i++)
  1647. $kids .= (3+2*$i).' 0 R ';
  1648. $this->_out($kids.']');
  1649. $this->_out('/Count '.$nb);
  1650. $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$wPt,$hPt));
  1651. $this->_out('>>');
  1652. $this->_out('endobj');
  1653. }
  1654. function _putfonts()
  1655. {
  1656. $nf=$this->n;
  1657. foreach($this->diffs as $diff)
  1658. {
  1659. // Encodings
  1660. $this->_newobj();
  1661. $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
  1662. $this->_out('endobj');
  1663. }
  1664. foreach($this->FontFiles as $file=>$info)
  1665. {
  1666. if (!isset($info['type']) || $info['type']!='TTF') {
  1667. // Font file embedding
  1668. $this->_newobj();
  1669. $this->FontFiles[$file]['n']=$this->n;
  1670. $font='';
  1671. $f=fopen($this->_getfontpath().$file,'rb',1);
  1672. if(!$f)
  1673. $this->Error('Font file not found');
  1674. while(!feof($f))
  1675. $font.=fread($f,8192);
  1676. fclose($f);
  1677. $compressed=(substr($file,-2)=='.z');
  1678. if(!$compressed && isset($info['length2']))
  1679. {
  1680. $header=(ord($font[0])==128);
  1681. if($header)
  1682. {
  1683. // Strip first binary header
  1684. $font=substr($font,6);
  1685. }
  1686. if($header && ord($font[$info['length1']])==128)
  1687. {
  1688. // Strip second binary header
  1689. $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6);
  1690. }
  1691. }
  1692. $this->_out('<</Length '.strlen($font));
  1693. if($compressed)
  1694. $this->_out('/Filter /FlateDecode');
  1695. $this->_out('/Length1 '.$info['length1']);
  1696. if(isset($info['length2']))
  1697. $this->_out('/Length2 '.$info['length2'].' /Length3 0');
  1698. $this->_out('>>');
  1699. $this->_putstream($font);
  1700. $this->_out('endobj');
  1701. }
  1702. }
  1703. foreach($this->fonts as $k=>$font)
  1704. {
  1705. // Font objects
  1706. //$this->fonts[$k]['n']=$this->n+1;
  1707. $type = $font['type'];
  1708. $name = $font['name'];
  1709. if($type=='Core')
  1710. {
  1711. // Standard font
  1712. $this->fonts[$k]['n']=$this->n+1;
  1713. $this->_newobj();
  1714. $this->_out('<</Type /Font');
  1715. $this->_out('/BaseFont /'.$name);
  1716. $this->_out('/Subtype /Type1');
  1717. if($name!='Symbol' && $name!='ZapfDingbats')
  1718. $this->_out('/Encoding /WinAnsiEncoding');
  1719. $this->_out('>>');
  1720. $this->_out('endobj');
  1721. }
  1722. elseif($type=='Type1' || $type=='TrueType')
  1723. {
  1724. // Additional Type1 or TrueType font
  1725. $this->fonts[$k]['n']=$this->n+1;
  1726. $this->_newobj();
  1727. $this->_out('<</Type /Font');
  1728. $this->_out('/BaseFont /'.$name);
  1729. $this->_out('/Subtype /'.$type);
  1730. $this->_out('/FirstChar 32 /LastChar 255');
  1731. $this->_out('/Widths '.($this->n+1).' 0 R');
  1732. $this->_out('/FontDescriptor '.($this->n+2).' 0 R');
  1733. if($font['enc'])
  1734. {
  1735. if(isset($font['diff']))
  1736. $this->_out('/Encoding '.($nf+$font['diff']).' 0 R');
  1737. else
  1738. $this->_out('/Encoding /WinAnsiEncoding');
  1739. }
  1740. $this->_out('>>');
  1741. $this->_out('endobj');
  1742. // Widths
  1743. $this->_newobj();
  1744. $cw=&$font['cw'];
  1745. $s='[';
  1746. for($i=32;$i<=255;$i++)
  1747. $s.=$cw[chr($i)].' ';
  1748. $this->_out($s.']');
  1749. $this->_out('endobj');
  1750. // Descriptor
  1751. $this->_newobj();
  1752. $s='<</Type /FontDescriptor /FontName /'.$name;
  1753. foreach($font['desc'] as $k=>$v)
  1754. $s.=' /'.$k.' '.$v;
  1755. $file=$font['file'];
  1756. if($file)
  1757. $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
  1758. $this->_out($s.'>>');
  1759. $this->_out('endobj');
  1760. }
  1761. // TrueType embedded SUBSETS or FULL
  1762. else if ($type=='TTF') {
  1763. $this->fonts[$k]['n']=$this->n+1;
  1764. require_once($this->_getfontpath().'unifont/ttfonts.php');
  1765. $ttf = new TTFontFile();
  1766. $fontname = 'MPDFAA'.'+'.$font['name'];
  1767. $subset = $font['subset'];
  1768. unset($subset[0]);
  1769. $ttfontstream = $ttf->makeSubset($font['ttffile'], $subset);
  1770. $ttfontsize = strlen($ttfontstream);
  1771. $fontstream = gzcompress($ttfontstream);
  1772. $codeToGlyph = $ttf->codeToGlyph;
  1773. unset($codeToGlyph[0]);
  1774. // Type0 Font
  1775. // A composite font - a font composed of other fonts, organized hierarchically
  1776. $this->_newobj();
  1777. $this->_out('<</Type /Font');
  1778. $this->_out('/Subtype /Type0');
  1779. $this->_out('/BaseFont /'.$fontname.'');
  1780. $this->_out('/Encoding /Identity-H');
  1781. $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
  1782. $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
  1783. $this->_out('>>');
  1784. $this->_out('endobj');
  1785. // CIDFontType2
  1786. // A CIDFont whose glyph descriptions are based on TrueType font technology
  1787. $this->_newobj();
  1788. $this->_out('<</Type /Font');
  1789. $this->_out('/Subtype /CIDFontType2');
  1790. $this->_out('/BaseFont /'.$fontname.'');
  1791. $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
  1792. $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
  1793. if (isset($font['desc']['MissingWidth'])){
  1794. $this->_out('/DW '.$font['desc']['MissingWidth'].'');
  1795. }
  1796. $this->_putTTfontwidths($font, $ttf->maxUni);
  1797. $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
  1798. $this->_out('>>');
  1799. $this->_out('endobj');
  1800. // ToUnicode
  1801. $this->_newobj();
  1802. $toUni = "/CIDInit /ProcSet findresource begin\n";
  1803. $toUni .= "12 dict begin\n";
  1804. $toUni .= "begincmap\n";
  1805. $toUni .= "/CIDSystemInfo\n";
  1806. $toUni .= "<</Registry (Adobe)\n";
  1807. $toUni .= "/Ordering (UCS)\n";
  1808. $toUni .= "/Supplement 0\n";
  1809. $toUni .= ">> def\n";
  1810. $toUni .= "/CMapName /Adobe-Identity-UCS def\n";
  1811. $toUni .= "/CMapType 2 def\n";
  1812. $toUni .= "1 begincodespacerange\n";
  1813. $toUni .= "<0000> <FFFF>\n";
  1814. $toUni .= "endcodespacerange\n";
  1815. $toUni .= "1 beginbfrange\n";
  1816. $toUni .= "<0000> <FFFF> <0000>\n";
  1817. $toUni .= "endbfrange\n";
  1818. $toUni .= "endcmap\n";
  1819. $toUni .= "CMapName currentdict /CMap defineresource pop\n";
  1820. $toUni .= "end\n";
  1821. $toUni .= "end";
  1822. $this->_out('<</Length '.(strlen($toUni)).'>>');
  1823. $this->_putstream($toUni);
  1824. $this->_out('endobj');
  1825. // CIDSystemInfo dictionary
  1826. $this->_newobj();
  1827. $this->_out('<</Registry (Adobe)');
  1828. $this->_out('/Ordering (UCS)');
  1829. $this->_out('/Supplement 0');
  1830. $this->_out('>>');
  1831. $this->_out('endobj');
  1832. // Font descriptor
  1833. $this->_newobj();
  1834. $this->_out('<</Type /FontDescriptor');
  1835. $this->_out('/FontName /'.$fontname);
  1836. foreach($font['desc'] as $kd=>$v) {
  1837. if ($kd == 'Flags') { $v = $v | 4; $v = $v & ~32; } // SYMBOLIC font flag
  1838. $this->_out(' /'.$kd.' '.$v);
  1839. }
  1840. $this->_out('/FontFile2 '.($this->n + 2).' 0 R');
  1841. $this->_out('>>');
  1842. $this->_out('endobj');
  1843. // Embed CIDToGIDMap
  1844. // A specification of the mapping from CIDs to glyph indices
  1845. $cidtogidmap = '';
  1846. $cidtogidmap = str_pad('', 256*256*2, "\x00");
  1847. foreach($codeToGlyph as $cc=>$glyph) {
  1848. $cidtogidmap[$cc*2] = chr($glyph >> 8);
  1849. $cidtogidmap[$cc*2 + 1] = chr($glyph & 0xFF);
  1850. }
  1851. $cidtogidmap = gzcompress($cidtogidmap);
  1852. $this->_newobj();
  1853. $this->_out('<</Length '.strlen($cidtogidmap).'');
  1854. $this->_out('/Filter /FlateDecode');
  1855. $this->_out('>>');
  1856. $this->_putstream($cidtogidmap);
  1857. $this->_out('endobj');
  1858. //Font file
  1859. $this->_newobj();
  1860. $this->_out('<</Length '.strlen($fontstream));
  1861. $this->_out('/Filter /FlateDecode');
  1862. $this->_out('/Length1 '.$ttfontsize);
  1863. $this->_out('>>');
  1864. $this->_putstream($fontstream);
  1865. $this->_out('endobj');
  1866. unset($ttf);
  1867. }
  1868. else
  1869. {
  1870. // Allow for additional types
  1871. $this->fonts[$k]['n'] = $this->n+1;
  1872. $mtd='_put'.strtolower($type);
  1873. if(!method_exists($this,$mtd))
  1874. $this->Error('Unsupported font type: '.$type);
  1875. $this->$mtd($font);
  1876. }
  1877. }
  1878. }
  1879. function _putTTfontwidths(&$font, $maxUni) {
  1880. if (file_exists($font['unifilename'].'.cw127.php')) {
  1881. include($font['unifilename'].'.cw127.php') ;
  1882. $startcid = 128;
  1883. }
  1884. else {
  1885. $rangeid = 0;
  1886. $range = array();
  1887. $prevcid = -2;
  1888. $prevwidth = -1;
  1889. $interval = false;
  1890. $startcid = 1;
  1891. }
  1892. $cwlen = $maxUni + 1;
  1893. // for each character
  1894. for ($cid=$startcid; $cid<$cwlen; $cid++) {
  1895. if ($cid==128 && (!file_exists($font['unifilename'].'.cw127.php'))) {
  1896. if (is_writable(dirname($this->_getfontpath().'unifont/x'))) {
  1897. $fh = fopen($font['unifilename'].'.cw127.php',"wb");
  1898. $cw127='<?php'."\n";
  1899. $cw127.='$rangeid='.$rangeid.";\n";
  1900. $cw127.='$prevcid='.$prevcid.";\n";
  1901. $cw127.='$prevwidth='.$prevwidth.";\n";
  1902. if ($interval) { $cw127.='$interval=true'.";\n"; }
  1903. else { $cw127.='$interval=false'.";\n"; }
  1904. $cw127.='$range='.var_export($range,true).";\n";
  1905. $cw127.="?>";
  1906. fwrite($fh,$cw127,strlen($cw127));
  1907. fclose($fh);
  1908. }
  1909. }
  1910. if ($font['cw'][$cid*2] == "\00" && $font['cw'][$cid*2+1] == "\00") { continue; }
  1911. $width = (ord($font['cw'][$cid*2]) << 8) + ord($font['cw'][$cid*2+1]);
  1912. if ($width == 65535) { $width = 0; }
  1913. if ($cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) { continue; }
  1914. if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
  1915. if ($cid == ($prevcid + 1)) {
  1916. if ($width == $prevwidth) {
  1917. if ($width == $range[$rangeid][0]) {
  1918. $range[$rangeid][] = $width;
  1919. }
  1920. else {
  1921. array_pop($range[$rangeid]);
  1922. // new range
  1923. $rangeid = $prevcid;
  1924. $range[$rangeid] = array();
  1925. $range[$rangeid][] = $prevwidth;
  1926. $range[$rangeid][] = $width;
  1927. }
  1928. $interval = true;
  1929. $range[$rangeid]['interval'] = true;
  1930. } else {
  1931. if ($interval) {
  1932. // new range
  1933. $rangeid = $cid;
  1934. $range[$rangeid] = array();
  1935. $range[$rangeid][] = $width;
  1936. }
  1937. else { $range[$rangeid][] = $width; }
  1938. $interval = false;
  1939. }
  1940. } else {
  1941. $rangeid = $cid;
  1942. $range[$rangeid] = array();
  1943. $range[$rangeid][] = $width;
  1944. $interval = false;
  1945. }
  1946. $prevcid = $cid;
  1947. $prevwidth = $width;
  1948. }
  1949. }
  1950. $prevk = -1;
  1951. $nextk = -1;
  1952. $prevint = false;
  1953. foreach ($range as $k => $ws) {
  1954. $cws = count($ws);
  1955. if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
  1956. if (isset($range[$k]['interval'])) { unset($range[$k]['interval']); }
  1957. $range[$prevk] = array_merge($range[$prevk], $range[$k]);
  1958. unset($range[$k]);
  1959. }
  1960. else { $prevk = $k; }
  1961. $nextk = $k + $cws;
  1962. if (isset($ws['interval'])) {
  1963. if ($cws > 3) { $prevint = true; }
  1964. else { $prevint = false; }
  1965. unset($range[$k]['interval']);
  1966. --$nextk;
  1967. }
  1968. else { $prevint = false; }
  1969. }
  1970. $w = '';
  1971. foreach ($range as $k => $ws) {
  1972. if (count(array_count_values($ws)) == 1) { $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0]; }
  1973. else { $w .= ' '.$k.' [ '.implode(' ', $ws).' ]' . "\n"; }
  1974. }
  1975. $this->_out('/W ['.$w.' ]');
  1976. }
  1977. function _putimages()
  1978. {
  1979. foreach(array_keys($this->images) as $file)
  1980. {
  1981. $this->_putimage($this->images[$file]);
  1982. unset($this->images[$file]['data']);
  1983. unset($this->images[$file]['smask']);
  1984. }
  1985. }
  1986. function _putimage(&$info)
  1987. {
  1988. $this->_newobj();
  1989. $info['n'] = $this->n;
  1990. $this->_out('<</Type /XObject');
  1991. $this->_out('/Subtype /Image');
  1992. $this->_out('/Width '.$info['w']);
  1993. $this->_out('/Height '.$info['h']);
  1994. if($info['cs']=='Indexed')
  1995. $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]');
  1996. else
  1997. {
  1998. $this->_out('/ColorSpace /'.$info['cs']);
  1999. if($info['cs']=='DeviceCMYK')
  2000. $this->_out('/Decode [1 0 1 0 1 0 1 0]');
  2001. }
  2002. $this->_out('/BitsPerComponent '.$info['bpc']);
  2003. if(isset($info['f']))
  2004. $this->_out('/Filter /'.$info['f']);
  2005. if(isset($info['dp']))
  2006. $this->_out('/DecodeParms <<'.$info['dp'].'>>');
  2007. if(isset($info['trns']) && is_array($info['trns']))
  2008. {
  2009. $trns = '';
  2010. for($i=0;$i<count($info['trns']);$i++)
  2011. $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
  2012. $this->_out('/Mask ['.$trns.']');
  2013. }
  2014. if(isset($info['smask']))
  2015. $this->_out('/SMask '.($this->n+1).' 0 R');
  2016. $this->_out('/Length '.strlen($info['data']).'>>');
  2017. $this->_putstream($info['data']);
  2018. $this->_out('endobj');
  2019. // Soft mask
  2020. if(isset($info['smask']))
  2021. {
  2022. $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w'];
  2023. $smask = array('w'=>$info['w'], 'h'=>$info['h'], 'cs'=>'DeviceGray', 'bpc'=>8, 'f'=>$info['f'], 'dp'=>$dp, 'data'=>$info['smask']);
  2024. $this->_putimage($smask);
  2025. }
  2026. // Palette
  2027. if($info['cs']=='Indexed')
  2028. {
  2029. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  2030. $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
  2031. $this->_newobj();
  2032. $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
  2033. $this->_putstream($pal);
  2034. $this->_out('endobj');
  2035. }
  2036. }
  2037. function _putxobjectdict()
  2038. {
  2039. foreach($this->images as $image)
  2040. $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
  2041. }
  2042. function _putresourcedict()
  2043. {
  2044. $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  2045. $this->_out('/Font <<');
  2046. foreach($this->fonts as $font) {
  2047. $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
  2048. }
  2049. $this->_out('>>');
  2050. $this->_out('/XObject <<');
  2051. $this->_putxobjectdict();
  2052. $this->_out('>>');
  2053. }
  2054. function _putresources()
  2055. {
  2056. $this->_putfonts();
  2057. $this->_putimages();
  2058. // Resource dictionary
  2059. $this->offsets[2] = strlen($this->buffer);
  2060. $this->_out('2 0 obj');
  2061. $this->_out('<<');
  2062. $this->_putresourcedict();
  2063. $this->_out('>>');
  2064. $this->_out('endobj');
  2065. }
  2066. function _putinfo()
  2067. {
  2068. $this->_out('/Producer '.$this->_textstring('tFPDF '.tFPDF_VERSION));
  2069. if(!empty($this->title))
  2070. $this->_out('/Title '.$this->_textstring($this->title));
  2071. if(!empty($this->subject))
  2072. $this->_out('/Subject '.$this->_textstring($this->subject));
  2073. if(!empty($this->author))
  2074. $this->_out('/Author '.$this->_textstring($this->author));
  2075. if(!empty($this->keywords))
  2076. $this->_out('/Keywords '.$this->_textstring($this->keywords));
  2077. if(!empty($this->creator))
  2078. $this->_out('/Creator '.$this->_textstring($this->creator));
  2079. $this->_out('/CreationDate '.$this->_textstring('D:'.@date('YmdHis')));
  2080. }
  2081. function _putcatalog()
  2082. {
  2083. $this->_out('/Type /Catalog');
  2084. $this->_out('/Pages 1 0 R');
  2085. if($this->ZoomMode=='fullpage')
  2086. $this->_out('/OpenAction [3 0 R /Fit]');
  2087. elseif($this->ZoomMode=='fullwidth')
  2088. $this->_out('/OpenAction [3 0 R /FitH null]');
  2089. elseif($this->ZoomMode=='real')
  2090. $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
  2091. elseif(!is_string($this->ZoomMode))
  2092. $this->_out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2F',$this->ZoomMode/100).']');
  2093. if($this->LayoutMode=='single')
  2094. $this->_out('/PageLayout /SinglePage');
  2095. elseif($this->LayoutMode=='continuous')
  2096. $this->_out('/PageLayout /OneColumn');
  2097. elseif($this->LayoutMode=='two')
  2098. $this->_out('/PageLayout /TwoColumnLeft');
  2099. }
  2100. function _putheader()
  2101. {
  2102. $this->_out('%PDF-'.$this->PDFVersion);
  2103. }
  2104. function _puttrailer()
  2105. {
  2106. $this->_out('/Size '.($this->n+1));
  2107. $this->_out('/Root '.$this->n.' 0 R');
  2108. $this->_out('/Info '.($this->n-1).' 0 R');
  2109. }
  2110. function _enddoc()
  2111. {
  2112. $this->_putheader();
  2113. $this->_putpages();
  2114. $this->_putresources();
  2115. // Info
  2116. $this->_newobj();
  2117. $this->_out('<<');
  2118. $this->_putinfo();
  2119. $this->_out('>>');
  2120. $this->_out('endobj');
  2121. // Catalog
  2122. $this->_newobj();
  2123. $this->_out('<<');
  2124. $this->_putcatalog();
  2125. $this->_out('>>');
  2126. $this->_out('endobj');
  2127. // Cross-ref
  2128. $o = strlen($this->buffer);
  2129. $this->_out('xref');
  2130. $this->_out('0 '.($this->n+1));
  2131. $this->_out('0000000000 65535 f ');
  2132. for($i=1;$i<=$this->n;$i++)
  2133. $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
  2134. // Trailer
  2135. $this->_out('trailer');
  2136. $this->_out('<<');
  2137. $this->_puttrailer();
  2138. $this->_out('>>');
  2139. $this->_out('startxref');
  2140. $this->_out($o);
  2141. $this->_out('%%EOF');
  2142. $this->state = 3;
  2143. }
  2144. // ********* NEW FUNCTIONS *********
  2145. // Converts UTF-8 strings to UTF16-BE.
  2146. function UTF8ToUTF16BE($str, $setbom=true) {
  2147. $outstr = "";
  2148. if ($setbom) {
  2149. $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
  2150. }
  2151. $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8');
  2152. return $outstr;
  2153. }
  2154. // Converts UTF-8 strings to codepoints array
  2155. function UTF8StringToArray($str) {
  2156. $out = array();
  2157. $len = strlen($str);
  2158. for ($i = 0; $i < $len; $i++) {
  2159. $uni = -1;
  2160. $h = ord($str[$i]);
  2161. if ( $h <= 0x7F )
  2162. $uni = $h;
  2163. elseif ( $h >= 0xC2 ) {
  2164. if ( ($h <= 0xDF) && ($i < $len -1) )
  2165. $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
  2166. elseif ( ($h <= 0xEF) && ($i < $len -2) )
  2167. $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6
  2168. | (ord($str[++$i]) & 0x3F);
  2169. elseif ( ($h <= 0xF4) && ($i < $len -3) )
  2170. $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12
  2171. | (ord($str[++$i]) & 0x3F) << 6
  2172. | (ord($str[++$i]) & 0x3F);
  2173. }
  2174. if ($uni >= 0) {
  2175. $out[] = $uni;
  2176. }
  2177. }
  2178. return $out;
  2179. }
  2180. // End of class
  2181. }
  2182. // Handle special IE contype request
  2183. if(isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT']=='contype')
  2184. {
  2185. header('Content-Type: application/pdf');
  2186. exit;
  2187. }
  2188. ?>