ILIAS  Release_4_0_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
tcpdf.php
Go to the documentation of this file.
1 <?php
2 //============================================================+
3 // File name : tcpdf.php
4 // Begin : 2002-08-03
5 // Last Update : 2009-07-24
6 // Author : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
7 // Version : 4.6.022
8 // License : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
9 // ----------------------------------------------------------------------------
10 // Copyright (C) 2002-2009 Nicola Asuni - Tecnick.com S.r.l.
11 //
12 // This program is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU Lesser General Public License as published by
14 // the Free Software Foundation, either version 2.1 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Lesser General Public License for more details.
21 //
22 // You should have received a copy of the GNU Lesser General Public License
23 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //
25 // See LICENSE.TXT file for more information.
26 // ----------------------------------------------------------------------------
27 //
28 // Description : This is a PHP class for generating PDF documents without
29 // requiring external extensions.
30 //
31 // NOTE:
32 // This class was originally derived in 2002 from the Public
33 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
34 // but now is almost entirely rewritten.
35 //
36 // Main features:
37 // * no external libraries are required for the basic functions;
38 // * supports all ISO page formats;
39 // * supports custom page formats, margins and units of measure;
40 // * supports UTF-8 Unicode and Right-To-Left languages;
41 // * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
42 // * supports document encryption;
43 // * includes methods to publish some XHTML code;
44 // * includes graphic (geometric) and transformation methods;
45 // * includes Javascript and forms support;
46 // * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;
47 // * includes methods to set Bookmarks and print a Table of Content;
48 // * includes methods to move and delete pages;
49 // * includes methods for automatic page header and footer management;
50 // * supports automatic page break;
51 // * supports automatic page numbering and page groups;
52 // * supports automatic line break and text justification;
53 // * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
54 // * supports stroke and clipping mode for text;
55 // * supports clipping masks;
56 // * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
57 // * supports several annotations, including links, text and file attachments;
58 // * supports page compression (requires zlib extension);
59 // * supports text hyphenation.
60 // * supports transactions to UNDO commands.
61 //
62 // -----------------------------------------------------------
63 // THANKS TO:
64 //
65 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
66 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
67 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
68 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
69 // dullus for text Justification.
70 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
71 // Patrick Benny for text stretch suggestion on Cell().
72 // Johannes Güntert for JavaScript support.
73 // Denis Van Nuffelen for Dynamic Form.
74 // Jacek Czekaj for multibyte justification
75 // Anthony Ferrara for the reintroduction of legacy image methods.
76 // Sourceforge user 1707880 (hucste) for line-trough mode.
77 // Larry Stanbery for page groups.
78 // Martin Hall-May for transparency.
79 // Aaron C. Spike for Polycurve method.
80 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
81 // Moritz Wagner and Andreas Wurmser for graphic functions.
82 // Andrew Whitehead for core fonts support.
83 // Esteban Joël Marín for OpenType font conversion.
84 // Teus Hagen for several suggestions and fixes.
85 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
86 // Kosmas Papachristos for some CSS improvements.
87 // Marcel Partap for some fixes.
88 // Won Kyu Park for several suggestions, fixes and patches.
89 // Anyone that has reported a bug or sent a suggestion.
90 //============================================================+
91 
135 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
136 
137 // includes some support files
138 
142 require_once(dirname(__FILE__).'/unicode_data.php');
143 
147 require_once(dirname(__FILE__).'/htmlcolors.php');
148 
149 if (!class_exists('TCPDF', false)) {
153  define('PDF_PRODUCER', 'TCPDF 4.6.022 (http://www.tcpdf.org)');
154 
165  class TCPDF {
166 
167  // protected or Protected properties
168 
173  protected $page;
174 
179  protected $n;
180 
185  protected $offsets;
186 
191  protected $buffer;
192 
197  protected $pages = array();
198 
203  protected $state;
204 
209  protected $compress;
210 
215  protected $CurOrientation;
216 
221  protected $pagedim = array();
222 
227  protected $k;
228 
233  protected $fwPt;
234 
239  protected $fhPt;
240 
245  protected $wPt;
246 
251  protected $hPt;
252 
257  protected $w;
258 
263  protected $h;
264 
269  protected $lMargin;
270 
275  protected $tMargin;
276 
281  protected $rMargin;
282 
287  protected $bMargin;
288 
293  //protected
294  public $cMargin;
295 
300  protected $oldcMargin;
301 
306  protected $x;
307 
312  protected $y;
313 
318  protected $lasth;
319 
324  protected $LineWidth;
325 
330  protected $CoreFonts;
331 
336  protected $fonts = array();
337 
342  protected $FontFiles = array();
343 
348  protected $diffs = array();
349 
354  protected $images = array();
355 
360  protected $PageAnnots = array();
361 
366  protected $links = array();
367 
372  protected $FontFamily;
373 
378  protected $FontStyle;
379 
385  protected $FontAscent;
386 
392  protected $FontDescent;
393 
398  protected $underline;
399 
404  protected $CurrentFont;
405 
410  protected $FontSizePt;
411 
416  protected $FontSize;
417 
422  protected $DrawColor;
423 
428  protected $FillColor;
429 
434  protected $TextColor;
435 
440  protected $ColorFlag;
441 
446  protected $AutoPageBreak;
447 
452  protected $PageBreakTrigger;
453 
458  protected $InFooter = false;
459 
464  protected $ZoomMode;
465 
470  protected $LayoutMode;
471 
476  protected $title = '';
477 
482  protected $subject = '';
483 
488  protected $author = '';
489 
494  protected $keywords = '';
495 
500  protected $creator = '';
501 
506  protected $AliasNbPages = '{nb}';
507 
512  protected $AliasNumPage = '{pnb}';
513 
520  protected $img_rb_x;
521 
528  protected $img_rb_y;
529 
536  protected $imgscale = 1;
537 
544  protected $isunicode = false;
545 
551  protected $PDFVersion = '1.7';
552 
553 
554  // ----------------------
555 
560  protected $header_margin;
561 
566  protected $footer_margin;
567 
573  protected $original_lMargin;
574 
580  protected $original_rMargin;
581 
586  protected $header_font;
587 
592  protected $footer_font;
593 
598  protected $l;
599 
604  protected $barcode = false;
605 
610  protected $print_header = true;
611 
616  protected $print_footer = true;
617 
622  protected $header_logo = '';
623 
628  protected $header_logo_width = 30;
629 
634  protected $header_title = '';
635 
640  protected $header_string = '';
641 
646  protected $default_table_columns = 4;
647 
648 
649  // variables for html parser
650 
655  protected $HREF = array();
656 
661  protected $fontlist = array();
662 
667  protected $fgcolor;
668 
673  protected $listordered = array();
674 
679  protected $listcount = array();
680 
685  protected $listnum = 0;
686 
691  protected $listindent;
692 
697  protected $bgcolor;
698 
703  protected $tempfontsize = 10;
704 
709  protected $lispacer = '';
710 
716  protected $encoding = 'UTF-8';
717 
723  protected $internal_encoding;
724 
730  protected $rtl = false;
731 
737  protected $tmprtl = false;
738 
739  // --- Variables used for document encryption:
740 
746  protected $encrypted;
747 
753  protected $Uvalue;
754 
760  protected $Ovalue;
761 
767  protected $Pvalue;
768 
774  protected $enc_obj_id;
775 
781  protected $last_rc4_key;
782 
788  protected $last_rc4_key_c;
789 
794  protected $padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
795 
800  protected $encryption_key;
801 
802  // --- bookmark ---
803 
809  protected $outlines = array();
810 
816  protected $OutlineRoot;
817 
818 
819  // --- javascript and form ---
820 
826  protected $javascript = '';
827 
833  protected $n_js;
834 
840  protected $linethrough;
841 
842  // --- Variables used for User's Rights ---
843  // See PDF reference chapter 8.7 Digital Signatures
844 
850  protected $ur;
851 
857  protected $ur_document;
858 
864  protected $ur_annots;
865 
871  protected $ur_form;
872 
878  protected $ur_signature;
879 
885  protected $dpi = 72;
886 
892  protected $newpagegroup = array();
893 
899  protected $pagegroups;
900 
906  protected $currpagegroup;
907 
913  protected $visibility = 'all';
914 
920  protected $n_ocg_print;
921 
927  protected $n_ocg_view;
928 
934  protected $extgstates;
935 
941  protected $jpeg_quality;
942 
948  protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
949 
955  protected $viewer_preferences;
956 
962  protected $PageMode;
963 
969  protected $gradients = array();
970 
977  protected $intmrk = array();
978 
985  protected $cntmrk = array();
986 
992  protected $footerpos = array();
993 
994 
1000  protected $footerlen = array();
1001 
1007  protected $newline = true;
1008 
1014  protected $endlinex = 0;
1015 
1021  protected $linestyleWidth = '';
1022 
1028  protected $linestyleCap = '0 J';
1029 
1035  protected $linestyleJoin = '0 j';
1036 
1042  protected $linestyleDash = '[] 0 d';
1043 
1049  protected $openMarkedContent = false;
1050 
1056  protected $htmlvspace = 0;
1057 
1063  protected $spot_colors = array();
1064 
1070  protected $lisymbol = '';
1071 
1077  protected $epsmarker = 'x#!#EPS#!#x';
1078 
1084  protected $transfmatrix = array();
1085 
1091  protected $booklet = false;
1092 
1098  protected $feps = 0.005;
1099 
1105  protected $tagvspaces = array();
1106 
1113  protected $customlistindent = -1;
1114 
1120  protected $opencell = true;
1121 
1127  protected $embeddedfiles = array();
1128 
1134  protected $premode = false;
1135 
1142  protected $transfmrk = array();
1143 
1149  protected $htmlLinkColorArray = array(0, 0, 255);
1150 
1156  protected $htmlLinkFontStyle = 'U';
1157 
1163  protected $numpages = 0;
1164 
1170  protected $pagelen = array();
1171 
1177  protected $numimages = 0;
1178 
1184  protected $imagekeys = array();
1185 
1191  protected $bufferlen = 0;
1192 
1198  protected $diskcache = false;
1199 
1205  protected $numfonts = 0;
1206 
1212  protected $fontkeys = array();
1213 
1219  protected $pageopen = array();
1220 
1226  protected $default_monospaced_font = 'courier';
1227 
1233  protected $objcopy;
1234 
1240  protected $cache_file_lenght = array();
1241 
1247  protected $thead = '';
1248 
1254  protected $theadMargins = array();
1255 
1261  protected $cache_UTF8StringToArray = array();
1262 
1268  protected $cache_maxsize_UTF8StringToArray = 8;
1269 
1275  protected $cache_size_UTF8StringToArray = 0;
1276 
1282  protected $sign = false;
1283 
1289  protected $signature_data = array();
1290 
1296  protected $signature_max_lenght = 5120;
1297 
1303  protected $re_spaces = '/[\s]/';
1304 
1310  protected $sig_obj_id = 0;
1311 
1312  //------------------------------------------------------------
1313  // METHODS
1314  //------------------------------------------------------------
1315 
1329  public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1330  /* Set internal character encoding to ASCII */
1331  if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1332  $this->internal_encoding = mb_internal_encoding();
1333  mb_internal_encoding('ASCII');
1334  }
1335  // set disk caching
1336  $this->diskcache = $diskcache ? true : false;
1337  // set language direction
1338  $this->rtl = false;
1339  $this->tmprtl = false;
1340  //Some checks
1341  $this->_dochecks();
1342  //Initialization of properties
1343  $this->isunicode = $unicode;
1344  $this->page = 0;
1345  $this->transfmrk[0] = array();
1346  $this->pagedim = array();
1347  $this->n = 2;
1348  $this->buffer = '';
1349  $this->pages = array();
1350  $this->state = 0;
1351  $this->fonts = array();
1352  $this->FontFiles = array();
1353  $this->diffs = array();
1354  $this->images = array();
1355  $this->links = array();
1356  $this->gradients = array();
1357  $this->InFooter = false;
1358  $this->lasth = 0;
1359  $this->FontFamily = 'helvetica';
1360  $this->FontStyle = '';
1361  $this->FontSizePt = 12;
1362  $this->underline = false;
1363  $this->linethrough = false;
1364  $this->DrawColor = '0 G';
1365  $this->FillColor = '0 g';
1366  $this->TextColor = '0 g';
1367  $this->ColorFlag = false;
1368  // encryption values
1369  $this->encrypted = false;
1370  $this->last_rc4_key = '';
1371  $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
1372  //Standard Unicode fonts
1373  $this->CoreFonts = array(
1374  'courier'=>'Courier',
1375  'courierB'=>'Courier-Bold',
1376  'courierI'=>'Courier-Oblique',
1377  'courierBI'=>'Courier-BoldOblique',
1378  'helvetica'=>'Helvetica',
1379  'helveticaB'=>'Helvetica-Bold',
1380  'helveticaI'=>'Helvetica-Oblique',
1381  'helveticaBI'=>'Helvetica-BoldOblique',
1382  'times'=>'Times-Roman',
1383  'timesB'=>'Times-Bold',
1384  'timesI'=>'Times-Italic',
1385  'timesBI'=>'Times-BoldItalic',
1386  'symbol'=>'Symbol',
1387  'zapfdingbats'=>'ZapfDingbats'
1388  );
1389  //Set scale factor
1390  $this->setPageUnit($unit);
1391  // set page format and orientation
1392  $this->setPageFormat($format, $orientation);
1393  //Page margins (1 cm)
1394  $margin = 28.35 / $this->k;
1395  $this->SetMargins($margin, $margin);
1396  //Interior cell margin
1397  $this->cMargin = $margin / 10;
1398  //Line width (0.2 mm)
1399  $this->LineWidth = 0.57 / $this->k;
1400  $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1401  $this->linestyleCap = '0 J';
1402  $this->linestyleJoin = '0 j';
1403  $this->linestyleDash = '[] 0 d';
1404  //Automatic page break
1405  $this->SetAutoPageBreak(true, (2 * $margin));
1406  //Full width display mode
1407  $this->SetDisplayMode('fullwidth');
1408  //Compression
1409  $this->SetCompression(true);
1410  //Set default PDF version number
1411  $this->PDFVersion = '1.7';
1412  $this->encoding = $encoding;
1413  $this->HREF = array();
1414  $this->getFontsList();
1415  $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1416  $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1417  $this->extgstates = array();
1418  // user's rights
1419  $this->sign = false;
1420  $this->ur = false;
1421  $this->ur_document = '/FullSave';
1422  $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
1423  $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1424  $this->ur_signature = '/Modify';
1425  // set default JPEG quality
1426  $this->jpeg_quality = 75;
1427  // initialize some settings
1428  $this->utf8Bidi(array(''), '');
1429  // set default font
1430  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1431  // check if PCRE Unicode support is enabled
1432  if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1433  // PCRE unicode support is turned ON
1434  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1435  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1436  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1437  //$this->re_spaces = '/[\s\p{Z}\p{Lo}]/u';
1438  $this->re_spaces = '/[\s\p{Z}]/u';
1439  } else {
1440  // PCRE unicode support is turned OFF
1441  $this->re_spaces = '/[\s]/';
1442  }
1443  }
1444 
1450  public function __destruct() {
1451  // restore internal encoding
1452  if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1453  mb_internal_encoding($this->internal_encoding);
1454  }
1455  // unset all class variables
1456  $this->_destroy(true);
1457  }
1458 
1465  public function setPageUnit($unit) {
1466  //Set scale factor
1467  switch (strtolower($unit)) {
1468  // points
1469  case 'px':
1470  case 'pt': {
1471  $this->k = 1;
1472  break;
1473  }
1474  // millimeters
1475  case 'mm': {
1476  $this->k = $this->dpi / 25.4;
1477  break;
1478  }
1479  // centimeters
1480  case 'cm': {
1481  $this->k = $this->dpi / 2.54;
1482  break;
1483  }
1484  // inches
1485  case 'in': {
1486  $this->k = $this->dpi;
1487  break;
1488  }
1489  // unsupported unit
1490  default : {
1491  $this->Error('Incorrect unit: '.$unit);
1492  break;
1493  }
1494  }
1495  if (isset($this->CurOrientation)) {
1496  $this->setPageOrientation($this->CurOrientation);
1497  }
1498  }
1499 
1507  public function setPageFormat($format, $orientation='P') {
1508  //Page format
1509  if (is_string($format)) {
1510  // Page formats (45 standard ISO paper formats and 4 american common formats).
1511  // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1512  switch (strtoupper($format)) {
1513  case '4A0': {$format = array(4767.87,6740.79); break;}
1514  case '2A0': {$format = array(3370.39,4767.87); break;}
1515  case 'A0': {$format = array(2383.94,3370.39); break;}
1516  case 'A1': {$format = array(1683.78,2383.94); break;}
1517  case 'A2': {$format = array(1190.55,1683.78); break;}
1518  case 'A3': {$format = array(841.89,1190.55); break;}
1519  case 'A4': default: {$format = array(595.28,841.89); break;}
1520  case 'A5': {$format = array(419.53,595.28); break;}
1521  case 'A6': {$format = array(297.64,419.53); break;}
1522  case 'A7': {$format = array(209.76,297.64); break;}
1523  case 'A8': {$format = array(147.40,209.76); break;}
1524  case 'A9': {$format = array(104.88,147.40); break;}
1525  case 'A10': {$format = array(73.70,104.88); break;}
1526  case 'B0': {$format = array(2834.65,4008.19); break;}
1527  case 'B1': {$format = array(2004.09,2834.65); break;}
1528  case 'B2': {$format = array(1417.32,2004.09); break;}
1529  case 'B3': {$format = array(1000.63,1417.32); break;}
1530  case 'B4': {$format = array(708.66,1000.63); break;}
1531  case 'B5': {$format = array(498.90,708.66); break;}
1532  case 'B6': {$format = array(354.33,498.90); break;}
1533  case 'B7': {$format = array(249.45,354.33); break;}
1534  case 'B8': {$format = array(175.75,249.45); break;}
1535  case 'B9': {$format = array(124.72,175.75); break;}
1536  case 'B10': {$format = array(87.87,124.72); break;}
1537  case 'C0': {$format = array(2599.37,3676.54); break;}
1538  case 'C1': {$format = array(1836.85,2599.37); break;}
1539  case 'C2': {$format = array(1298.27,1836.85); break;}
1540  case 'C3': {$format = array(918.43,1298.27); break;}
1541  case 'C4': {$format = array(649.13,918.43); break;}
1542  case 'C5': {$format = array(459.21,649.13); break;}
1543  case 'C6': {$format = array(323.15,459.21); break;}
1544  case 'C7': {$format = array(229.61,323.15); break;}
1545  case 'C8': {$format = array(161.57,229.61); break;}
1546  case 'C9': {$format = array(113.39,161.57); break;}
1547  case 'C10': {$format = array(79.37,113.39); break;}
1548  case 'RA0': {$format = array(2437.80,3458.27); break;}
1549  case 'RA1': {$format = array(1729.13,2437.80); break;}
1550  case 'RA2': {$format = array(1218.90,1729.13); break;}
1551  case 'RA3': {$format = array(864.57,1218.90); break;}
1552  case 'RA4': {$format = array(609.45,864.57); break;}
1553  case 'SRA0': {$format = array(2551.18,3628.35); break;}
1554  case 'SRA1': {$format = array(1814.17,2551.18); break;}
1555  case 'SRA2': {$format = array(1275.59,1814.17); break;}
1556  case 'SRA3': {$format = array(907.09,1275.59); break;}
1557  case 'SRA4': {$format = array(637.80,907.09); break;}
1558  case 'LETTER': {$format = array(612.00,792.00); break;}
1559  case 'LEGAL': {$format = array(612.00,1008.00); break;}
1560  case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1561  case 'FOLIO': {$format = array(612.00,936.00); break;}
1562  }
1563  $this->fwPt = $format[0];
1564  $this->fhPt = $format[1];
1565  } else {
1566  $this->fwPt = $format[0] * $this->k;
1567  $this->fhPt = $format[1] * $this->k;
1568  }
1569  $this->setPageOrientation($orientation);
1570  }
1571 
1580  public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1581  $orientation = strtoupper($orientation);
1582  if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1583  $this->CurOrientation = 'P';
1584  $this->wPt = $this->fwPt;
1585  $this->hPt = $this->fhPt;
1586  } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1587  $this->CurOrientation = 'L';
1588  $this->wPt = $this->fhPt;
1589  $this->hPt = $this->fwPt;
1590  } else {
1591  $this->Error('Incorrect orientation: '.$orientation);
1592  }
1593  $this->w = $this->wPt / $this->k;
1594  $this->h = $this->hPt / $this->k;
1595  if ($this->empty_string($autopagebreak)) {
1596  if (isset($this->AutoPageBreak)) {
1597  $autopagebreak = $this->AutoPageBreak;
1598  } else {
1599  $autopagebreak = true;
1600  }
1601  }
1602  if ($this->empty_string($bottommargin)) {
1603  if (isset($this->bMargin)) {
1604  $bottommargin = $this->bMargin;
1605  } else {
1606  // default value = 2 cm
1607  $bottommargin = 2 * 28.35 / $this->k;
1608  }
1609  }
1610  $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1611  // store page dimensions
1612  $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
1613  }
1614 
1621  public function setSpacesRE($re='/[\s]/') {
1622  // if PCRE unicode support is turned ON:
1623  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1624  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1625  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1626  $this->re_spaces = $re;
1627  }
1628 
1636  public function setRTL($enable, $resetx=true) {
1637  $enable = $enable ? true : false;
1638  $resetx = ($resetx AND ($enable != $this->rtl));
1639  $this->rtl = $enable;
1640  $this->tmprtl = false;
1641  if ($resetx) {
1642  $this->Ln(0);
1643  }
1644  }
1645 
1652  public function getRTL() {
1653  return $this->rtl;
1654  }
1655 
1662  public function setTempRTL($mode) {
1663  switch ($mode) {
1664  case false:
1665  case 'L':
1666  case 'R': {
1667  $this->tmprtl = $mode;
1668  }
1669  }
1670  }
1671 
1679  public function setLastH($h) {
1680  $this->lasth = $h;
1681  }
1682 
1689  public function getLastH() {
1690  return $this->lasth;
1691  }
1692 
1700  public function setImageScale($scale) {
1701  $this->imgscale = $scale;
1702  }
1703 
1711  public function getImageScale() {
1712  return $this->imgscale;
1713  }
1714 
1724  public function getPageDimensions($pagenum='') {
1725  if (empty($pagenum)) {
1726  $pagenum = $this->page;
1727  }
1728  return $this->pagedim[$pagenum];
1729  }
1730 
1740  public function getPageWidth($pagenum='') {
1741  if (empty($pagenum)) {
1742  return $this->w;
1743  }
1744  return $this->pagedim[$pagenum]['w'];
1745  }
1746 
1756  public function getPageHeight($pagenum='') {
1757  if (empty($pagenum)) {
1758  return $this->h;
1759  }
1760  return $this->pagedim[$pagenum]['h'];
1761  }
1762 
1772  public function getBreakMargin($pagenum='') {
1773  if (empty($pagenum)) {
1774  return $this->bMargin;
1775  }
1776  return $this->pagedim[$pagenum]['bm'];
1777  }
1778 
1786  public function getScaleFactor() {
1787  return $this->k;
1788  }
1789 
1799  public function SetMargins($left, $top, $right=-1) {
1800  //Set left, top and right margins
1801  $this->lMargin = $left;
1802  $this->tMargin = $top;
1803  if ($right == -1) {
1804  $right = $left;
1805  }
1806  $this->rMargin = $right;
1807  }
1808 
1816  public function SetLeftMargin($margin) {
1817  //Set left margin
1818  $this->lMargin=$margin;
1819  if (($this->page > 0) AND ($this->x < $margin)) {
1820  $this->x = $margin;
1821  }
1822  }
1823 
1831  public function SetTopMargin($margin) {
1832  //Set top margin
1833  $this->tMargin=$margin;
1834  if (($this->page > 0) AND ($this->y < $margin)) {
1835  $this->y = $margin;
1836  }
1837  }
1838 
1846  public function SetRightMargin($margin) {
1847  $this->rMargin=$margin;
1848  if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1849  $this->x = $this->w - $margin;
1850  }
1851  }
1852 
1860  public function SetCellPadding($pad) {
1861  $this->cMargin = $pad;
1862  }
1863 
1872  public function SetAutoPageBreak($auto, $margin=0) {
1873  //Set auto page break mode and triggering margin
1874  $this->AutoPageBreak = $auto;
1875  $this->bMargin = $margin;
1876  $this->PageBreakTrigger = $this->h - $margin;
1877  }
1878 
1887  public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
1888  //Set display mode in viewer
1889  if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1890  $this->ZoomMode = $zoom;
1891  } else {
1892  $this->Error('Incorrect zoom display mode: '.$zoom);
1893  }
1894  switch ($layout) {
1895  case 'default':
1896  case 'single':
1897  case 'SinglePage': {
1898  $this->LayoutMode = 'SinglePage';
1899  break;
1900  }
1901  case 'continuous':
1902  case 'OneColumn': {
1903  $this->LayoutMode = 'OneColumn';
1904  break;
1905  }
1906  case 'two':
1907  case 'TwoColumnLeft': {
1908  $this->LayoutMode = 'TwoColumnLeft';
1909  break;
1910  }
1911  case 'TwoColumnRight': {
1912  $this->LayoutMode = 'TwoColumnRight';
1913  break;
1914  }
1915  case 'TwoPageLeft': {
1916  $this->LayoutMode = 'TwoPageLeft';
1917  break;
1918  }
1919  case 'TwoPageRight': {
1920  $this->LayoutMode = 'TwoPageRight';
1921  break;
1922  }
1923  default: {
1924  $this->LayoutMode = 'SinglePage';
1925  }
1926  }
1927  switch ($mode) {
1928  case 'UseNone': {
1929  $this->PageMode = 'UseNone';
1930  break;
1931  }
1932  case 'UseOutlines': {
1933  $this->PageMode = 'UseOutlines';
1934  break;
1935  }
1936  case 'UseThumbs': {
1937  $this->PageMode = 'UseThumbs';
1938  break;
1939  }
1940  case 'FullScreen': {
1941  $this->PageMode = 'FullScreen';
1942  break;
1943  }
1944  case 'UseOC': {
1945  $this->PageMode = 'UseOC';
1946  break;
1947  }
1948  case '': {
1949  $this->PageMode = 'UseAttachments';
1950  break;
1951  }
1952  default: {
1953  $this->PageMode = 'UseNone';
1954  }
1955  }
1956  }
1957 
1965  public function SetCompression($compress) {
1966  //Set page compression
1967  if (function_exists('gzcompress')) {
1968  $this->compress = $compress;
1969  } else {
1970  $this->compress = false;
1971  }
1972  }
1973 
1981  public function SetTitle($title) {
1982  //Title of document
1983  $this->title = $title;
1984  }
1985 
1993  public function SetSubject($subject) {
1994  //Subject of document
1995  $this->subject = $subject;
1996  }
1997 
2005  public function SetAuthor($author) {
2006  //Author of document
2007  $this->author = $author;
2008  }
2009 
2017  public function SetKeywords($keywords) {
2018  //Keywords of document
2019  $this->keywords = $keywords;
2020  }
2021 
2029  public function SetCreator($creator) {
2030  //Creator of document
2031  $this->creator = $creator;
2032  }
2033 
2041  public function Error($msg) {
2042  // unset all class variables
2043  $this->_destroy(true);
2044  // exit program and print error
2045  die('<strong>TCPDF ERROR: </strong>'.$msg);
2046  }
2047 
2056  public function Open() {
2057  //Begin document
2058  $this->state = 1;
2059  }
2060 
2069  public function Close() {
2070  if ($this->state == 3) {
2071  return;
2072  }
2073  if ($this->page == 0) {
2074  $this->AddPage();
2075  }
2076  // close page
2077  $this->endPage();
2078  // close document
2079  $this->_enddoc();
2080  // unset all class variables (except critical ones)
2081  $this->_destroy(false);
2082  }
2083 
2092  public function setPage($pnum, $resetmargins=false) {
2093  if ($pnum == $this->page) {
2094  return;
2095  }
2096  if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2097  $this->state = 2;
2098  // save current graphic settings
2099  //$gvars = $this->getGraphicVars();
2100  $oldpage = $this->page;
2101  $this->page = $pnum;
2102  $this->wPt = $this->pagedim[$this->page]['w'];
2103  $this->hPt = $this->pagedim[$this->page]['h'];
2104  $this->w = $this->wPt / $this->k;
2105  $this->h = $this->hPt / $this->k;
2106  $this->tMargin = $this->pagedim[$this->page]['tm'];
2107  $this->bMargin = $this->pagedim[$this->page]['bm'];
2108  $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2109  $this->original_rMargin = $this->pagedim[$this->page]['orm'];
2110  $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
2111  $this->CurOrientation = $this->pagedim[$this->page]['or'];
2112  $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
2113  // restore graphic settings
2114  //$this->setGraphicVars($gvars);
2115  if ($resetmargins) {
2116  $this->lMargin = $this->pagedim[$this->page]['olm'];
2117  $this->rMargin = $this->pagedim[$this->page]['orm'];
2118  $this->SetY($this->tMargin);
2119  } else {
2120  // account for booklet mode
2121  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
2122  $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
2123  $this->lMargin += $deltam;
2124  $this->rMargin -= $deltam;
2125  }
2126  }
2127  } else {
2128  $this->Error('Wrong page number on setPage() function.');
2129  }
2130  }
2131 
2139  public function lastPage($resetmargins=false) {
2140  $this->setPage($this->getNumPages(), $resetmargins);
2141  }
2142 
2150  public function getPage() {
2151  return $this->page;
2152  }
2153 
2154 
2162  public function getNumPages() {
2163  return $this->numpages;
2164  }
2165 
2175  public function AddPage($orientation='', $format='') {
2176  if (!isset($this->original_lMargin)) {
2177  $this->original_lMargin = $this->lMargin;
2178  }
2179  if (!isset($this->original_rMargin)) {
2180  $this->original_rMargin = $this->rMargin;
2181  }
2182  // terminate previous page
2183  $this->endPage();
2184  // start new page
2185  $this->startPage($orientation, $format);
2186  }
2187 
2194  protected function endPage() {
2195  // check if page is already closed
2196  if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
2197  return;
2198  }
2199  $this->InFooter = true;
2200  // print page footer
2201  $this->setFooter();
2202  // close page
2203  $this->_endpage();
2204  // mark page as closed
2205  $this->pageopen[$this->page] = false;
2206  $this->InFooter = false;
2207  }
2208 
2218  protected function startPage($orientation='', $format='') {
2219  if ($this->numpages > $this->page) {
2220  // this page has been already added
2221  $this->setPage($this->page + 1);
2222  $this->SetY($this->tMargin);
2223  return;
2224  }
2225  // start a new page
2226  if ($this->state == 0) {
2227  $this->Open();
2228  }
2229  ++$this->numpages;
2230  $this->swapMargins($this->booklet);
2231  // save current graphic settings
2232  $gvars = $this->getGraphicVars();
2233  // start new page
2234  $this->_beginpage($orientation, $format);
2235  // mark page as open
2236  $this->pageopen[$this->page] = true;
2237  // restore graphic settings
2238  $this->setGraphicVars($gvars);
2239  // mark this point
2240  $this->setPageMark();
2241  // print page header
2242  $this->setHeader();
2243  // restore graphic settings
2244  $this->setGraphicVars($gvars);
2245  // mark this point
2246  $this->setPageMark();
2247  // print table header (if any)
2248  $this->setTableHeader();
2249  }
2250 
2258  public function setPageMark() {
2259  $this->intmrk[$this->page] = $this->pagelen[$this->page];
2260  $this->setContentMark();
2261  }
2262 
2269  protected function setContentMark($page=0) {
2270  if ($page <= 0) {
2271  $page = $this->page;
2272  }
2273  if (isset($this->footerlen[$page])) {
2274  $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
2275  } else {
2276  $this->cntmrk[$page] = $this->pagelen[$page];
2277  }
2278  }
2279 
2288  public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
2289  $this->header_logo = $ln;
2290  $this->header_logo_width = $lw;
2291  $this->header_title = $ht;
2292  $this->header_string = $hs;
2293  }
2294 
2302  public function getHeaderData() {
2303  $ret = array();
2304  $ret['logo'] = $this->header_logo;
2305  $ret['logo_width'] = $this->header_logo_width;
2306  $ret['title'] = $this->header_title;
2307  $ret['string'] = $this->header_string;
2308  return $ret;
2309  }
2310 
2317  public function setHeaderMargin($hm=10) {
2318  $this->header_margin = $hm;
2319  }
2320 
2327  public function getHeaderMargin() {
2328  return $this->header_margin;
2329  }
2330 
2337  public function setFooterMargin($fm=10) {
2338  $this->footer_margin = $fm;
2339  }
2340 
2347  public function getFooterMargin() {
2348  return $this->footer_margin;
2349  }
2355  public function setPrintHeader($val=true) {
2356  $this->print_header = $val;
2357  }
2358 
2364  public function setPrintFooter($val=true) {
2365  $this->print_footer = $val;
2366  }
2367 
2373  public function getImageRBX() {
2374  return $this->img_rb_x;
2375  }
2376 
2382  public function getImageRBY() {
2383  return $this->img_rb_y;
2384  }
2385 
2391  public function Header() {
2392  $ormargins = $this->getOriginalMargins();
2393  $headerfont = $this->getHeaderFont();
2394  $headerdata = $this->getHeaderData();
2395  if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2396  $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2397  $imgy = $this->getImageRBY();
2398  } else {
2399  $imgy = $this->GetY();
2400  }
2401  $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2402  // set starting margin for text data cell
2403  if ($this->getRTL()) {
2404  $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2405  } else {
2406  $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2407  }
2408  $this->SetTextColor(0, 0, 0);
2409  // header title
2410  $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2411  $this->SetX($header_x);
2412  $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
2413  // header string
2414  $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2415  $this->SetX($header_x);
2416  $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
2417  // print an ending header line
2418  $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2419  $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
2420  if ($this->getRTL()) {
2421  $this->SetX($ormargins['right']);
2422  } else {
2423  $this->SetX($ormargins['left']);
2424  }
2425  $this->Cell(0, 0, '', 'T', 0, 'C');
2426  }
2427 
2433  public function Footer() {
2434  $cur_y = $this->GetY();
2435  $ormargins = $this->getOriginalMargins();
2436  $this->SetTextColor(0, 0, 0);
2437  //set style for cell border
2438  $line_width = 0.85 / $this->getScaleFactor();
2439  $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2440  //print document barcode
2441  $barcode = $this->getBarcode();
2442  if (!empty($barcode)) {
2443  $this->Ln($line_width);
2444  $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2445  $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2446  }
2447  if (empty($this->pagegroups)) {
2448  $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
2449  } else {
2450  $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
2451  }
2452  $this->SetY($cur_y);
2453  //Print page number
2454  if ($this->getRTL()) {
2455  $this->SetX($ormargins['right']);
2456  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2457  } else {
2458  $this->SetX($ormargins['left']);
2459  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2460  }
2461  }
2462 
2468  protected function setHeader() {
2469  if ($this->print_header) {
2470  $lasth = $this->lasth;
2471  $this->_out('q');
2472  $this->rMargin = $this->original_rMargin;
2473  $this->lMargin = $this->original_lMargin;
2474  $this->cMargin = 0;
2475  //set current position
2476  if ($this->rtl) {
2477  $this->SetXY($this->original_rMargin, $this->header_margin);
2478  } else {
2479  $this->SetXY($this->original_lMargin, $this->header_margin);
2480  }
2481  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2482  $this->Header();
2483  //restore position
2484  if ($this->rtl) {
2485  $this->SetXY($this->original_rMargin, $this->tMargin);
2486  } else {
2487  $this->SetXY($this->original_lMargin, $this->tMargin);
2488  }
2489  $this->_out('Q');
2490  $this->lasth = $lasth;
2491  }
2492  }
2493 
2499  protected function setFooter() {
2500  //Page footer
2501  // save current graphic settings
2502  $gvars = $this->getGraphicVars();
2503  // mark this point
2504  $this->footerpos[$this->page] = $this->pagelen[$this->page];
2505  $this->_out("\n");
2506  if ($this->print_footer) {
2507  $lasth = $this->lasth;
2508  $this->_out('q');
2509  $this->rMargin = $this->original_rMargin;
2510  $this->lMargin = $this->original_lMargin;
2511  $this->cMargin = 0;
2512  //set current position
2513  $footer_y = $this->h - $this->footer_margin;
2514  if ($this->rtl) {
2515  $this->SetXY($this->original_rMargin, $footer_y);
2516  } else {
2517  $this->SetXY($this->original_lMargin, $footer_y);
2518  }
2519  $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
2520  $this->Footer();
2521  //restore position
2522  if ($this->rtl) {
2523  $this->SetXY($this->original_rMargin, $this->tMargin);
2524  } else {
2525  $this->SetXY($this->original_lMargin, $this->tMargin);
2526  }
2527  $this->_out('Q');
2528  $this->lasth = $lasth;
2529  }
2530  // restore graphic settings
2531  $this->setGraphicVars($gvars);
2532  // calculate footer lenght
2533  $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
2534  }
2535 
2541  protected function setTableHeader() {
2542  if (isset($this->theadMargins['top'])) {
2543  // restore the original top-margin
2544  $this->tMargin = $this->theadMargins['top'];
2545  $this->pagedim[$this->page]['tm'] = $this->tMargin;
2546  $this->y = $this->tMargin;
2547  }
2548  if (!$this->empty_string($this->thead)) {
2549  // set margins
2550  $prev_lMargin = $this->lMargin;
2551  $prev_rMargin = $this->rMargin;
2552  $this->lMargin = $this->pagedim[$this->page]['olm'];
2553  $this->rMargin = $this->pagedim[$this->page]['orm'];
2554  $this->cMargin = $this->theadMargins['cmargin'];
2555  // print table header
2556  $this->writeHTML($this->thead, false, false, false, false, '');
2557  // set new top margin to skip the table headers
2558  if (!isset($this->theadMargins['top'])) {
2559  $this->theadMargins['top'] = $this->tMargin;
2560  }
2561  $this->tMargin = $this->y;
2562  $this->pagedim[$this->page]['tm'] = $this->tMargin;
2563  $this->lasth = 0;
2564  $this->lMargin = $prev_lMargin;
2565  $this->rMargin = $prev_rMargin;
2566  }
2567  }
2568 
2576  public function PageNo() {
2577  return $this->page;
2578  }
2579 
2592  public function AddSpotColor($name, $c, $m, $y, $k) {
2593  if (!isset($this->spot_colors[$name])) {
2594  $i = 1 + count($this->spot_colors);
2595  $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2596  }
2597  }
2598 
2608  public function SetDrawColorArray($color) {
2609  if (isset($color)) {
2610  $color = array_values($color);
2611  $r = isset($color[0]) ? $color[0] : -1;
2612  $g = isset($color[1]) ? $color[1] : -1;
2613  $b = isset($color[2]) ? $color[2] : -1;
2614  $k = isset($color[3]) ? $color[3] : -1;
2615  if ($r >= 0) {
2616  $this->SetDrawColor($r, $g, $b, $k);
2617  }
2618  }
2619  }
2620 
2631  public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2632  // set default values
2633  if (!is_numeric($col1)) {
2634  $col1 = 0;
2635  }
2636  if (!is_numeric($col2)) {
2637  $col2 = -1;
2638  }
2639  if (!is_numeric($col3)) {
2640  $col3 = -1;
2641  }
2642  if (!is_numeric($col4)) {
2643  $col4 = -1;
2644  }
2645  //Set color for all stroking operations
2646  if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2647  // Grey scale
2648  $this->DrawColor = sprintf('%.3F G', $col1/255);
2649  } elseif ($col4 == -1) {
2650  // RGB
2651  $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
2652  } else {
2653  // CMYK
2654  $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
2655  }
2656  if ($this->page > 0) {
2657  $this->_out($this->DrawColor);
2658  }
2659  }
2660 
2669  public function SetDrawSpotColor($name, $tint=100) {
2670  if (!isset($this->spot_colors[$name])) {
2671  $this->Error('Undefined spot color: '.$name);
2672  }
2673  $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
2674  if ($this->page > 0) {
2675  $this->_out($this->DrawColor);
2676  }
2677  }
2678 
2688  public function SetFillColorArray($color) {
2689  if (isset($color)) {
2690  $color = array_values($color);
2691  $r = isset($color[0]) ? $color[0] : -1;
2692  $g = isset($color[1]) ? $color[1] : -1;
2693  $b = isset($color[2]) ? $color[2] : -1;
2694  $k = isset($color[3]) ? $color[3] : -1;
2695  if ($r >= 0) {
2696  $this->SetFillColor($r, $g, $b, $k);
2697  }
2698  }
2699  }
2700 
2711  public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2712  // set default values
2713  if (!is_numeric($col1)) {
2714  $col1 = 0;
2715  }
2716  if (!is_numeric($col2)) {
2717  $col2 = -1;
2718  }
2719  if (!is_numeric($col3)) {
2720  $col3 = -1;
2721  }
2722  if (!is_numeric($col4)) {
2723  $col4 = -1;
2724  }
2725  //Set color for all filling operations
2726  if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2727  // Grey scale
2728  $this->FillColor = sprintf('%.3F g', $col1/255);
2729  $this->bgcolor = array('G' => $col1);
2730  } elseif ($col4 == -1) {
2731  // RGB
2732  $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2733  $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2734  } else {
2735  // CMYK
2736  $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2737  $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2738  }
2739  $this->ColorFlag = ($this->FillColor != $this->TextColor);
2740  if ($this->page > 0) {
2741  $this->_out($this->FillColor);
2742  }
2743  }
2744 
2753  public function SetFillSpotColor($name, $tint=100) {
2754  if (!isset($this->spot_colors[$name])) {
2755  $this->Error('Undefined spot color: '.$name);
2756  }
2757  $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2758  $this->ColorFlag = ($this->FillColor != $this->TextColor);
2759  if ($this->page > 0) {
2760  $this->_out($this->FillColor);
2761  }
2762  }
2763 
2772  public function SetTextColorArray($color) {
2773  if (isset($color)) {
2774  $color = array_values($color);
2775  $r = isset($color[0]) ? $color[0] : -1;
2776  $g = isset($color[1]) ? $color[1] : -1;
2777  $b = isset($color[2]) ? $color[2] : -1;
2778  $k = isset($color[3]) ? $color[3] : -1;
2779  if ($r >= 0) {
2780  $this->SetTextColor($r, $g, $b, $k);
2781  }
2782  }
2783  }
2784 
2795  public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2796  // set default values
2797  if (!is_numeric($col1)) {
2798  $col1 = 0;
2799  }
2800  if (!is_numeric($col2)) {
2801  $col2 = -1;
2802  }
2803  if (!is_numeric($col3)) {
2804  $col3 = -1;
2805  }
2806  if (!is_numeric($col4)) {
2807  $col4 = -1;
2808  }
2809  //Set color for text
2810  if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2811  // Grey scale
2812  $this->TextColor = sprintf('%.3F g', $col1/255);
2813  $this->fgcolor = array('G' => $col1);
2814  } elseif ($col4 == -1) {
2815  // RGB
2816  $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2817  $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2818  } else {
2819  // CMYK
2820  $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2821  $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2822  }
2823  $this->ColorFlag = ($this->FillColor != $this->TextColor);
2824  }
2825 
2834  public function SetTextSpotColor($name, $tint=100) {
2835  if (!isset($this->spot_colors[$name])) {
2836  $this->Error('Undefined spot color: '.$name);
2837  }
2838  $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2839  $this->ColorFlag = ($this->FillColor != $this->TextColor);
2840  if ($this->page > 0) {
2841  $this->_out($this->TextColor);
2842  }
2843  }
2844 
2856  public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2857  return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
2858  }
2859 
2871  public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2872  // store current values
2873  if (!$this->empty_string($fontname)) {
2874  $prev_FontFamily = $this->FontFamily;
2875  $prev_FontStyle = $this->FontStyle;
2876  $prev_FontSizePt = $this->FontSizePt;
2877  $this->SetFont($fontname, $fontstyle, $fontsize);
2878  }
2879  $w = 0;
2880  foreach ($sa as $char) {
2881  $w += $this->GetCharWidth($char);
2882  }
2883  // restore previous values
2884  if (!$this->empty_string($fontname)) {
2885  $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2886  }
2887  return $w;
2888  }
2889 
2898  public function GetCharWidth($char) {
2899  if ($char == 173) {
2900  // SHY character will not be printed
2901  return (0);
2902  }
2903  $cw = &$this->CurrentFont['cw'];
2904  if (isset($cw[$char])) {
2905  $w = $cw[$char];
2906  } elseif (isset($this->CurrentFont['dw'])) {
2907  // default width
2908  $w = $this->CurrentFont['dw'];
2909  } elseif (isset($cw[32])) {
2910  // default width
2911  $dw = $cw[32];
2912  } else {
2913  $w = 600;
2914  }
2915  return ($w * $this->FontSize / 1000);
2916  }
2917 
2925  public function GetNumChars($s) {
2926  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
2927  return count($this->UTF8StringToArray($s));
2928  }
2929  return strlen($s);
2930  }
2931 
2937  protected function getFontsList() {
2938  $fontsdir = opendir($this->_getfontpath());
2939  while (($file = readdir($fontsdir)) !== false) {
2940  if (substr($file, -4) == '.php') {
2941  array_push($this->fontlist, strtolower(basename($file, '.php')));
2942  }
2943  }
2944  closedir($fontsdir);
2945  }
2946 
2959  public function AddFont($family, $style='', $fontfile='') {
2960  if ($this->empty_string($family)) {
2961  if (!$this->empty_string($this->FontFamily)) {
2962  $family = $this->FontFamily;
2963  } else {
2964  $this->Error('Empty font family');
2965  }
2966  }
2967  $family = strtolower($family);
2968  if ((!$this->isunicode) AND ($family == 'arial')) {
2969  $family = 'helvetica';
2970  }
2971  if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
2972  $style = '';
2973  }
2974  $tempstyle = strtoupper($style);
2975  $style = '';
2976  // underline
2977  if (strpos($tempstyle, 'U') !== false) {
2978  $this->underline = true;
2979  } else {
2980  $this->underline = false;
2981  }
2982  // line through (deleted)
2983  if (strpos($tempstyle, 'D') !== false) {
2984  $this->linethrough = true;
2985  } else {
2986  $this->linethrough = false;
2987  }
2988  // bold
2989  if (strpos($tempstyle, 'B') !== false) {
2990  $style .= 'B';
2991  }
2992  // oblique
2993  if (strpos($tempstyle, 'I') !== false) {
2994  $style .= 'I';
2995  }
2996  $bistyle = $style;
2997  $fontkey = $family.$style;
2998  $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
2999  $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
3000  // check if the font has been already added
3001  if ($this->getFontBuffer($fontkey) !== false) {
3002  return $fontdata;
3003  }
3004  if (isset($type)) {
3005  unset($type);
3006  }
3007  if (isset($cw)) {
3008  unset($cw);
3009  }
3010  // get specified font directory (if any)
3011  $fontdir = '';
3012  if (!$this->empty_string($fontfile)) {
3013  $fontdir = dirname($fontfile);
3014  if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
3015  $fontdir = '';
3016  } else {
3017  $fontdir .= '/';
3018  }
3019  }
3020  // search and include font file
3021  if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
3022  // build a standard filenames for specified font
3023  $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
3024  $fontfile2 = str_replace(' ', '', $family).'.php';
3025  // search files on various directories
3026  if (file_exists($fontdir.$fontfile1)) {
3027  $fontfile = $fontdir.$fontfile1;
3028  } elseif (file_exists($this->_getfontpath().$fontfile1)) {
3029  $fontfile = $this->_getfontpath().$fontfile1;
3030  } elseif (file_exists($fontfile1)) {
3031  $fontfile = $fontfile1;
3032  } elseif (file_exists($fontdir.$fontfile2)) {
3033  $fontfile = $fontdir.$fontfile2;
3034  } elseif (file_exists($this->_getfontpath().$fontfile2)) {
3035  $fontfile = $this->_getfontpath().$fontfile2;
3036  } else {
3037  $fontfile = $fontfile2;
3038  }
3039  }
3040  // include font file
3041  if (file_exists($fontfile)) {
3042  include($fontfile);
3043  } else {
3044  $this->Error('Could not include font definition file: '.$family.'');
3045  }
3046  // check font parameters
3047  if ((!isset($type)) OR (!isset($cw))) {
3048  $this->Error('The font definition file has a bad format: '.$fontfile.'');
3049  }
3050  if (!isset($file)) {
3051  $file = '';
3052  }
3053  if (!isset($enc)) {
3054  $enc = '';
3055  }
3056  if (!isset($dw) OR $this->empty_string($dw)) {
3057  // set default width
3058  if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
3059  $dw = $desc['MissingWidth'];
3060  } elseif (isset($cw[32])) {
3061  $dw = $cw[32];
3062  } else {
3063  $dw = 600;
3064  }
3065  }
3066  ++$this->numfonts;
3067  // register CID font (all styles at once)
3068  if ($type == 'cidfont0') {
3069  $file = ''; // not embedded
3070  $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
3071  $sname = $name.$styles[$bistyle];
3072  if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
3073  $desc['StemV'] = 120;
3074  }
3075  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc));
3076  } elseif ($type == 'core') {
3077  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw));
3078  } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
3079  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'file' => $file, 'enc' => $enc, 'desc' => $desc));
3080  } elseif ($type == 'TrueTypeUnicode') {
3081  $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg));
3082  } else {
3083  $this->Error('Unknow font type: '.$type.'');
3084  }
3085  if (isset($diff) AND (!empty($diff))) {
3086  //Search existing encodings
3087  $d = 0;
3088  $nb = count($this->diffs);
3089  for ($i=1; $i <= $nb; ++$i) {
3090  if ($this->diffs[$i] == $diff) {
3091  $d = $i;
3092  break;
3093  }
3094  }
3095  if ($d == 0) {
3096  $d = $nb + 1;
3097  $this->diffs[$d] = $diff;
3098  }
3099  $this->setFontSubBuffer($fontkey, 'diff', $d);
3100  }
3101  if (!$this->empty_string($file)) {
3102  if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
3103  $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
3104  } elseif ($type != 'core') {
3105  $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
3106  }
3107  }
3108  return $fontdata;
3109  }
3110 
3125  public function SetFont($family, $style='', $size=0, $fontfile='') {
3126  //Select a font; size given in points
3127  if ($size == 0) {
3128  $size = $this->FontSizePt;
3129  }
3130  // try to add font (if not already added)
3131  $fontdata = $this->AddFont($family, $style, $fontfile);
3132  $this->FontFamily = $fontdata['family'];
3133  $this->FontStyle = $fontdata['style'];
3134  $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
3135  $this->SetFontSize($size);
3136  }
3137 
3145  public function SetFontSize($size) {
3146  //Set font size in points
3147  $this->FontSizePt = $size;
3148  $this->FontSize = $size / $this->k;
3149  if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
3150  $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
3151  } else {
3152  $this->FontAscent = 0.8 * $this->FontSize;
3153  }
3154  if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
3155  $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
3156  } else {
3157  $this->FontDescent = 0.2 * $this->FontSize;
3158  }
3159  if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
3160  $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
3161  }
3162  }
3163 
3170  public function SetDefaultMonospacedFont($font) {
3171  $this->default_monospaced_font = $font;
3172  }
3173 
3181  public function AddLink() {
3182  //Create a new internal link
3183  $n = count($this->links) + 1;
3184  $this->links[$n] = array(0, 0);
3185  return $n;
3186  }
3187 
3197  public function SetLink($link, $y=0, $page=-1) {
3198  if ($y == -1) {
3199  $y = $this->y;
3200  }
3201  if ($page == -1) {
3202  $page = $this->page;
3203  }
3204  $this->links[$link] = array($page, $y);
3205  }
3206 
3220  public function Link($x, $y, $w, $h, $link, $spaces=0) {
3221  $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
3222  }
3223 
3237  public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
3238  // recalculate coordinates to account for graphic transformations
3239  if (isset($this->transfmatrix)) {
3240  $maxid = count($this->transfmatrix) - 1;
3241  for ($i=$maxid; $i >= 0; $i--) {
3242  $ctm = $this->transfmatrix[$i];
3243  if (isset($ctm['a'])) {
3244  $x = $x * $this->k;
3245  $y = ($this->h - $y) * $this->k;
3246  $w = $w * $this->k;
3247  $h = $h * $this->k;
3248  // top left
3249  $xt = $x;
3250  $yt = $y;
3251  $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3252  $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3253  // top right
3254  $xt = $x + $w;
3255  $yt = $y;
3256  $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3257  $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3258  // bottom left
3259  $xt = $x;
3260  $yt = $y - $h;
3261  $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3262  $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3263  // bottom right
3264  $xt = $x + $w;
3265  $yt = $y - $h;
3266  $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3267  $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3268  // new coordinates (rectangle area)
3269  $x = min($x1, $x2, $x3, $x4);
3270  $y = max($y1, $y2, $y3, $y4);
3271  $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
3272  $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
3273  $x = $x / $this->k;
3274  $y = $this->h - ($y / $this->k);
3275  }
3276  }
3277  }
3278  if ($this->page <= 0) {
3279  $page = 1;
3280  } else {
3281  $page = $this->page;
3282  }
3283  $this->PageAnnots[$page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
3284  if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
3285  $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + 100000));
3286  }
3287  }
3288 
3295  protected function _putEmbeddedFiles() {
3296  reset($this->embeddedfiles);
3297  foreach ($this->embeddedfiles as $filename => $filedata) {
3298  $data = file_get_contents($filedata['file']);
3299  $filter = '';
3300  if ($this->compress) {
3301  $data = gzcompress($data);
3302  $filter = ' /Filter /FlateDecode';
3303  }
3304  $this->offsets[$filedata['n']] = $this->bufferlen;
3305  $this->_out($filedata['n'].' 0 obj');
3306  $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
3307  $this->_putstream($data);
3308  $this->_out('endobj');
3309  }
3310  }
3311 
3326  public function Text($x, $y, $txt, $stroke=0, $clip=false) {
3327  //Output a string
3328  if ($this->rtl) {
3329  // bidirectional algorithm (some chars may be changed affecting the line length)
3330  $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
3331  $l = $this->GetArrStringWidth($s);
3332  $xr = $this->w - $x - $this->GetArrStringWidth($s);
3333  } else {
3334  $xr = $x;
3335  }
3336  $opt = '';
3337  if (($stroke > 0) AND (!$clip)) {
3338  $opt .= '1 Tr '.intval($stroke).' w ';
3339  } elseif (($stroke > 0) AND $clip) {
3340  $opt .= '5 Tr '.intval($stroke).' w ';
3341  } elseif ($clip) {
3342  $opt .= '7 Tr ';
3343  }
3344  $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
3345  if ($this->underline AND ($txt!='')) {
3346  $s .= ' '.$this->_dounderline($xr, $y, $txt);
3347  }
3348  if ($this->linethrough AND ($txt!='')) {
3349  $s .= ' '.$this->_dolinethrough($xr, $y, $txt);
3350  }
3351  if ($this->ColorFlag AND (!$clip)) {
3352  $s='q '.$this->TextColor.' '.$s.' Q';
3353  }
3354  $this->_out($s);
3355  }
3356 
3366  public function AcceptPageBreak() {
3367  return $this->AutoPageBreak;
3368  }
3369 
3379  protected function checkPageBreak($h=0, $y='', $addpage=true) {
3380  if ($this->empty_string($y)) {
3381  $y = $this->y;
3382  }
3383  if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
3384  if ($addpage) {
3385  //Automatic page break
3386  $x = $this->x;
3387  $this->AddPage($this->CurOrientation);
3388  $this->y = $this->tMargin;
3389  $oldpage = $this->page - 1;
3390  if ($this->rtl) {
3391  if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
3392  $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
3393  } else {
3394  $this->x = $x;
3395  }
3396  } else {
3397  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3398  $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
3399  } else {
3400  $this->x = $x;
3401  }
3402  }
3403  }
3404  return true;
3405  }
3406  return false;
3407  }
3408 
3427  public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3428  //$min_cell_height = $this->FontAscent + $this->FontDescent;
3429  $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3430  if ($h < $min_cell_height) {
3431  $h = $min_cell_height;
3432  }
3433  $this->checkPageBreak($h);
3434  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
3435  }
3436 
3444  public function removeSHY($txt='') {
3445  /*
3446  * Unicode Data
3447  * Name : SOFT HYPHEN, commonly abbreviated as SHY
3448  * HTML Entity (decimal): &#173;
3449  * HTML Entity (hex): &#xad;
3450  * HTML Entity (named): &shy;
3451  * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
3452  * UTF-8 (hex): 0xC2 0xAD (c2ad)
3453  * UTF-8 character: chr(194).chr(173)
3454  */
3455  $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
3456  if (!$this->isunicode) {
3457  $txt = preg_replace('/([\\xad]{1})/', '', $txt);
3458  }
3459  return $txt;
3460  }
3461 
3479  protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3480  $txt = $this->removeSHY($txt);
3481  $rs = ''; //string to be returned
3482  if (!$ignore_min_height) {
3483  $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3484  if ($h < $min_cell_height) {
3485  $h = $min_cell_height;
3486  }
3487  }
3488  $k = $this->k;
3489  if ($this->empty_string($w) OR ($w <= 0)) {
3490  if ($this->rtl) {
3491  $w = $this->x - $this->lMargin;
3492  } else {
3493  $w = $this->w - $this->rMargin - $this->x;
3494  }
3495  }
3496  $s = '';
3497  if (($fill == 1) OR ($border == 1)) {
3498  if ($fill == 1) {
3499  $op = ($border == 1) ? 'B' : 'f';
3500  } else {
3501  $op = 'S';
3502  }
3503  if ($this->rtl) {
3504  $xk = (($this->x - $w) * $k);
3505  } else {
3506  $xk = ($this->x * $k);
3507  }
3508  $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
3509  }
3510  if (is_string($border)) {
3511  $lm = ($this->LineWidth / 2);
3512  $x = $this->x;
3513  $y = $this->y;
3514  if (strpos($border,'L') !== false) {
3515  if ($this->rtl) {
3516  $xk = ($x - $w) * $k;
3517  } else {
3518  $xk = $x * $k;
3519  }
3520  $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
3521  }
3522  if (strpos($border,'T') !== false) {
3523  if ($this->rtl) {
3524  $xk = ($x - $w + $lm) * $k;
3525  $xwk = ($x - $lm) * $k;
3526  } else {
3527  $xk = ($x - $lm) * $k;
3528  $xwk = ($x + $w + $lm) * $k;
3529  }
3530  $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
3531  }
3532  if (strpos($border,'R') !== false) {
3533  if ($this->rtl) {
3534  $xk = $x * $k;
3535  } else {
3536  $xk = ($x + $w) * $k;
3537  }
3538  $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
3539  }
3540  if (strpos($border,'B') !== false) {
3541  if ($this->rtl) {
3542  $xk = ($x - $w + $lm) * $k;
3543  $xwk = ($x - $lm) * $k;
3544  } else {
3545  $xk = ($x - $lm) * $k;
3546  $xwk = ($x + $w + $lm) * $k;
3547  }
3548  $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
3549  }
3550  }
3551  if ($txt != '') {
3552  // text lenght
3553  $width = $this->GetStringWidth($txt);
3554  // ratio between cell lenght and text lenght
3555  $ratio = ($w - (2 * $this->cMargin)) / $width;
3556 
3557  // stretch text if required
3558  if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
3559  if ($stretch > 2) {
3560  // spacing
3561  //Calculate character spacing in points
3562  $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
3563  //Set character spacing
3564  $rs .= sprintf('BT %.2F Tc ET ', $char_space);
3565  } else {
3566  // scaling
3567  //Calculate horizontal scaling
3568  $horiz_scale = $ratio * 100.0;
3569  //Set horizontal scaling
3570  $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
3571  }
3572  $align = '';
3573  $width = $w - (2 * $this->cMargin);
3574  } else {
3575  $stretch == 0;
3576  }
3577  if ($align == 'L') {
3578  if ($this->rtl) {
3579  $dx = $w - $width - $this->cMargin;
3580  } else {
3581  $dx = $this->cMargin;
3582  }
3583  } elseif ($align == 'R') {
3584  if ($this->rtl) {
3585  $dx = $this->cMargin;
3586  } else {
3587  $dx = $w - $width - $this->cMargin;
3588  }
3589  } elseif ($align == 'C') {
3590  $dx = ($w - $width) / 2;
3591  } elseif ($align == 'J') {
3592  if ($this->rtl) {
3593  $dx = $w - $width - $this->cMargin;
3594  } else {
3595  $dx = $this->cMargin;
3596  }
3597  } else {
3598  $dx = $this->cMargin;
3599  }
3600  if ($this->ColorFlag) {
3601  $s .= 'q '.$this->TextColor.' ';
3602  }
3603  $txt2 = $this->_escapetext($txt);
3604  if ($this->rtl) {
3605  $xdk = ($this->x - $dx - $width) * $k;
3606  } else {
3607  $xdk = ($this->x + $dx) * $k;
3608  }
3609  // Justification
3610  if ($align == 'J') {
3611  // count number of spaces
3612  $ns = substr_count($txt, ' ');
3613  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
3614  // get string width without spaces
3615  $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3616  // calculate average space width
3617  $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3618  // set word position to be used with TJ operator
3619  $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3620  } else {
3621  // get string width
3622  $width = $this->GetStringWidth($txt);
3623  $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3624  $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
3625  }
3626  }
3627  // calculate approximate position of the font base line
3628  //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3629  $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3630  // print text
3631  $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3632  if ($this->rtl) {
3633  $xdx = $this->x - $dx - $width;
3634  } else {
3635  $xdx = $this->x + $dx;
3636  }
3637  if ($this->underline) {
3638  $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3639  }
3640  if ($this->linethrough) {
3641  $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3642  }
3643  if ($this->ColorFlag) {
3644  $s .= ' Q';
3645  }
3646  if ($link) {
3647  $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
3648  }
3649  }
3650  // output cell
3651  if ($s) {
3652  // output cell
3653  $rs .= $s;
3654  // reset text stretching
3655  if ($stretch > 2) {
3656  //Reset character horizontal spacing
3657  $rs .= ' BT 0 Tc ET';
3658  } elseif ($stretch > 0) {
3659  //Reset character horizontal scaling
3660  $rs .= ' BT 100 Tz ET';
3661  }
3662  }
3663  // reset word spacing
3664  if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
3665  $rs .= ' BT 0 Tw ET';
3666  }
3667  $this->lasth = $h;
3668  if ($ln > 0) {
3669  //Go to the beginning of the next line
3670  $this->y += $h;
3671  if ($ln == 1) {
3672  if ($this->rtl) {
3673  $this->x = $this->w - $this->rMargin;
3674  } else {
3675  $this->x = $this->lMargin;
3676  }
3677  }
3678  } else {
3679  // go left or right by case
3680  if ($this->rtl) {
3681  $this->x -= $w;
3682  } else {
3683  $this->x += $w;
3684  }
3685  }
3686  $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
3687  $rs = $gstyles.$rs;
3688  return $rs;
3689  }
3690 
3714  public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) {
3715  if ($this->empty_string($this->lasth) OR $reseth) {
3716  //set row height
3717  $this->lasth = $this->FontSize * $this->cell_height_ratio;
3718  }
3719  if (!$this->empty_string($y)) {
3720  $this->SetY($y);
3721  } else {
3722  $y = $this->GetY();
3723  }
3724  // check for page break
3725  $this->checkPageBreak($h);
3726  $y = $this->GetY();
3727  // get current page number
3728  $startpage = $this->page;
3729  if (!$this->empty_string($x)) {
3730  $this->SetX($x);
3731  } else {
3732  $x = $this->GetX();
3733  }
3734  if ($this->empty_string($w) OR ($w <= 0)) {
3735  if ($this->rtl) {
3736  $w = $this->x - $this->lMargin;
3737  } else {
3738  $w = $this->w - $this->rMargin - $this->x;
3739  }
3740  }
3741  // store original margin values
3742  $lMargin = $this->lMargin;
3743  $rMargin = $this->rMargin;
3744  if ($this->rtl) {
3745  $this->SetRightMargin($this->w - $this->x);
3746  $this->SetLeftMargin($this->x - $w);
3747  } else {
3748  $this->SetLeftMargin($this->x);
3749  $this->SetRightMargin($this->w - $this->x - $w);
3750  }
3751  $starty = $this->y;
3752  if ($autopadding) {
3753  // Adjust internal padding
3754  if ($this->cMargin < ($this->LineWidth / 2)) {
3755  $this->cMargin = ($this->LineWidth / 2);
3756  }
3757  // Add top space if needed
3758  if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3759  $this->y += $this->LineWidth / 2;
3760  }
3761  // add top padding
3762  $this->y += $this->cMargin;
3763  }
3764  if ($ishtml) {
3765  // ******* Write HTML text
3766  $this->writeHTML($txt, true, 0, $reseth, true, $align);
3767  $nl = 1;
3768  } else {
3769  // ******* Write text
3770  $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
3771  }
3772  if ($autopadding) {
3773  // add bottom padding
3774  $this->y += $this->cMargin;
3775  // Add bottom space if needed
3776  if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3777  $this->y += $this->LineWidth / 2;
3778  }
3779  }
3780  // Get end-of-text Y position
3781  $currentY = $this->y;
3782  // get latest page number
3783  $endpage = $this->page;
3784  // check if a new page has been created
3785  if ($endpage > $startpage) {
3786  // design borders around HTML cells.
3787  for ($page=$startpage; $page <= $endpage; ++$page) {
3788  $this->setPage($page);
3789  if ($page == $startpage) {
3790  $this->y = $starty; // put cursor at the beginning of cell on the first page
3791  $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
3792  $cborder = $this->getBorderMode($border, $position='start');
3793  } elseif ($page == $endpage) {
3794  $this->y = $this->tMargin; // put cursor at the beginning of last page
3795  $h = $currentY - $this->tMargin;
3796  $cborder = $this->getBorderMode($border, $position='end');
3797  } else {
3798  $this->y = $this->tMargin; // put cursor at the beginning of the current page
3799  $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3800  $cborder = $this->getBorderMode($border, $position='middle');
3801  }
3802  $nx = $x;
3803  // account for margin changes
3804  if ($page > $startpage) {
3805  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
3806  $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
3807  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
3808  $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
3809  }
3810  }
3811  $this->SetX($nx);
3812  $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
3813  if ($cborder OR $fill) {
3814  $pagebuff = $this->getPageBuffer($this->page);
3815  $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
3816  $pend = substr($pagebuff, $this->intmrk[$this->page]);
3817  $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3818  $this->intmrk[$this->page] += strlen($ccode."\n");
3819  }
3820  }
3821  } else {
3822  $h = max($h, ($currentY - $y));
3823  // put cursor at the beginning of text
3824  $this->SetY($y);
3825  $this->SetX($x);
3826  // design a cell around the text
3827  $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
3828  if ($border OR $fill) {
3829  if (end($this->transfmrk[$this->page]) !== false) {
3830  $pagemarkkey = key($this->transfmrk[$this->page]);
3831  $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
3832  } elseif ($this->InFooter) {
3833  $pagemark = &$this->footerpos[$this->page];
3834  } else {
3835  $pagemark = &$this->intmrk[$this->page];
3836  }
3837  $pagebuff = $this->getPageBuffer($this->page);
3838  $pstart = substr($pagebuff, 0, $pagemark);
3839  $pend = substr($pagebuff, $pagemark);
3840  $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3841  $pagemark += strlen($ccode."\n");
3842  }
3843  }
3844  // Get end-of-cell Y position
3845  $currentY = $this->GetY();
3846  // restore original margin values
3847  $this->SetLeftMargin($lMargin);
3848  $this->SetRightMargin($rMargin);
3849  if ($ln > 0) {
3850  //Go to the beginning of the next line
3851  $this->SetY($currentY);
3852  if ($ln == 2) {
3853  $this->SetX($x + $w);
3854  }
3855  } else {
3856  // go left or right by case
3857  $this->setPage($startpage);
3858  $this->y = $y;
3859  $this->SetX($x + $w);
3860  }
3861  $this->setContentMark();
3862  return $nl;
3863  }
3864 
3873  protected function getBorderMode($border, $position='start') {
3874  if ((!$this->opencell) AND ($border == 1)) {
3875  return 1;
3876  }
3877  $cborder = '';
3878  switch ($position) {
3879  case 'start': {
3880  if ($border == 1) {
3881  $cborder = 'LTR';
3882  } else {
3883  if (!(false === strpos($border, 'L'))) {
3884  $cborder .= 'L';
3885  }
3886  if (!(false === strpos($border, 'T'))) {
3887  $cborder .= 'T';
3888  }
3889  if (!(false === strpos($border, 'R'))) {
3890  $cborder .= 'R';
3891  }
3892  if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3893  $cborder .= 'B';
3894  }
3895  }
3896  break;
3897  }
3898  case 'middle': {
3899  if ($border == 1) {
3900  $cborder = 'LR';
3901  } else {
3902  if (!(false === strpos($border, 'L'))) {
3903  $cborder .= 'L';
3904  }
3905  if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3906  $cborder .= 'T';
3907  }
3908  if (!(false === strpos($border, 'R'))) {
3909  $cborder .= 'R';
3910  }
3911  if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3912  $cborder .= 'B';
3913  }
3914  }
3915  break;
3916  }
3917  case 'end': {
3918  if ($border == 1) {
3919  $cborder = 'LRB';
3920  } else {
3921  if (!(false === strpos($border, 'L'))) {
3922  $cborder .= 'L';
3923  }
3924  if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3925  $cborder .= 'T';
3926  }
3927  if (!(false === strpos($border, 'R'))) {
3928  $cborder .= 'R';
3929  }
3930  if (!(false === strpos($border, 'B'))) {
3931  $cborder .= 'B';
3932  }
3933  }
3934  break;
3935  }
3936  default: {
3937  $cborder = $border;
3938  break;
3939  }
3940  }
3941  return $cborder;
3942  }
3943 
3952  public function getNumLines($txt, $w=0) {
3953  $lines = 0;
3954  if ($this->empty_string($w) OR ($w <= 0)) {
3955  if ($this->rtl) {
3956  $w = $this->x - $this->lMargin;
3957  } else {
3958  $w = $this->w - $this->rMargin - $this->x;
3959  }
3960  }
3961  // max column width
3962  $wmax = $w - (2 * $this->cMargin);
3963  // remove carriage returns
3964  $txt = str_replace("\r", '', $txt);
3965  // remove last newline (if any)
3966  if (substr($txt,-1) == "\n") {
3967  $txt = substr($txt, 0, -1);
3968  }
3969  // divide text in blocks
3970  $txtblocks = explode("\n", $txt);
3971  // for each block;
3972  foreach ($txtblocks as $block) {
3973  // estimate the number of lines
3974  $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
3975  }
3976  return $lines;
3977  }
3978 
3995  public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
3996  if (strlen($txt) == 0) {
3997  $txt = ' ';
3998  }
3999  // remove carriage returns
4000  $s = str_replace("\r", '', $txt);
4001  // check if string contains arabic text
4002  if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
4003  $arabic = true;
4004  } else {
4005  $arabic = false;
4006  }
4007  // check if string contains RTL text
4008  if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
4009  $rtlmode = true;
4010  } else {
4011  $rtlmode = false;
4012  }
4013  // get a char width
4014  $chrwidth = $this->GetCharWidth('.');
4015  // get array of unicode values
4016  $chars = $this->UTF8StringToArray($s);
4017  // get array of chars
4018  $uchars = $this->UTF8ArrayToUniArray($chars);
4019  // get the number of characters
4020  $nb = count($chars);
4021  // replacement for SHY character (minus symbol)
4022  $shy_replacement = 45;
4023  $shy_replacement_char = $this->unichr($shy_replacement);
4024  // widht for SHY replacement
4025  $shy_replacement_width = $this->GetCharWidth($shy_replacement);
4026  // store current position
4027  $prevx = $this->x;
4028  $prevy = $this->y;
4029  // max Y
4030  $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
4031  // calculate remaining line width ($w)
4032  if ($this->rtl) {
4033  $w = $this->x - $this->lMargin;
4034  } else {
4035  $w = $this->w - $this->rMargin - $this->x;
4036  }
4037  // max column width
4038  $wmax = $w - (2 * $this->cMargin);
4039  if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
4040  // a single character do not fit on column
4041  return '';
4042  }
4043  $i = 0; // character position
4044  $j = 0; // current starting position
4045  $sep = -1; // position of the last blank space
4046  $shy = false; // true if the last blank is a soft hypen (SHY)
4047  $l = 0; // current string lenght
4048  $nl = 0; //number of lines
4049  $linebreak = false;
4050  // for each character
4051  while ($i < $nb) {
4052  if (($maxh > 0) AND ($this->y >= $maxy) ) {
4053  $firstline = true;
4054  }
4055  //Get the current character
4056  $c = $chars[$i];
4057  if ($c == 10) { // 10 = "\n" = new line
4058  //Explicit line break
4059  if ($align == 'J') {
4060  if ($this->rtl) {
4061  $talign = 'R';
4062  } else {
4063  $talign = 'L';
4064  }
4065  } else {
4066  $talign = $align;
4067  }
4068  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4069  if ($firstline) {
4070  $startx = $this->x;
4071  $tmparr = array_slice($chars, $j, $i);
4072  if ($rtlmode) {
4073  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4074  }
4075  $linew = $this->GetArrStringWidth($tmparr);
4076  unset($tmparr);
4077  if ($this->rtl) {
4078  $this->endlinex = $startx - $linew;
4079  } else {
4080  $this->endlinex = $startx + $linew;
4081  }
4082  $w = $linew;
4083  $tmpcmargin = $this->cMargin;
4084  if ($maxh == 0) {
4085  $this->cMargin = 0;
4086  }
4087  }
4088  $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
4089  unset($tmpstr);
4090  if ($firstline) {
4091  $this->cMargin = $tmpcmargin;
4092  return ($this->UniArrSubString($uchars, $i));
4093  }
4094  ++$nl;
4095  $j = $i + 1;
4096  $l = 0;
4097  $sep = -1;
4098  $shy = false;
4099  // account for margin changes
4100  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4101  // AcceptPageBreak() may be overriden on extended classed to include margin changes
4102  $this->AcceptPageBreak();
4103  }
4104  $w = $this->getRemainingWidth();
4105  $wmax = $w - (2 * $this->cMargin);
4106  } else {
4107  // 160 is the non-breaking space.
4108  // 173 is SHY (Soft Hypen).
4109  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
4110  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
4111  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
4112  if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
4113  // update last blank space position
4114  $sep = $i;
4115  // check if is a SHY
4116  if ($c == 173) {
4117  $shy = true;
4118  } else {
4119  $shy = false;
4120  }
4121  }
4122  // update string length
4123  if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
4124  // with bidirectional algorithm some chars may be changed affecting the line length
4125  // *** very slow ***
4126  $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
4127  } else {
4128  $l += $this->GetCharWidth($c);
4129  }
4130  if (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax)) ) {
4131  // we have reached the end of column
4132  if ($sep == -1) {
4133  // check if the line was already started
4134  if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
4135  OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
4136  // print a void cell and go to next line
4137  $this->Cell($w, $h, '', 0, 1);
4138  $linebreak = true;
4139  if ($firstline) {
4140  return ($this->UniArrSubString($uchars, $j));
4141  }
4142  } else {
4143  // truncate the word because do not fit on column
4144  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4145  if ($firstline) {
4146  $startx = $this->x;
4147  $tmparr = array_slice($chars, $j, $i);
4148  if ($rtlmode) {
4149  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4150  }
4151  $linew = $this->GetArrStringWidth($tmparr);
4152  unset($tmparr);
4153  if ($this->rtl) {
4154  $this->endlinex = $startx - $linew;
4155  } else {
4156  $this->endlinex = $startx + $linew;
4157  }
4158  $w = $linew;
4159  $tmpcmargin = $this->cMargin;
4160  if ($maxh == 0) {
4161  $this->cMargin = 0;
4162  }
4163  }
4164  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
4165  unset($tmpstr);
4166  if ($firstline) {
4167  $this->cMargin = $tmpcmargin;
4168  return ($this->UniArrSubString($uchars, $i));
4169  }
4170  $j = $i;
4171  --$i;
4172  }
4173  } else {
4174  // word wrapping
4175  if ($this->rtl AND (!$firstblock)) {
4176  $endspace = 1;
4177  } else {
4178  $endspace = 0;
4179  }
4180  if ($shy) {
4181  // add hypen (minus symbol) at the end of the line
4182  $shy_width = $shy_replacement_width;
4183  if ($this->rtl) {
4184  $shy_char_left = $shy_replacement_char;
4185  $shy_char_right = '';
4186  } else {
4187  $shy_char_left = '';
4188  $shy_char_right = $shy_replacement_char;
4189  }
4190  } else {
4191  $shy_width = 0;
4192  $shy_char_left = '';
4193  $shy_char_right = '';
4194  }
4195  $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
4196  if ($firstline) {
4197  $startx = $this->x;
4198  $tmparr = array_slice($chars, $j, ($sep + $endspace));
4199  if ($rtlmode) {
4200  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4201  }
4202  $linew = $this->GetArrStringWidth($tmparr);
4203  unset($tmparr);
4204  if ($this->rtl) {
4205  $this->endlinex = $startx - $linew - $shy_width;
4206  } else {
4207  $this->endlinex = $startx + $linew + $shy_width;
4208  }
4209  $w = $linew;
4210  $tmpcmargin = $this->cMargin;
4211  if ($maxh == 0) {
4212  $this->cMargin = 0;
4213  }
4214  }
4215  // print the line
4216  $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
4217  unset($tmpstr);
4218  if ($firstline) {
4219  // return the remaining text
4220  $this->cMargin = $tmpcmargin;
4221  return ($this->UniArrSubString($uchars, ($sep + $endspace)));
4222  }
4223  $i = $sep;
4224  $sep = -1;
4225  $shy = false;
4226  $j = ($i+1);
4227  }
4228  // account for margin changes
4229  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4230  // AcceptPageBreak() may be overriden on extended classed to include margin changes
4231  $this->AcceptPageBreak();
4232  }
4233  $w = $this->getRemainingWidth();
4234  $wmax = $w - (2 * $this->cMargin);
4235  if ($linebreak) {
4236  $linebreak = false;
4237  } else {
4238  ++$nl;
4239  $l = 0;
4240  }
4241  }
4242  }
4243  ++$i;
4244  } // end while i < nb
4245  // print last substring (if any)
4246  if ($l > 0) {
4247  switch ($align) {
4248  case 'J':
4249  case 'C': {
4250  $w = $w;
4251  break;
4252  }
4253  case 'L': {
4254  if ($this->rtl) {
4255  $w = $w;
4256  } else {
4257  $w = $l;
4258  }
4259  break;
4260  }
4261  case 'R': {
4262  if ($this->rtl) {
4263  $w = $l;
4264  } else {
4265  $w = $w;
4266  }
4267  break;
4268  }
4269  default: {
4270  $w = $l;
4271  break;
4272  }
4273  }
4274  $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
4275  if ($firstline) {
4276  $startx = $this->x;
4277  $tmparr = array_slice($chars, $j, $nb);
4278  if ($rtlmode) {
4279  $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4280  }
4281  $linew = $this->GetArrStringWidth($tmparr);
4282  unset($tmparr);
4283  if ($this->rtl) {
4284  $this->endlinex = $startx - $linew;
4285  } else {
4286  $this->endlinex = $startx + $linew;
4287  }
4288  $w = $linew;
4289  $tmpcmargin = $this->cMargin;
4290  if ($maxh == 0) {
4291  $this->cMargin = 0;
4292  }
4293  }
4294  $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
4295  unset($tmpstr);
4296  if ($firstline) {
4297  $this->cMargin = $tmpcmargin;
4298  return ($this->UniArrSubString($uchars, $nb));
4299  }
4300  ++$nl;
4301  }
4302  if ($firstline) {
4303  return '';
4304  }
4305  return $nl;
4306  }
4307 
4313  protected function getRemainingWidth() {
4314  if ($this->rtl) {
4315  return ($this->x - $this->lMargin);
4316  } else {
4317  return ($this->w - $this->rMargin - $this->x);
4318  }
4319  }
4320 
4329  public function UTF8ArrSubString($strarr, $start='', $end='') {
4330  if (strlen($start) == 0) {
4331  $start = 0;
4332  }
4333  if (strlen($end) == 0) {
4334  $end = count($strarr);
4335  }
4336  $string = '';
4337  for ($i=$start; $i < $end; ++$i) {
4338  $string .= $this->unichr($strarr[$i]);
4339  }
4340  return $string;
4341  }
4342 
4352  public function UniArrSubString($uniarr, $start='', $end='') {
4353  if (strlen($start) == 0) {
4354  $start = 0;
4355  }
4356  if (strlen($end) == 0) {
4357  $end = count($uniarr);
4358  }
4359  $string = '';
4360  for ($i=$start; $i < $end; ++$i) {
4361  $string .= $uniarr[$i];
4362  }
4363  return $string;
4364  }
4365 
4373  public function UTF8ArrayToUniArray($ta) {
4374  return array_map(array($this, 'unichr'), $ta);
4375  }
4376 
4385  public function unichr($c) {
4386  if (!$this->isunicode) {
4387  return chr($c);
4388  } elseif ($c <= 0x7F) {
4389  // one byte
4390  return chr($c);
4391  } elseif ($c <= 0x7FF) {
4392  // two bytes
4393  return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
4394  } elseif ($c <= 0xFFFF) {
4395  // three bytes
4396  return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4397  } elseif ($c <= 0x10FFFF) {
4398  // four bytes
4399  return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4400  } else {
4401  return '';
4402  }
4403  }
4404 
4435  public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false) {
4436  if ($x === '') {
4437  $x = $this->x;
4438  }
4439  if ($y === '') {
4440  $y = $this->y;
4441  }
4442  // get image dimensions
4443  $imsize = @getimagesize($file);
4444  if ($imsize === FALSE) {
4445  // encode spaces on filename
4446  $file = str_replace(' ', '%20', $file);
4447  $imsize = @getimagesize($file);
4448  if ($imsize === FALSE) {
4449  $this->Error('[Image] No such file or directory in '.$file);
4450  }
4451  }
4452  // get original image width and height in pixels
4453  list($pixw, $pixh) = $imsize;
4454  // calculate image width and height on document
4455  if (($w <= 0) AND ($h <= 0)) {
4456  // convert image size to document unit
4457  $w = $this->pixelsToUnits($pixw);
4458  $h = $this->pixelsToUnits($pixh);
4459  } elseif ($w <= 0) {
4460  $w = $h * $pixw / $pixh;
4461  } elseif ($h <= 0) {
4462  $h = $w * $pixh / $pixw;
4463  } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
4464  // scale image dimensions proportionally to fit within the ($w, $h) box
4465  if ((($w * $pixh) / ($h * $pixw)) < 1) {
4466  $h = $w * $pixh / $pixw;
4467  } else {
4468  $w = $h * $pixw / $pixh;
4469  }
4470  }
4471  // calculate new minimum dimensions in pixels
4472  $neww = round($w * $this->k * $dpi / $this->dpi);
4473  $newh = round($h * $this->k * $dpi / $this->dpi);
4474  // check if resize is necessary (resize is used only to reduce the image)
4475  if (($neww * $newh) >= ($pixw * $pixh)) {
4476  $resize = false;
4477  }
4478  // check if image has been already added on document
4479  if (!in_array($file, $this->imagekeys)) {
4480  //First use of image, get info
4481  if ($type == '') {
4482  $fileinfo = pathinfo($file);
4483  if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
4484  $type = $fileinfo['extension'];
4485  } else {
4486  $this->Error('Image file has no extension and no type was specified: '.$file);
4487  }
4488  }
4489  $type = strtolower($type);
4490  if ($type == 'jpg') {
4491  $type = 'jpeg';
4492  }
4493  $mqr = get_magic_quotes_runtime();
4494  set_magic_quotes_runtime(0);
4495  // Specific image handlers
4496  $mtd = '_parse'.$type;
4497  // GD image handler function
4498  $gdfunction = 'imagecreatefrom'.$type;
4499  $info = false;
4500  if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
4501  // TCPDF image functions
4502  $info = $this->$mtd($file);
4503  if ($info == 'pngalpha') {
4504  return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
4505  }
4506  }
4507  if (!$info) {
4508  if (function_exists($gdfunction)) {
4509  // GD library
4510  $img = $gdfunction($file);
4511  if ($resize) {
4512  $imgr = imagecreatetruecolor($neww, $newh);
4513  imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
4514  $info = $this->_toJPEG($imgr);
4515  } else {
4516  $info = $this->_toJPEG($img);
4517  }
4518  } elseif (extension_loaded('imagick')) {
4519  // ImageMagick library
4520  $img = new Imagick();
4521  $img->readImage($file);
4522  if ($resize) {
4523  $img->resizeImage($neww, $newh, 10, 1, false);
4524  }
4525  $img->setCompressionQuality($this->jpeg_quality);
4526  $img->setImageFormat('jpeg');
4527  $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4528  $img->writeImage($tempname);
4529  $info = $this->_parsejpeg($tempname);
4530  unlink($tempname);
4531  $img->destroy();
4532  } else {
4533  return;
4534  }
4535  }
4536  if ($info === false) {
4537  //If false, we cannot process image
4538  return;
4539  }
4540  set_magic_quotes_runtime($mqr);
4541  if ($ismask) {
4542  // force grayscale
4543  $info['cs'] = 'DeviceGray';
4544  }
4545  $info['i'] = $this->numimages + 1;
4546  if ($imgmask !== false) {
4547  $info['masked'] = $imgmask;
4548  }
4549  // add image to document
4550  $this->setImageBuffer($file, $info);
4551  } else {
4552  $info = $this->getImageBuffer($file);
4553  }
4554  // Check whether we need a new page first as this does not fit
4555  if ($this->checkPageBreak($h, $y)) {
4556  $y = $this->GetY() + $this->cMargin;
4557  }
4558  // set bottomcoordinates
4559  $this->img_rb_y = $y + $h;
4560  // set alignment
4561  if ($this->rtl) {
4562  if ($palign == 'L') {
4563  $ximg = $this->lMargin;
4564  // set right side coordinate
4565  $this->img_rb_x = $ximg + $w;
4566  } elseif ($palign == 'C') {
4567  $ximg = ($this->w - $x - $w) / 2;
4568  // set right side coordinate
4569  $this->img_rb_x = $ximg + $w;
4570  } else {
4571  $ximg = $this->w - $x - $w;
4572  // set left side coordinate
4573  $this->img_rb_x = $ximg;
4574  }
4575  } else {
4576  if ($palign == 'R') {
4577  $ximg = $this->w - $this->rMargin - $w;
4578  // set left side coordinate
4579  $this->img_rb_x = $ximg;
4580  } elseif ($palign == 'C') {
4581  $ximg = ($this->w - $x - $w) / 2;
4582  // set right side coordinate
4583  $this->img_rb_x = $ximg + $w;
4584  } else {
4585  $ximg = $x;
4586  // set right side coordinate
4587  $this->img_rb_x = $ximg + $w;
4588  }
4589  }
4590  if ($ismask) {
4591  // embed hidden, ouside the canvas
4592  $xkimg = ($this->pagedim[$this->page]['w'] + 10);
4593  } else {
4594  $xkimg = $ximg * $this->k;
4595  }
4596  $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
4597  if (!empty($border)) {
4598  $bx = $x;
4599  $by = $y;
4600  $this->x = $ximg;
4601  $this->y = $y;
4602  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
4603  $this->x = $bx;
4604  $this->y = $by;
4605  }
4606  if ($link) {
4607  $this->Link($ximg, $y, $w, $h, $link, 0);
4608  }
4609  // set pointer to align the successive text/objects
4610  switch($align) {
4611  case 'T': {
4612  $this->y = $y;
4613  $this->x = $this->img_rb_x;
4614  break;
4615  }
4616  case 'M': {
4617  $this->y = $y + round($h/2);
4618  $this->x = $this->img_rb_x;
4619  break;
4620  }
4621  case 'B': {
4622  $this->y = $this->img_rb_y;
4623  $this->x = $this->img_rb_x;
4624  break;
4625  }
4626  case 'N': {
4627  $this->SetY($this->img_rb_y);
4628  break;
4629  }
4630  default:{
4631  break;
4632  }
4633  }
4634  $this->endlinex = $this->img_rb_x;
4635  return $info['i'];
4636  }
4637 
4646  protected function _toJPEG($image) {
4647  $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4648  imagejpeg($image, $tempname, $this->jpeg_quality);
4649  imagedestroy($image);
4650  $retvars = $this->_parsejpeg($tempname);
4651  // tidy up by removing temporary image
4652  unlink($tempname);
4653  return $retvars;
4654  }
4655 
4662  protected function _parsejpeg($file) {
4663  $a = getimagesize($file);
4664  if (empty($a)) {
4665  $this->Error('Missing or incorrect image file: '.$file);
4666  }
4667  if ($a[2] != 2) {
4668  $this->Error('Not a JPEG file: '.$file);
4669  }
4670  if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
4671  $colspace = 'DeviceRGB';
4672  } elseif ($a['channels'] == 4) {
4673  $colspace = 'DeviceCMYK';
4674  } else {
4675  $colspace = 'DeviceGray';
4676  }
4677  $bpc = isset($a['bits']) ? $a['bits'] : 8;
4678  $data = file_get_contents($file);
4679  return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
4680  }
4681 
4688  protected function _parsepng($file) {
4689  $f = fopen($file, 'rb');
4690  if ($f === false) {
4691  $this->Error('Can\'t open image file: '.$file);
4692  }
4693  //Check signature
4694  if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
4695  $this->Error('Not a PNG file: '.$file);
4696  }
4697  //Read header chunk
4698  fread($f, 4);
4699  if (fread($f, 4) != 'IHDR') {
4700  $this->Error('Incorrect PNG file: '.$file);
4701  }
4702  $w = $this->_freadint($f);
4703  $h = $this->_freadint($f);
4704  $bpc = ord(fread($f, 1));
4705  if ($bpc > 8) {
4706  //$this->Error('16-bit depth not supported: '.$file);
4707  fclose($f);
4708  return false;
4709  }
4710  $ct = ord(fread($f, 1));
4711  if ($ct == 0) {
4712  $colspace = 'DeviceGray';
4713  } elseif ($ct == 2) {
4714  $colspace = 'DeviceRGB';
4715  } elseif ($ct == 3) {
4716  $colspace = 'Indexed';
4717  } else {
4718  // alpha channel
4719  fclose($f);
4720  return 'pngalpha';
4721  }
4722  if (ord(fread($f, 1)) != 0) {
4723  //$this->Error('Unknown compression method: '.$file);
4724  fclose($f);
4725  return false;
4726  }
4727  if (ord(fread($f, 1)) != 0) {
4728  //$this->Error('Unknown filter method: '.$file);
4729  fclose($f);
4730  return false;
4731  }
4732  if (ord(fread($f, 1)) != 0) {
4733  //$this->Error('Interlacing not supported: '.$file);
4734  fclose($f);
4735  return false;
4736  }
4737  fread($f, 4);
4738  $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
4739  //Scan chunks looking for palette, transparency and image data
4740  $pal = '';
4741  $trns = '';
4742  $data = '';
4743  do {
4744  $n = $this->_freadint($f);
4745  $type = fread($f, 4);
4746  if ($type == 'PLTE') {
4747  //Read palette
4748  $pal = $this->rfread($f, $n);
4749  fread($f, 4);
4750  } elseif ($type == 'tRNS') {
4751  //Read transparency info
4752  $t = $this->rfread($f, $n);
4753  if ($ct == 0) {
4754  $trns = array(ord(substr($t, 1, 1)));
4755  } elseif ($ct == 2) {
4756  $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
4757  } else {
4758  $pos = strpos($t, chr(0));
4759  if ($pos !== false) {
4760  $trns = array($pos);
4761  }
4762  }
4763  fread($f, 4);
4764  } elseif ($type == 'IDAT') {
4765  //Read image data block
4766  $data .= $this->rfread($f, $n);
4767  fread($f, 4);
4768  } elseif ($type == 'IEND') {
4769  break;
4770  } else {
4771  $this->rfread($f, $n + 4);
4772  }
4773  } while ($n);
4774  if (($colspace == 'Indexed') AND (empty($pal))) {
4775  //$this->Error('Missing palette in '.$file);
4776  fclose($f);
4777  return false;
4778  }
4779  fclose($f);
4780  return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
4781  }
4782 
4793  protected function rfread($handle, $length) {
4794  $data = fread($handle, $length);
4795  if ($data === false) {
4796  return false;
4797  }
4798  $rest = $length - strlen($data);
4799  if ($rest > 0) {
4800  $data .= $this->rfread($handle, $rest);
4801  }
4802  return $data;
4803  }
4804 
4823  protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
4824  // get image size
4825  list($wpx, $hpx) = getimagesize($file);
4826  // generate images
4827  $img = imagecreatefrompng($file);
4828  $imgalpha = imagecreate($wpx, $hpx);
4829  // generate gray scale pallete
4830  for ($c = 0; $c < 256; ++$c) {
4831  ImageColorAllocate($imgalpha, $c, $c, $c);
4832  }
4833  // extract alpha channel
4834  for ($xpx = 0; $xpx < $wpx; ++$xpx) {
4835  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
4836  $colorindex = imagecolorat($img, $xpx, $ypx);
4837  $col = imagecolorsforindex($img, $colorindex);
4838  imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
4839  }
4840  }
4841  // create temp alpha file
4842  $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
4843  imagepng($imgalpha, $tempfile_alpha);
4844  imagedestroy($imgalpha);
4845  // extract image without alpha channel
4846  $imgplain = imagecreatetruecolor($wpx, $hpx);
4847  imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4848  // create temp image file
4849  $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
4850  imagepng($imgplain, $tempfile_plain);
4851  imagedestroy($imgplain);
4852  // embed mask image
4853  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
4854  // embed image, masked with previously embedded mask
4855  $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
4856  // remove temp files
4857  unlink($tempfile_alpha);
4858  unlink($tempfile_plain);
4859  }
4860 
4867  protected function getGDgamma($v) {
4868  return (pow(($v / 255), 2.2) * 255);
4869  }
4870 
4880  public function Ln($h='', $cell=false) {
4881  //Line feed; default value is last cell height
4882  if ($cell) {
4883  $cellmargin = $this->cMargin;
4884  } else {
4885  $cellmargin = 0;
4886  }
4887  if ($this->rtl) {
4888  $this->x = $this->w - $this->rMargin - $cellmargin;
4889  } else {
4890  $this->x = $this->lMargin + $cellmargin;
4891  }
4892  if (is_string($h)) {
4893  $this->y += $this->lasth;
4894  } else {
4895  $this->y += $h;
4896  }
4897  $this->newline = true;
4898  }
4899 
4908  public function GetX() {
4909  //Get x position
4910  if ($this->rtl) {
4911  return ($this->w - $this->x);
4912  } else {
4913  return $this->x;
4914  }
4915  }
4916 
4924  public function GetAbsX() {
4925  return $this->x;
4926  }
4927 
4935  public function GetY() {
4936  //Get y position
4937  return $this->y;
4938  }
4939 
4948  public function SetX($x) {
4949  //Set x position
4950  if ($this->rtl) {
4951  if ($x >= 0) {
4952  $this->x = $this->w - $x;
4953  } else {
4954  $this->x = abs($x);
4955  }
4956  } else {
4957  if ($x >= 0) {
4958  $this->x = $x;
4959  } else {
4960  $this->x = $this->w + $x;
4961  }
4962  }
4963  if ($this->x < 0) {
4964  $this->x = 0;
4965  }
4966  if ($this->x > $this->w) {
4967  $this->x = $this->w;
4968  }
4969  }
4970 
4980  public function SetY($y, $resetx=true) {
4981  if ($resetx) {
4982  //reset x
4983  if ($this->rtl) {
4984  $this->x = $this->w - $this->rMargin;
4985  } else {
4986  $this->x = $this->lMargin;
4987  }
4988  }
4989  if ($y >= 0) {
4990  $this->y = $y;
4991  } else {
4992  $this->y = $this->h + $y;
4993  }
4994  if ($this->y < 0) {
4995  $this->y = 0;
4996  }
4997  if ($this->y > $this->h) {
4998  $this->y = $this->h;
4999  }
5000  }
5001 
5011  public function SetXY($x, $y) {
5012  //Set x and y positions
5013  $this->SetY($y);
5014  $this->SetX($x);
5015  }
5016 
5027  public function Output($name='doc.pdf', $dest='I') {
5028  //Output PDF to some destination
5029  //Finish document if necessary
5030  if ($this->state < 3) {
5031  $this->Close();
5032  }
5033  //Normalize parameters
5034  if (is_bool($dest)) {
5035  $dest = $dest ? 'D' : 'F';
5036  }
5037  $dest = strtoupper($dest);
5038  if ($dest != 'F') {
5039  $name = preg_replace('/[\s]+/', '_', $name);
5040  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
5041  }
5042  if ($this->sign) {
5043  // *** apply digital signature to the document ***
5044  // get the document content
5045  $pdfdoc = $this->getBuffer();
5046  // remove last newline
5047  $pdfdoc = substr($pdfdoc, 0, -1);
5048  // Remove the original buffer
5049  if (isset($this->diskcache) AND $this->diskcache) {
5050  // remove buffer file from cache
5051  unlink($this->buffer);
5052  }
5053  unset($this->buffer);
5054  // remove filler space
5055  $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58;
5056  $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght);
5057  // define the ByteRange
5058  $byte_range = array();
5059  $byte_range[0] = 0;
5060  $byte_range[1] = $tmppos - 1;
5061  $byte_range[2] = $byte_range[1] + $this->signature_max_lenght;
5062  $byte_range[3] = strlen($pdfdoc) - $byte_range[1];
5063  // replace the ByteRange
5064  $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]);
5065  $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc);
5066  // write the document to a temporary folder
5067  $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
5068  $f = fopen($tempdoc, 'wb');
5069  if (!$f) {
5070  $this->Error('Unable to create temporary file: '.$tempdoc);
5071  }
5072  $pdfdoc_lenght = strlen($pdfdoc);
5073  fwrite($f, $pdfdoc, $pdfdoc_lenght);
5074  fclose($f);
5075  // get digital signature via openssl library
5076  $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
5077  if (empty($this->signature_data['extracerts'])) {
5078  openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
5079  } else {
5080  openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
5081  }
5082  unlink($tempdoc);
5083  // read signature
5084  $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
5085  unlink($tempsign);
5086  // extract signature
5087  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
5088  $tmparr = explode("\n\n", $signature);
5089  $signature = $tmparr[1];
5090  unset($tmparr);
5091  // decode signature
5092  $signature = base64_decode(trim($signature));
5093  // convert signature to hex
5094  $signature = current(unpack('H*', $signature));
5095  $signature = str_pad($signature, $this->signature_max_lenght, '0');
5096  // Add signature to the document
5097  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3]));
5098  $this->diskcache = false;
5099  $this->buffer = &$pdfdoc;
5100  $this->bufferlen = strlen($pdfdoc);
5101  }
5102  switch($dest) {
5103  case 'I': {
5104  // Send PDF to the standard output
5105  if (ob_get_contents()) {
5106  $this->Error('Some data has already been output, can\'t send PDF file');
5107  }
5108  if (php_sapi_name() != 'cli') {
5109  //We send to a browser
5110  header('Content-Type: application/pdf');
5111  if (headers_sent()) {
5112  $this->Error('Some data has already been output to browser, can\'t send PDF file');
5113  }
5114  header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5115  header('Pragma: public');
5116  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5117  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
5118  header('Content-Length: '.$this->bufferlen);
5119  header('Content-Disposition: inline; filename="'.basename($name).'";');
5120  }
5121  echo $this->getBuffer();
5122  break;
5123  }
5124  case 'D': {
5125  // Download PDF as file
5126  if (ob_get_contents()) {
5127  $this->Error('Some data has already been output, can\'t send PDF file');
5128  }
5129  header('Content-Description: File Transfer');
5130  if (headers_sent()) {
5131  $this->Error('Some data has already been output to browser, can\'t send PDF file');
5132  }
5133  header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5134  header('Pragma: public');
5135  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5136  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
5137  // force download dialog
5138  header('Content-Type: application/force-download');
5139  header('Content-Type: application/octet-stream', false);
5140  header('Content-Type: application/download', false);
5141  header('Content-Type: application/pdf', false);
5142  // use the Content-Disposition header to supply a recommended filename
5143  header('Content-Disposition: attachment; filename="'.basename($name).'";');
5144  header('Content-Transfer-Encoding: binary');
5145  header('Content-Length: '.$this->bufferlen);
5146  echo $this->getBuffer();
5147  break;
5148  }
5149  case 'F': {
5150  // Save PDF to a local file
5151  if ($this->diskcache) {
5152  copy($this->buffer, $name);
5153  } else {
5154  $f = fopen($name, 'wb');
5155  if (!$f) {
5156  $this->Error('Unable to create output file: '.$name);
5157  }
5158  fwrite($f, $this->getBuffer(), $this->bufferlen);
5159  fclose($f);
5160  }
5161  break;
5162  }
5163  case 'S': {
5164  // Returns PDF as a string
5165  return $this->getBuffer();
5166  }
5167  default: {
5168  $this->Error('Incorrect output destination: '.$dest);
5169  }
5170  }
5171  return '';
5172  }
5173 
5181  public function _destroy($destroyall=false, $preserve_objcopy=false) {
5182  if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
5183  // remove buffer file from cache
5184  unlink($this->buffer);
5185  }
5186  foreach (array_keys(get_object_vars($this)) as $val) {
5187  if ($destroyall OR (
5188  ($val != 'internal_encoding')
5189  AND ($val != 'state')
5190  AND ($val != 'bufferlen')
5191  AND ($val != 'buffer')
5192  AND ($val != 'diskcache')
5193  AND ($val != 'sign')
5194  AND ($val != 'signature_data')
5195  AND ($val != 'signature_max_lenght')
5196  )) {
5197  if (!$preserve_objcopy OR ($val != 'objcopy')) {
5198  unset($this->$val);
5199  }
5200  }
5201  }
5202  }
5203 
5208  protected function _dochecks() {
5209  //Check for locale-related bug
5210  if (1.1 == 1) {
5211  $this->Error('Don\'t alter the locale before including class file');
5212  }
5213  //Check for decimal separator
5214  if (sprintf('%.1F', 1.0) != '1.0') {
5215  setlocale(LC_NUMERIC, 'C');
5216  }
5217  }
5218 
5224  protected function _getfontpath() {
5225  if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
5226  define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
5227  }
5228  return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
5229  }
5230 
5235  protected function _putpages() {
5236  $nb = $this->numpages;
5237  if (!empty($this->AliasNbPages)) {
5238  $nbs = $this->formatPageNumber($nb);
5239  $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
5240  $alias_a = $this->_escape($this->AliasNbPages);
5241  $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
5242  if ($this->isunicode) {
5243  $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
5244  $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
5245  $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
5246  $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
5247  }
5248  }
5249  if (!empty($this->AliasNumPage)) {
5250  $alias_pa = $this->_escape($this->AliasNumPage);
5251  $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
5252  if ($this->isunicode) {
5253  $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
5254  $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
5255  $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
5256  $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
5257  }
5258  }
5259  $pagegroupnum = 0;
5260  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
5261  for ($n=1; $n <= $nb; ++$n) {
5262  $temppage = $this->getPageBuffer($n);
5263  if (!empty($this->pagegroups)) {
5264  if(isset($this->newpagegroup[$n])) {
5265  $pagegroupnum = 0;
5266  }
5267  ++$pagegroupnum;
5268  foreach ($this->pagegroups as $k => $v) {
5269  // replace total pages group numbers
5270  $vs = $this->formatPageNumber($v);
5271  $vu = $this->UTF8ToUTF16BE($vs, false);
5272  $alias_ga = $this->_escape($k);
5273  $alias_gau = $this->_escape('{'.$k.'}');
5274  if ($this->isunicode) {
5275  $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
5276  $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
5277  $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
5278  $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
5279  }
5280  $temppage = str_replace($alias_gau, $vu, $temppage);
5281  if ($this->isunicode) {
5282  $temppage = str_replace($alias_gbu, $vu, $temppage);
5283  $temppage = str_replace($alias_gcu, $vu, $temppage);
5284  $temppage = str_replace($alias_gb, $vs, $temppage);
5285  $temppage = str_replace($alias_gc, $vs, $temppage);
5286  }
5287  $temppage = str_replace($alias_ga, $vs, $temppage);
5288  // replace page group numbers
5289  $pvs = $this->formatPageNumber($pagegroupnum);
5290  $pvu = $this->UTF8ToUTF16BE($pvs, false);
5291  $pk = str_replace('{nb', '{pnb', $k);
5292  $alias_pga = $this->_escape($pk);
5293  $alias_pgau = $this->_escape('{'.$pk.'}');
5294  if ($this->isunicode) {
5295  $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
5296  $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
5297  $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
5298  $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
5299  }
5300  $temppage = str_replace($alias_pgau, $pvu, $temppage);
5301  if ($this->isunicode) {
5302  $temppage = str_replace($alias_pgbu, $pvu, $temppage);
5303  $temppage = str_replace($alias_pgcu, $pvu, $temppage);
5304  $temppage = str_replace($alias_pgb, $pvs, $temppage);
5305  $temppage = str_replace($alias_pgc, $pvs, $temppage);
5306  }
5307  $temppage = str_replace($alias_pga, $pvs, $temppage);
5308  }
5309  }
5310  if (!empty($this->AliasNbPages)) {
5311  // replace total pages number
5312  $temppage = str_replace($alias_au, $nbu, $temppage);
5313  if ($this->isunicode) {
5314  $temppage = str_replace($alias_bu, $nbu, $temppage);
5315  $temppage = str_replace($alias_cu, $nbu, $temppage);
5316  $temppage = str_replace($alias_b, $nbs, $temppage);
5317  $temppage = str_replace($alias_c, $nbs, $temppage);
5318  }
5319  $temppage = str_replace($alias_a, $nbs, $temppage);
5320  }
5321  if (!empty($this->AliasNumPage)) {
5322  // replace page number
5323  $pnbs = $this->formatPageNumber($n);
5324  $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
5325  $temppage = str_replace($alias_pau, $pnbu, $temppage);
5326  if ($this->isunicode) {
5327  $temppage = str_replace($alias_pbu, $pnbu, $temppage);
5328  $temppage = str_replace($alias_pcu, $pnbu, $temppage);
5329  $temppage = str_replace($alias_pb, $pnbs, $temppage);
5330  $temppage = str_replace($alias_pc, $pnbs, $temppage);
5331  }
5332  $temppage = str_replace($alias_pa, $pnbs, $temppage);
5333  }
5334  $temppage = str_replace($this->epsmarker, '', $temppage);
5335  //$this->setPageBuffer($n, $temppage);
5336  //Page
5337  $this->_newobj();
5338  $this->_out('<</Type /Page');
5339  $this->_out('/Parent 1 0 R');
5340  $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
5341  $this->_out('/Resources 2 0 R');
5342  $this->_putannots($n);
5343  $this->_out('/Contents '.($this->n + 1).' 0 R>>');
5344  $this->_out('endobj');
5345  //Page content
5346  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
5347  $this->_newobj();
5348  $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
5349  $this->_putstream($p);
5350  $this->_out('endobj');
5351  if ($this->diskcache) {
5352  // remove temporary files
5353  unlink($this->pages[$n]);
5354  }
5355  }
5356  //Pages root
5357  $this->offsets[1] = $this->bufferlen;
5358  $this->_out('1 0 obj');
5359  $this->_out('<</Type /Pages');
5360  $kids='/Kids [';
5361  for ($i=0; $i < $nb; ++$i) {
5362  $kids .= (3 + (2 * $i)).' 0 R ';
5363  }
5364  $this->_out($kids.']');
5365  $this->_out('/Count '.$nb);
5366  //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
5367  $this->_out('>>');
5368  $this->_out('endobj');
5369  }
5370 
5380  protected function _putannots($n) {
5381  if (isset($this->PageAnnots[$n])) {
5382  $annots = '/Annots [';
5383  foreach ($this->PageAnnots[$n] as $key => $pl) {
5384  $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
5385  $a = $pl['x'] * $this->k;
5386  $b = $this->pagedim[$n]['h'] - ($pl['y'] * $this->k);
5387  $c = $pl['w'] * $this->k;
5388  $d = $pl['h'] * $this->k;
5389  $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d);
5390  $annots .= "\n";
5391  $annots .= '<</Type /Annot';
5392  $annots .= ' /Subtype /'.$pl['opt']['subtype'];
5393  $annots .= ' /Rect ['.$rect.']';
5394  $annots .= ' /Contents '.$this->_textstring($pl['txt']);
5395  //$annots .= ' /P ';
5396  $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
5397  $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis'));
5398  if (isset($pl['opt']['f'])) {
5399  $val = 0;
5400  if (is_array($pl['opt']['f'])) {
5401  foreach ($pl['opt']['f'] as $f) {
5402  switch (strtolower($f)) {
5403  case 'invisible': {
5404  $val += 1 << 0;
5405  break;
5406  }
5407  case 'hidden': {
5408  $val += 1 << 1;
5409  break;
5410  }
5411  case 'print': {
5412  $val += 1 << 2;
5413  break;
5414  }
5415  case 'nozoom': {
5416  $val += 1 << 3;
5417  break;
5418  }
5419  case 'norotate': {
5420  $val += 1 << 4;
5421  break;
5422  }
5423  case 'noview': {
5424  $val += 1 << 5;
5425  break;
5426  }
5427  case 'readonly': {
5428  $val += 1 << 6;
5429  break;
5430  }
5431  case 'locked': {
5432  $val += 1 << 8;
5433  break;
5434  }
5435  case 'togglenoview': {
5436  $val += 1 << 9;
5437  break;
5438  }
5439  case 'lockedcontents': {
5440  $val += 1 << 10;
5441  break;
5442  }
5443  default: {
5444  break;
5445  }
5446  }
5447  }
5448  }
5449  $annots .= ' /F '.intval($val);
5450  }
5451  //$annots .= ' /AP ';
5452  //$annots .= ' /AS ';
5453  $annots .= ' /Border [';
5454  if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
5455  $annots .= intval($pl['opt']['border'][0]).' ';
5456  $annots .= intval($pl['opt']['border'][1]).' ';
5457  $annots .= intval($pl['opt']['border'][2]);
5458  if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
5459  $annots .= ' [';
5460  foreach ($pl['opt']['border'][3] as $dash) {
5461  $annots .= intval($dash).' ';
5462  }
5463  $annots .= ']';
5464  }
5465  } else {
5466  $annots .= '0 0 0';
5467  }
5468  $annots .= ']';
5469  if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
5470  $annots .= ' /BS <<Type /Border';
5471  if (isset($pl['opt']['bs']['w'])) {
5472  $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w']));
5473  }
5474  $bstyles = array('S', 'D', 'B', 'I', 'U');
5475  if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
5476  $annots .= ' /S /'.$pl['opt']['bs']['s'];
5477  }
5478  if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
5479  $annots .= ' /D [';
5480  foreach ($pl['opt']['bs']['d'] as $cord) {
5481  $cord = floatval($cord);
5482  $annots .= sprintf(" %.4F", $cord);
5483  }
5484  $annots .= ']';
5485  }
5486  $annots .= '>> ';
5487  }
5488  if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
5489  $annots .= ' /BE <<';
5490  $bstyles = array('S', 'C');
5491  if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
5492  $annots .= ' /S /'.$pl['opt']['bs']['s'];
5493  } else {
5494  $annots .= ' /S /S';
5495  }
5496  if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
5497  $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
5498  }
5499  $annots .= '>>';
5500  }
5501  $annots .= ' /C [';
5502  if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
5503  foreach ($pl['opt']['c'] as $col) {
5504  $col = intval($col);
5505  $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
5506  $annots .= sprintf(" %.4F", $color);
5507  }
5508  }
5509  $annots .= ']';
5510  //$annots .= ' /StructParent ';
5511  //$annots .= ' /OC ';
5512  $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
5513  if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
5514  // this is a markup type
5515  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
5516  $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
5517  }
5518  //$annots .= ' /Popup ';
5519  if (isset($pl['opt']['ca'])) {
5520  $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
5521  }
5522  if (isset($pl['opt']['rc'])) {
5523  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5524  }
5525  $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis'));
5526  //$annots .= ' /IRT ';
5527  if (isset($pl['opt']['subj'])) {
5528  $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
5529  }
5530  //$annots .= ' /RT ';
5531  //$annots .= ' /IT ';
5532  //$annots .= ' /ExData ';
5533  }
5534  switch (strtolower($pl['opt']['subtype'])) {
5535  case 'text': {
5536  if (isset($pl['opt']['open'])) {
5537  $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
5538  }
5539  $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
5540  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5541  $annots .= ' /Name /'.$pl['opt']['name'];
5542  } else {
5543  $annots .= ' /Name /Note';
5544  }
5545  $statemodels = array('Marked', 'Review');
5546  if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
5547  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5548  } else {
5549  $pl['opt']['statemodel'] = 'Marked';
5550  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5551  }
5552  if ($pl['opt']['statemodel'] == 'Marked') {
5553  $states = array('Accepted', 'Unmarked');
5554  } else {
5555  $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
5556  }
5557  if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
5558  $annots .= ' /State /'.$pl['opt']['state'];
5559  } else {
5560  if ($pl['opt']['statemodel'] == 'Marked') {
5561  $annots .= ' /State /Unmarked';
5562  } else {
5563  $annots .= ' /State /None';
5564  }
5565  }
5566  break;
5567  }
5568  case 'link': {
5569  if(is_string($pl['txt'])) {
5570  // external URI link
5571  $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt'])).'>>';
5572  } else {
5573  // internal link
5574  $l = $this->links[$pl['txt']];
5575  $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
5576  }
5577  $hmodes = array('N', 'I', 'O', 'P');
5578  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
5579  $annots .= ' /H /'.$pl['opt']['h'];
5580  } else {
5581  $annots .= ' /H /I';
5582  }
5583  //$annots .= ' /PA ';
5584  //$annots .= ' /Quadpoints ';
5585  break;
5586  }
5587  case 'freetext': {
5588  $annots .= ' /DA '.$this->_textstring($pl['txt']);
5589  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
5590  $annots .= ' /Q '.intval($pl['opt']['q']);
5591  }
5592  if (isset($pl['opt']['rc'])) {
5593  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5594  }
5595  if (isset($pl['opt']['ds'])) {
5596  $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
5597  }
5598  if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
5599  $annots .= ' /CL [';
5600  foreach ($pl['opt']['cl'] as $cl) {
5601  $annots .= sprintf("%.4F ", $cl * $this->k);
5602  }
5603  $annots .= ']';
5604  }
5605  $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
5606  if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
5607  $annots .= ' /IT '.$pl['opt']['it'];
5608  }
5609  if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
5610  $l = $pl['opt']['rd'][0] * $this->k;
5611  $r = $pl['opt']['rd'][1] * $this->k;
5612  $t = $pl['opt']['rd'][2] * $this->k;
5613  $b = $pl['opt']['rd'][3] * $this->k;
5614  $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
5615  }
5616  //$annots .= ' /LE ';
5617  break;
5618  }
5619  // ... to be completed ...
5620  case 'line': {
5621  break;
5622  }
5623  case 'square': {
5624  break;
5625  }
5626  case 'circle': {
5627  break;
5628  }
5629  case 'polygon': {
5630  break;
5631  }
5632  case 'polyline': {
5633  break;
5634  }
5635  case 'highlight': {
5636  break;
5637  }
5638  case 'underline': {
5639  break;
5640  }
5641  case 'squiggly': {
5642  break;
5643  }
5644  case 'strikeout': {
5645  break;
5646  }
5647  case 'stamp': {
5648  break;
5649  }
5650  case 'caret': {
5651  break;
5652  }
5653  case 'ink': {
5654  break;
5655  }
5656  case 'popup': {
5657  break;
5658  }
5659  case 'fileattachment': {
5660  if (!isset($pl['opt']['fs'])) {
5661  break;
5662  }
5663  $filename = basename($pl['opt']['fs']);
5664  if (isset($this->embeddedfiles[$filename]['n'])) {
5665  $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
5666  $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
5667  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5668  $annots .= ' /Name /'.$pl['opt']['name'];
5669  } else {
5670  $annots .= ' /Name /PushPin';
5671  }
5672  }
5673  break;
5674  }
5675  case 'sound': {
5676  if (!isset($pl['opt']['sound'])) {
5677  break;
5678  }
5679  $filename = basename($pl['opt']['sound']);
5680  if (isset($this->embeddedfiles[$filename]['n'])) {
5681  // ... TO BE COMPLETED ...
5682  $iconsapp = array('Speaker', 'Mic');
5683  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5684  $annots .= ' /Name /'.$pl['opt']['name'];
5685  } else {
5686  $annots .= ' /Name /Speaker';
5687  }
5688  }
5689  break;
5690  }
5691  case 'movie': {
5692  break;
5693  }
5694  case 'widget': {
5695  // PDF32000_2008.pdf page 408 (... TO BE COMPLETED ...)
5696  $hmode = array('N', 'I', 'O', 'P', 'T');
5697  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
5698  $annots .= ' /H /'.$pl['opt']['h'];
5699  }
5700  if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) {
5701  $annots .= ' /MK <<';
5702  // ... TO BE COMPLETED ...
5703  $annots .= '>>';
5704  }
5705  break;
5706  }
5707  case 'screen': {
5708  break;
5709  }
5710  case 'printermark': {
5711  break;
5712  }
5713  case 'trapnet': {
5714  break;
5715  }
5716  case 'watermark': {
5717  break;
5718  }
5719  case '3d': {
5720  break;
5721  }
5722  default: {
5723  break;
5724  }
5725  }
5726  $annots .= '>>';
5727  }
5728  $annots .= "\n]";
5729  $this->_out($annots);
5730  }
5731  }
5732 
5737  protected function _putfonts() {
5738  $nf = $this->n;
5739  foreach ($this->diffs as $diff) {
5740  //Encodings
5741  $this->_newobj();
5742  $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
5743  $this->_out('endobj');
5744  }
5745  $mqr = get_magic_quotes_runtime();
5746  set_magic_quotes_runtime(0);
5747  foreach ($this->FontFiles as $file => $info) {
5748  // search and get font file to embedd
5749  $fontdir = $info['fontdir'];
5750  $file = strtolower($file);
5751  $fontfile = '';
5752  // search files on various directories
5753  if (file_exists($fontdir.$file)) {
5754  $fontfile = $fontdir.$file;
5755  } elseif (file_exists($this->_getfontpath().$file)) {
5756  $fontfile = $this->_getfontpath().$file;
5757  } elseif (file_exists($file)) {
5758  $fontfile = $file;
5759  }
5760  if (!$this->empty_string($fontfile)) {
5761  $font = file_get_contents($fontfile);
5762  $compressed = (substr($file, -2) == '.z');
5763  if ((!$compressed) AND (isset($info['length2']))) {
5764  $header = (ord($font{0}) == 128);
5765  if ($header) {
5766  //Strip first binary header
5767  $font = substr($font, 6);
5768  }
5769  if ($header AND (ord($font{$info['length1']}) == 128)) {
5770  //Strip second binary header
5771  $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
5772  }
5773  }
5774  $this->_newobj();
5775  $this->FontFiles[$file]['n'] = $this->n;
5776  $this->_out('<</Length '.strlen($font));
5777  if ($compressed) {
5778  $this->_out('/Filter /FlateDecode');
5779  }
5780  $this->_out('/Length1 '.$info['length1']);
5781  if (isset($info['length2'])) {
5782  $this->_out('/Length2 '.$info['length2'].' /Length3 0');
5783  }
5784  $this->_out('>>');
5785  $this->_putstream($font);
5786  $this->_out('endobj');
5787  }
5788  }
5789  set_magic_quotes_runtime($mqr);
5790  foreach ($this->fontkeys as $k) {
5791  //Font objects
5792  $this->setFontSubBuffer($k, 'n', $this->n + 1);
5793  $font = $this->getFontBuffer($k);
5794  $type = $font['type'];
5795  $name = $font['name'];
5796  if ($type == 'core') {
5797  //Standard font
5798  $this->_newobj();
5799  $this->_out('<</Type /Font');
5800  $this->_out('/BaseFont /'.$name);
5801  $this->_out('/Subtype /Type1');
5802  if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
5803  $this->_out('/Encoding /WinAnsiEncoding');
5804  }
5805  $this->_out('>>');
5806  $this->_out('endobj');
5807  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
5808  //Additional Type1 or TrueType font
5809  $this->_newobj();
5810  $this->_out('<</Type /Font');
5811  $this->_out('/BaseFont /'.$name);
5812  $this->_out('/Subtype /'.$type);
5813  $this->_out('/FirstChar 32 /LastChar 255');
5814  $this->_out('/Widths '.($this->n + 1).' 0 R');
5815  $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
5816  if ($font['enc']) {
5817  if (isset($font['diff'])) {
5818  $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
5819  } else {
5820  $this->_out('/Encoding /WinAnsiEncoding');
5821  }
5822  }
5823  $this->_out('>>');
5824  $this->_out('endobj');
5825  // Widths
5826  $this->_newobj();
5827  $cw = &$font['cw'];
5828  $s = '[';
5829  for ($i = 32; $i < 256; ++$i) {
5830  $s .= $cw[$i].' ';
5831  }
5832  $this->_out($s.']');
5833  $this->_out('endobj');
5834  //Descriptor
5835  $this->_newobj();
5836  $s = '<</Type /FontDescriptor /FontName /'.$name;
5837  foreach ($font['desc'] as $k => $v) {
5838  $s .= ' /'.$k.' '.$v.'';
5839  }
5840  if (!$this->empty_string($font['file'])) {
5841  $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
5842  }
5843  $this->_out($s.'>>');
5844  $this->_out('endobj');
5845  } else {
5846  //Allow for additional types
5847  $mtd = '_put'.strtolower($type);
5848  if (!method_exists($this, $mtd)) {
5849  $this->Error('Unsupported font type: '.$type);
5850  }
5851  $this->$mtd($font);
5852  }
5853  }
5854  }
5855 
5864  protected function _putfontwidths($font, $cidoffset=0) {
5865  ksort($font['cw']);
5866  $rangeid = 0;
5867  $range = array();
5868  $prevcid = -2;
5869  $prevwidth = -1;
5870  $interval = false;
5871  // for each character
5872  foreach ($font['cw'] as $cid => $width) {
5873  $cid -= $cidoffset;
5874  if ($width != $font['dw']) {
5875  if ($cid == ($prevcid + 1)) {
5876  // consecutive CID
5877  if ($width == $prevwidth) {
5878  if ($width == $range[$rangeid][0]) {
5879  $range[$rangeid][] = $width;
5880  } else {
5881  array_pop($range[$rangeid]);
5882  // new range
5883  $rangeid = $prevcid;
5884  $range[$rangeid] = array();
5885  $range[$rangeid][] = $prevwidth;
5886  $range[$rangeid][] = $width;
5887  }
5888  $interval = true;
5889  $range[$rangeid]['interval'] = true;
5890  } else {
5891  if ($interval) {
5892  // new range
5893  $rangeid = $cid;
5894  $range[$rangeid] = array();
5895  $range[$rangeid][] = $width;
5896  } else {
5897  $range[$rangeid][] = $width;
5898  }
5899  $interval = false;
5900  }
5901  } else {
5902  // new range
5903  $rangeid = $cid;
5904  $range[$rangeid] = array();
5905  $range[$rangeid][] = $width;
5906  $interval = false;
5907  }
5908  $prevcid = $cid;
5909  $prevwidth = $width;
5910  }
5911  }
5912  // optimize ranges
5913  $prevk = -1;
5914  $nextk = -1;
5915  $prevint = false;
5916  foreach ($range as $k => $ws) {
5917  $cws = count($ws);
5918  if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
5919  if (isset($range[$k]['interval'])) {
5920  unset($range[$k]['interval']);
5921  }
5922  $range[$prevk] = array_merge($range[$prevk], $range[$k]);
5923  unset($range[$k]);
5924  } else {
5925  $prevk = $k;
5926  }
5927  $nextk = $k + $cws;
5928  if (isset($ws['interval'])) {
5929  if ($cws > 3) {
5930  $prevint = true;
5931  } else {
5932  $prevint = false;
5933  }
5934  unset($range[$k]['interval']);
5935  --$nextk;
5936  } else {
5937  $prevint = false;
5938  }
5939  }
5940  // output data
5941  $w = '';
5942  foreach ($range as $k => $ws) {
5943  if (count(array_count_values($ws)) == 1) {
5944  // interval mode is more compact
5945  $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
5946  } else {
5947  // range mode
5948  $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
5949  }
5950  }
5951  $this->_out('/W ['.$w.' ]');
5952  }
5953 
5962  protected function _puttruetypeunicode($font) {
5963  // Type0 Font
5964  // A composite font composed of other fonts, organized hierarchically
5965  $this->_newobj();
5966  $this->_out('<</Type /Font');
5967  $this->_out('/Subtype /Type0');
5968  $this->_out('/BaseFont /'.$font['name'].'');
5969  $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values.
5970  $this->_out('/ToUnicode /Identity-H');
5971  $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5972  $this->_out('>>');
5973  $this->_out('endobj');
5974  // CIDFontType2
5975  // A CIDFont whose glyph descriptions are based on TrueType font technology
5976  $this->_newobj();
5977  $this->_out('<</Type /Font');
5978  $this->_out('/Subtype /CIDFontType2');
5979  $this->_out('/BaseFont /'.$font['name'].'');
5980  // A dictionary containing entries that define the character collection of the CIDFont.
5981  $cidinfo = '/Registry '.$this->_datastring('Adobe');
5982  $cidinfo .= ' /Ordering '.$this->_datastring('Identity');
5983  $cidinfo .= ' /Supplement 0';
5984  $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
5985  $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
5986  $this->_out('/DW '.$font['dw'].''); // default width
5987  $this->_putfontwidths($font, 0);
5988  $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
5989  $this->_out('>>');
5990  $this->_out('endobj');
5991  // Font descriptor
5992  // A font descriptor describing the CIDFont default metrics other than its glyph widths
5993  $this->_newobj();
5994  $this->_out('<</Type /FontDescriptor');
5995  $this->_out('/FontName /'.$font['name']);
5996  foreach ($font['desc'] as $key => $value) {
5997  $this->_out('/'.$key.' '.$value);
5998  }
5999  $fontdir = '';
6000  if (!$this->empty_string($font['file'])) {
6001  // A stream containing a TrueType font
6002  $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
6003  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
6004  }
6005  $this->_out('>>');
6006  $this->_out('endobj');
6007  $this->_newobj();
6008  if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
6009  // Embed CIDToGIDMap
6010  // A specification of the mapping from CIDs to glyph indices
6011  // search and get CTG font file to embedd
6012  $ctgfile = strtolower($font['ctg']);
6013  // search and get ctg font file to embedd
6014  $fontfile = '';
6015  // search files on various directories
6016  if (file_exists($fontdir.$ctgfile)) {
6017  $fontfile = $fontdir.$ctgfile;
6018  } elseif (file_exists($this->_getfontpath().$ctgfile)) {
6019  $fontfile = $this->_getfontpath().$ctgfile;
6020  } elseif (file_exists($ctgfile)) {
6021  $fontfile = $ctgfile;
6022  }
6023  if ($this->empty_string($fontfile)) {
6024  $this->Error('Font file not found: '.$ctgfile);
6025  }
6026  $size = filesize($fontfile);
6027  $this->_out('<</Length '.$size.'');
6028  if (substr($fontfile, -2) == '.z') { // check file extension
6029  // Decompresses data encoded using the public-domain
6030  // zlib/deflate compression method, reproducing the
6031  // original text or binary data
6032  $this->_out('/Filter /FlateDecode');
6033  }
6034  $this->_out('>>');
6035  $this->_putstream(file_get_contents($fontfile));
6036  }
6037  $this->_out('endobj');
6038  }
6039 
6047  protected function _putcidfont0($font) {
6048  $cidoffset = 31;
6049  if (isset($font['cidinfo']['uni2cid'])) {
6050  // convert unicode to cid.
6051  $uni2cid = $font['cidinfo']['uni2cid'];
6052  $cw = array();
6053  foreach ($font['cw'] as $uni => $width) {
6054  if (isset($uni2cid[$uni])) {
6055  $cw[($uni2cid[$uni] + $cidoffset)] = $width;
6056  } elseif ($uni < 256) {
6057  $cw[$uni] = $width;
6058  } // else unknown character
6059  }
6060  $font = array_merge($font, array('cw' => $cw));
6061  }
6062  $name = $font['name'];
6063  $enc = $font['enc'];
6064  if ($enc) {
6065  $longname = $name.'-'.$enc;
6066  } else {
6067  $longname = $name;
6068  }
6069  $this->_newobj();
6070  $this->_out('<</Type /Font');
6071  $this->_out('/BaseFont /'.$longname);
6072  $this->_out('/Subtype /Type0');
6073  if ($enc) {
6074  $this->_out('/Encoding /'.$enc);
6075  }
6076  $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
6077  $this->_out('>>');
6078  $this->_out('endobj');
6079  $this->_newobj();
6080  $this->_out('<</Type /Font');
6081  $this->_out('/BaseFont /'.$name);
6082  $this->_out('/Subtype /CIDFontType0');
6083  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
6084  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
6085  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
6086  $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
6087  $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
6088  $this->_out('/DW '.$font['dw']);
6089  $this->_putfontwidths($font, $cidoffset);
6090  $this->_out('>>');
6091  $this->_out('endobj');
6092  $this->_newobj();
6093  $s = '<</Type /FontDescriptor /FontName /'.$name;
6094  foreach ($font['desc'] as $k => $v) {
6095  if ($k != 'Style') {
6096  $s .= ' /'.$k.' '.$v.'';
6097  }
6098  }
6099  $this->_out($s.'>>');
6100  $this->_out('endobj');
6101  }
6102 
6107  protected function _putimages() {
6108  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
6109  foreach ($this->imagekeys as $file) {
6110  $info = $this->getImageBuffer($file);
6111  $this->_newobj();
6112  $this->setImageSubBuffer($file, 'n', $this->n);
6113  $this->_out('<</Type /XObject');
6114  $this->_out('/Subtype /Image');
6115  $this->_out('/Width '.$info['w']);
6116  $this->_out('/Height '.$info['h']);
6117  if (isset($info['masked'])) {
6118  $this->_out('/SMask '.($this->n - 1).' 0 R');
6119  }
6120  if ($info['cs'] == 'Indexed') {
6121  $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
6122  } else {
6123  $this->_out('/ColorSpace /'.$info['cs']);
6124  if ($info['cs'] == 'DeviceCMYK') {
6125  $this->_out('/Decode [1 0 1 0 1 0 1 0]');
6126  }
6127  }
6128  $this->_out('/BitsPerComponent '.$info['bpc']);
6129  if (isset($info['f'])) {
6130  $this->_out('/Filter /'.$info['f']);
6131  }
6132  if (isset($info['parms'])) {
6133  $this->_out($info['parms']);
6134  }
6135  if (isset($info['trns']) AND is_array($info['trns'])) {
6136  $trns='';
6137  $count_info = count($info['trns']);
6138  for ($i=0; $i < $count_info; ++$i) {
6139  $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
6140  }
6141  $this->_out('/Mask ['.$trns.']');
6142  }
6143  $this->_out('/Length '.strlen($info['data']).'>>');
6144  $this->_putstream($info['data']);
6145  $this->_out('endobj');
6146  //Palette
6147  if ($info['cs'] == 'Indexed') {
6148  $this->_newobj();
6149  $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
6150  $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
6151  $this->_putstream($pal);
6152  $this->_out('endobj');
6153  }
6154  }
6155  }
6156 
6162  protected function _putspotcolors() {
6163  foreach ($this->spot_colors as $name => $color) {
6164  $this->_newobj();
6165  $this->spot_colors[$name]['n'] = $this->n;
6166  $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
6167  $this->_out('/DeviceCMYK <<');
6168  $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
6169  $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
6170  $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
6171  $this->_out('endobj');
6172  }
6173  }
6174 
6179  protected function _putxobjectdict() {
6180  foreach ($this->imagekeys as $file) {
6181  $info = $this->getImageBuffer($file);
6182  $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
6183  }
6184  }
6185 
6190  protected function _putresourcedict() {
6191  $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
6192  $this->_out('/Font <<');
6193  foreach ($this->fontkeys as $fontkey) {
6194  $font = $this->getFontBuffer($fontkey);
6195  $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
6196  }
6197  $this->_out('>>');
6198  $this->_out('/XObject <<');
6199  $this->_putxobjectdict();
6200  $this->_out('>>');
6201  // visibility
6202  $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
6203  // transparency
6204  $this->_out('/ExtGState <<');
6205  foreach ($this->extgstates as $k => $extgstate) {
6206  $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
6207  }
6208  $this->_out('>>');
6209  // gradients
6210  if (isset($this->gradients) AND (count($this->gradients) > 0)) {
6211  $this->_out('/Shading <<');
6212  foreach ($this->gradients as $id => $grad) {
6213  $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
6214  }
6215  $this->_out('>>');
6216  }
6217  // spot colors
6218  if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
6219  $this->_out('/ColorSpace <<');
6220  foreach ($this->spot_colors as $color) {
6221  $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
6222  }
6223  $this->_out('>>');
6224  }
6225  }
6226 
6231  protected function _putresources() {
6232  $this->_putextgstates();
6233  $this->_putocg();
6234  $this->_putfonts();
6235  $this->_putimages();
6236  $this->_putspotcolors();
6237  $this->_putshaders();
6238  //Resource dictionary
6239  $this->offsets[2] = $this->bufferlen;
6240  $this->_out('2 0 obj');
6241  $this->_out('<<');
6242  $this->_putresourcedict();
6243  $this->_out('>>');
6244  $this->_out('endobj');
6245  $this->_putjavascript();
6246  $this->_putbookmarks();
6247  $this->_putEmbeddedFiles();
6248  // encryption
6249  if ($this->encrypted) {
6250  $this->_newobj();
6251  $this->enc_obj_id = $this->n;
6252  $this->_out('<<');
6253  $this->_putencryption();
6254  $this->_out('>>');
6255  $this->_out('endobj');
6256  }
6257  }
6258 
6264  protected function _putinfo() {
6265  if ($this->empty_string($this->title)) {
6266  $this->title = '?';
6267  }
6268  $this->_out('/Title '.$this->_textstring($this->title));
6269  if ($this->empty_string($this->author)) {
6270  $this->author = '?';
6271  }
6272  $this->_out('/Author '.$this->_textstring($this->author));
6273  if ($this->empty_string($this->subject)) {
6274  $this->subject = '?';
6275  }
6276  $this->_out('/Subject '.$this->_textstring($this->subject));
6277  if ($this->empty_string($this->keywords)) {
6278  $this->keywords = '?';
6279  }
6280  $this->_out('/Keywords '.$this->_textstring($this->keywords));
6281  if ($this->empty_string($this->creator)) {
6282  $this->creator = '?';
6283  }
6284  $this->_out('/Creator '.$this->_textstring($this->creator));
6285  if (defined('PDF_PRODUCER')) {
6286  $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
6287  } else {
6288  $this->_out('/Producer '.$this->_textstring('TCPDF'));
6289  }
6290  $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis')));
6291  $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis')));
6292  }
6293 
6298  protected function _putcatalog() {
6299  $this->_out('/Type /Catalog');
6300  $this->_out('/Pages 1 0 R');
6301  if ($this->ZoomMode == 'fullpage') {
6302  $this->_out('/OpenAction [3 0 R /Fit]');
6303  } elseif ($this->ZoomMode == 'fullwidth') {
6304  $this->_out('/OpenAction [3 0 R /FitH null]');
6305  } elseif ($this->ZoomMode == 'real') {
6306  $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
6307  } elseif (!is_string($this->ZoomMode)) {
6308  $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
6309  }
6310  if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
6311  $this->_out('/PageLayout /'.$this->LayoutMode.'');
6312  }
6313  if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
6314  $this->_out('/PageMode /'.$this->PageMode);
6315  }
6316  if (isset($this->l['a_meta_language'])) {
6317  $this->_out('/Lang /'.$this->l['a_meta_language']);
6318  }
6319  $this->_out('/Names <<');
6320  if (!$this->empty_string($this->javascript)) {
6321  $this->_out('/JavaScript '.($this->n_js).' 0 R');
6322  }
6323  $this->_out('>>');
6324  if (count($this->outlines) > 0) {
6325  $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
6326  $this->_out('/PageMode /UseOutlines');
6327  }
6328  $this->_putviewerpreferences();
6329  $p = $this->n_ocg_print.' 0 R';
6330  $v = $this->n_ocg_view.' 0 R';
6331  $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
6332  $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
6333  // signatures
6334  if ($this->sign AND isset($this->signature_data['cert_type'])) {
6335  if ($this->signature_data['cert_type'] > 0) {
6336  $this->_out('/AcroForm<<');
6337  $this->_out('/Fields ['.$this->sig_obj_id.' 0 R]');
6338  $this->_out('/NeedAppearances false');
6339  $this->_out('/SigFlags 3');
6340  $this->_out('>>');
6341  $this->_out('/Perms<</DocMDP '.($this->sig_obj_id + 1).' 0 R>>');
6342  } else {
6343  $this->_out('/Perms<</UR3 '.($this->sig_obj_id + 1).' 0 R>>');
6344  }
6345  }
6346  }
6347 
6354  protected function _putviewerpreferences() {
6355  $this->_out('/ViewerPreferences<<');
6356  if ($this->rtl) {
6357  $this->_out('/Direction /R2L');
6358  } else {
6359  $this->_out('/Direction /L2R');
6360  }
6361  if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
6362  $this->_out('/HideToolbar true');
6363  }
6364  if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
6365  $this->_out('/HideMenubar true');
6366  }
6367  if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
6368  $this->_out('/HideWindowUI true');
6369  }
6370  if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
6371  $this->_out('/FitWindow true');
6372  }
6373  if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
6374  $this->_out('/CenterWindow true');
6375  }
6376  if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
6377  $this->_out('/DisplayDocTitle true');
6378  }
6379  if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
6380  $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
6381  }
6382  if (isset($this->viewer_preferences['ViewArea'])) {
6383  $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
6384  }
6385  if (isset($this->viewer_preferences['ViewClip'])) {
6386  $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
6387  }
6388  if (isset($this->viewer_preferences['PrintArea'])) {
6389  $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
6390  }
6391  if (isset($this->viewer_preferences['PrintClip'])) {
6392  $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
6393  }
6394  if (isset($this->viewer_preferences['PrintScaling'])) {
6395  $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
6396  }
6397  if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
6398  $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
6399  }
6400  if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
6401  if ($this->viewer_preferences['PickTrayByPDFSize']) {
6402  $this->_out('/PickTrayByPDFSize true');
6403  } else {
6404  $this->_out('/PickTrayByPDFSize false');
6405  }
6406  }
6407  if (isset($this->viewer_preferences['PrintPageRange'])) {
6408  $PrintPageRangeNum = '';
6409  foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
6410  $PrintPageRangeNum .= ' '.($v - 1).'';
6411  }
6412  $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
6413  }
6414  if (isset($this->viewer_preferences['NumCopies'])) {
6415  $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
6416  }
6417  $this->_out('>>');
6418  }
6419 
6424  protected function _puttrailer() {
6425  $this->_out('/Size '.($this->n + 1));
6426  $this->_out('/Root '.$this->n.' 0 R');
6427  $this->_out('/Info '.($this->n - 1).' 0 R');
6428  if ($this->encrypted) {
6429  $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
6430  $this->_out('/ID [()()]');
6431  }
6432  }
6433 
6438  protected function _putheader() {
6439  $this->_out('%PDF-'.$this->PDFVersion);
6440  }
6441 
6446  protected function _enddoc() {
6447  $this->state = 1;
6448  $this->_putheader();
6449  $this->_putpages();
6450  $this->_putresources();
6451  // Signature
6452  if ($this->sign AND isset($this->signature_data['cert_type'])) {
6453  // widget annotation
6454  $this->sig_obj_id = $this->_newobj();
6455  $this->_out('<<');
6456  $this->_out('/FT /Sig');
6457  $this->_out('/T '.$this->_textstring('SIGNATURE'));
6458  $this->_out('/Ff 0');
6459  $this->_out('/V '.($this->sig_obj_id + 1).' 0 R');
6460  $this->_out('>>');
6461  $this->_out('endobj');
6462  // signature
6463  $this->_newobj();
6464  $this->_out('<<');
6465  $this->_putsignature();
6466  $this->_putursignature();
6467  $this->_out('>>');
6468  $this->_out('endobj');
6469  }
6470  // Info
6471  $this->_newobj();
6472  $this->_out('<<');
6473  $this->_putinfo();
6474  $this->_out('>>');
6475  $this->_out('endobj');
6476  // Catalog
6477  $this->_newobj();
6478  $this->_out('<<');
6479  $this->_putcatalog();
6480  $this->_out('>>');
6481  $this->_out('endobj');
6482  // Cross-ref
6483  $o = $this->bufferlen;
6484  $this->_out('xref');
6485  $this->_out('0 '.($this->n + 1));
6486  $this->_out('0000000000 65535 f ');
6487  for ($i=1; $i <= $this->n; ++$i) {
6488  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
6489  }
6490  if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
6491  $this->_out('100000 '.count($this->embeddedfiles));
6492  foreach ($this->embeddedfiles as $filename => $filedata) {
6493  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
6494  }
6495  }
6496  //Trailer
6497  $this->_out('trailer');
6498  $this->_out('<<');
6499  $this->_puttrailer();
6500  $this->_out('>>');
6501  $this->_out('startxref');
6502  $this->_out($o);
6503  $this->_out('%%EOF');
6504  $this->state = 3; // end-of-doc
6505  if ($this->diskcache) {
6506  // remove temporary files used for images
6507  foreach ($this->imagekeys as $key) {
6508  // remove temporary files
6509  unlink($this->images[$key]);
6510  }
6511  foreach ($this->fontkeys as $key) {
6512  // remove temporary files
6513  unlink($this->fonts[$key]);
6514  }
6515  }
6516  }
6517 
6524  protected function _beginpage($orientation='', $format='') {
6525  ++$this->page;
6526  $this->setPageBuffer($this->page, '');
6527  // initialize array for graphics tranformation positions inside a page buffer
6528  $this->transfmrk[$this->page] = array();
6529  $this->state = 2;
6530  if ($this->empty_string($orientation)) {
6531  if (isset($this->CurOrientation)) {
6532  $orientation = $this->CurOrientation;
6533  } else {
6534  $orientation = 'P';
6535  }
6536  }
6537  if ($this->empty_string($format)) {
6538  $this->setPageOrientation($orientation);
6539  } else {
6540  $this->setPageFormat($format, $orientation);
6541  }
6542  if ($this->rtl) {
6543  $this->x = $this->w - $this->rMargin;
6544  } else {
6545  $this->x = $this->lMargin;
6546  }
6547  $this->y = $this->tMargin;
6548  if (isset($this->newpagegroup[$this->page])) {
6549  // start a new group
6550  $n = sizeof($this->pagegroups) + 1;
6551  $alias = '{nb'.$n.'}';
6552  $this->pagegroups[$alias] = 1;
6553  $this->currpagegroup = $alias;
6554  } elseif ($this->currpagegroup) {
6555  ++$this->pagegroups[$this->currpagegroup];
6556  }
6557  }
6558 
6563  protected function _endpage() {
6564  $this->setVisibility('all');
6565  $this->state = 1;
6566  }
6567 
6573  protected function _newobj() {
6574  ++$this->n;
6575  $this->offsets[$this->n] = $this->bufferlen;
6576  $this->_out($this->n.' 0 obj');
6577  return $this->n;
6578  }
6579 
6587  protected function _dounderline($x, $y, $txt) {
6588  $up = $this->CurrentFont['up'];
6589  $ut = $this->CurrentFont['ut'];
6590  $w = $this->GetStringWidth($txt);
6591  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);
6592  }
6593 
6601  protected function _dolinethrough($x, $y, $txt) {
6602  $up = $this->CurrentFont['up'];
6603  $ut = $this->CurrentFont['ut'];
6604  $w = $this->GetStringWidth($txt);
6605  return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - ($this->FontSize/2) - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
6606  }
6607 
6614  protected function _freadint($f) {
6615  $a = unpack('Ni', fread($f, 4));
6616  return $a['i'];
6617  }
6618 
6625  protected function _escape($s) {
6626  // the chr(13) substitution fixes the Bugs item #1421290.
6627  return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
6628  }
6629 
6636  protected function _datastring($s) {
6637  if ($this->encrypted) {
6638  $s = $this->_RC4($this->_objectkey($this->n), $s);
6639  }
6640  return '('. $this->_escape($s).')';
6641  }
6642 
6649  protected function _textstring($s) {
6650  if ($this->isunicode) {
6651  //Convert string to UTF-16BE
6652  $s = $this->UTF8ToUTF16BE($s, true);
6653  }
6654  return $this->_datastring($s);
6655  }
6656 
6663  protected function _escapetext($s) {
6664  if ($this->isunicode) {
6665  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
6666  $s = $this->UTF8ToLatin1($s);
6667  } else {
6668  //Convert string to UTF-16BE and reverse RTL language
6669  $s = $this->utf8StrRev($s, false, $this->tmprtl);
6670  }
6671  }
6672  return $this->_escape($s);
6673  }
6674 
6680  protected function _putstream($s) {
6681  if ($this->encrypted) {
6682  $s = $this->_RC4($this->_objectkey($this->n), $s);
6683  }
6684  $this->_out('stream');
6685  $this->_out($s);
6686  $this->_out('endstream');
6687  }
6688 
6694  protected function _out($s) {
6695  if ($this->state == 2) {
6696  if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
6697  // puts data before page footer
6698  $pagebuff = $this->getPageBuffer($this->page);
6699  $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
6700  $footer = substr($pagebuff, -$this->footerlen[$this->page]);
6701  $this->setPageBuffer($this->page, $page.$s."\n".$footer);
6702  // update footer position
6703  $this->footerpos[$this->page] += strlen($s."\n");
6704  } else {
6705  $this->setPageBuffer($this->page, $s."\n", true);
6706  }
6707  } else {
6708  $this->setBuffer($s."\n");
6709  }
6710  }
6711 
6746  protected function UTF8StringToArray($str) {
6747  if (isset($this->cache_UTF8StringToArray['_'.$str])) {
6748  // return cached value
6749  return($this->cache_UTF8StringToArray['_'.$str]);
6750  }
6751  // check cache size
6752  if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
6753  // remove first element
6754  array_shift($this->cache_UTF8StringToArray);
6755  }
6756  ++$this->cache_size_UTF8StringToArray;
6757  if (!$this->isunicode) {
6758  // split string into array of equivalent codes
6759  $strarr = array();
6760  $strlen = strlen($str);
6761  for ($i=0; $i < $strlen; ++$i) {
6762  $strarr[] = ord($str{$i});
6763  }
6764  // insert new value on cache
6765  $this->cache_UTF8StringToArray['_'.$str] = $strarr;
6766  return $strarr;
6767  }
6768  $unicode = array(); // array containing unicode values
6769  $bytes = array(); // array containing single character byte sequences
6770  $numbytes = 1; // number of octetc needed to represent the UTF-8 character
6771  $str .= ''; // force $str to be a string
6772  $length = strlen($str);
6773  for ($i = 0; $i < $length; ++$i) {
6774  $char = ord($str{$i}); // get one string character at time
6775  if (count($bytes) == 0) { // get starting octect
6776  if ($char <= 0x7F) {
6777  $unicode[] = $char; // use the character "as is" because is ASCII
6778  $numbytes = 1;
6779  } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
6780  $bytes[] = ($char - 0xC0) << 0x06;
6781  $numbytes = 2;
6782  } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
6783  $bytes[] = ($char - 0xE0) << 0x0C;
6784  $numbytes = 3;
6785  } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
6786  $bytes[] = ($char - 0xF0) << 0x12;
6787  $numbytes = 4;
6788  } else {
6789  // use replacement character for other invalid sequences
6790  $unicode[] = 0xFFFD;
6791  $bytes = array();
6792  $numbytes = 1;
6793  }
6794  } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
6795  $bytes[] = $char - 0x80;
6796  if (count($bytes) == $numbytes) {
6797  // compose UTF-8 bytes to a single unicode value
6798  $char = $bytes[0];
6799  for ($j = 1; $j < $numbytes; ++$j) {
6800  $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
6801  }
6802  if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
6803  /* The definition of UTF-8 prohibits encoding character numbers between
6804  U+D800 and U+DFFF, which are reserved for use with the UTF-16
6805  encoding form (as surrogate pairs) and do not directly represent
6806  characters. */
6807  $unicode[] = 0xFFFD; // use replacement character
6808  } else {
6809  $unicode[] = $char; // add char to array
6810  }
6811  // reset data for next char
6812  $bytes = array();
6813  $numbytes = 1;
6814  }
6815  } else {
6816  // use replacement character for other invalid sequences
6817  $unicode[] = 0xFFFD;
6818  $bytes = array();
6819  $numbytes = 1;
6820  }
6821  }
6822  // insert new value on cache
6823  $this->cache_UTF8StringToArray['_'.$str] = $unicode;
6824  return $unicode;
6825  }
6826 
6837  protected function UTF8ToUTF16BE($str, $setbom=true) {
6838  if (!$this->isunicode) {
6839  return $str; // string is not in unicode
6840  }
6841  $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6842  return $this->arrUTF8ToUTF16BE($unicode, $setbom);
6843  }
6844 
6853  protected function UTF8ToLatin1($str) {
6854  global $utf8tolatin;
6855  if (!$this->isunicode) {
6856  return $str; // string is not in unicode
6857  }
6858  $outstr = ''; // string to be returned
6859  $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6860  foreach ($unicode as $char) {
6861  if ($char < 256) {
6862  $outstr .= chr($char);
6863  } elseif (array_key_exists($char, $utf8tolatin)) {
6864  // map from UTF-8
6865  $outstr .= chr($utf8tolatin[$char]);
6866  } elseif ($char == 0xFFFD) {
6867  // skip
6868  } else {
6869  $outstr .= '?';
6870  }
6871  }
6872  return $outstr;
6873  }
6874 
6913  protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
6914  $outstr = ''; // string to be returned
6915  if ($setbom) {
6916  $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
6917  }
6918  foreach ($unicode as $char) {
6919  if ($char == 0xFFFD) {
6920  $outstr .= "\xFF\xFD"; // replacement character
6921  } elseif ($char < 0x10000) {
6922  $outstr .= chr($char >> 0x08);
6923  $outstr .= chr($char & 0xFF);
6924  } else {
6925  $char -= 0x10000;
6926  $w1 = 0xD800 | ($char >> 0x10);
6927  $w2 = 0xDC00 | ($char & 0x3FF);
6928  $outstr .= chr($w1 >> 0x08);
6929  $outstr .= chr($w1 & 0xFF);
6930  $outstr .= chr($w2 >> 0x08);
6931  $outstr .= chr($w2 & 0xFF);
6932  }
6933  }
6934  return $outstr;
6935  }
6936  // ====================================================
6937 
6944  public function setHeaderFont($font) {
6945  $this->header_font = $font;
6946  }
6947 
6954  public function getHeaderFont() {
6955  return $this->header_font;
6956  }
6957 
6964  public function setFooterFont($font) {
6965  $this->footer_font = $font;
6966  }
6967 
6974  public function getFooterFont() {
6975  return $this->footer_font;
6976  }
6977 
6984  public function setLanguageArray($language) {
6985  $this->l = $language;
6986  if (isset($this->l['a_meta_dir'])) {
6987  $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
6988  } else {
6989  $this->rtl = false;
6990  }
6991  }
6992 
6997  public function getPDFData() {
6998  if ($this->state < 3) {
6999  $this->Close();
7000  }
7001  return $this->buffer;
7002  }
7003 
7015  public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
7016  if (!$this->empty_string($url) AND ($url{0} == '#')) {
7017  // convert url to internal link
7018  $page = intval(substr($url, 1));
7019  $url = $this->AddLink();
7020  $this->SetLink($url, 0, $page);
7021  }
7022  // store current settings
7023  $prevcolor = $this->fgcolor;
7024  $prevstyle = $this->FontStyle;
7025  if (empty($color)) {
7026  $this->SetTextColorArray($this->htmlLinkColorArray);
7027  } else {
7028  $this->SetTextColorArray($color);
7029  }
7030  if ($style == -1) {
7031  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
7032  } else {
7033  $this->SetFont('', $this->FontStyle.$style);
7034  }
7035  $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
7036  // restore settings
7037  $this->SetFont('', $prevstyle);
7038  $this->SetTextColorArray($prevcolor);
7039  return $ret;
7040  }
7041 
7048  public function convertHTMLColorToDec($color='#FFFFFF') {
7049  global $webcolor;
7050  $returncolor = false;
7051  $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
7052  $color = strtolower($color);
7053  if (strlen($color) == 0) {
7054  return false;
7055  }
7056  if (substr($color, 0, 3) == 'rgb') {
7057  $codes = substr($color, 4);
7058  $codes = str_replace(')', '', $codes);
7059  $returncolor = explode(',', $codes, 3);
7060  return $returncolor;
7061  }
7062  if (substr($color, 0, 1) != '#') {
7063  // decode color name
7064  if (isset($webcolor[$color])) {
7065  $color_code = $webcolor[$color];
7066  } else {
7067  return false;
7068  }
7069  } else {
7070  $color_code = substr($color, 1);
7071  }
7072  switch (strlen($color_code)) {
7073  case 3: {
7074  // three-digit hexadecimal representation
7075  $r = substr($color_code, 0, 1);
7076  $g = substr($color_code, 1, 1);
7077  $b = substr($color_code, 2, 1);
7078  $returncolor['R'] = hexdec($r.$r);
7079  $returncolor['G'] = hexdec($g.$g);
7080  $returncolor['B'] = hexdec($b.$b);
7081  break;
7082  }
7083  case 6: {
7084  // six-digit hexadecimal representation
7085  $returncolor['R'] = hexdec(substr($color_code, 0, 2));
7086  $returncolor['G'] = hexdec(substr($color_code, 2, 2));
7087  $returncolor['B'] = hexdec(substr($color_code, 4, 2));
7088  break;
7089  }
7090  }
7091  return $returncolor;
7092  }
7093 
7101  public function pixelsToUnits($px) {
7102  return ($px / ($this->imgscale * $this->k));
7103  }
7104 
7112  public function unhtmlentities($text_to_convert) {
7113  return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
7114  }
7115 
7116  // ENCRYPTION METHODS ----------------------------------
7117  // SINCE 2.0.000 (2008-01-02)
7118 
7125  protected function _objectkey($n) {
7126  return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
7127  }
7128 
7134  protected function _putencryption() {
7135  $this->_out('/Filter /Standard');
7136  $this->_out('/V 1');
7137  $this->_out('/R 2');
7138  $this->_out('/O ('.$this->_escape($this->Ovalue).')');
7139  $this->_out('/U ('.$this->_escape($this->Uvalue).')');
7140  $this->_out('/P '.$this->Pvalue);
7141  }
7142 
7153  protected function _RC4($key, $text) {
7154  if ($this->last_rc4_key != $key) {
7155  $k = str_repeat($key, ((256 / strlen($key)) + 1));
7156  $rc4 = range(0, 255);
7157  $j = 0;
7158  for ($i = 0; $i < 256; ++$i) {
7159  $t = $rc4[$i];
7160  $j = ($j + $t + ord($k{$i})) % 256;
7161  $rc4[$i] = $rc4[$j];
7162  $rc4[$j] = $t;
7163  }
7164  $this->last_rc4_key = $key;
7165  $this->last_rc4_key_c = $rc4;
7166  } else {
7167  $rc4 = $this->last_rc4_key_c;
7168  }
7169  $len = strlen($text);
7170  $a = 0;
7171  $b = 0;
7172  $out = '';
7173  for ($i = 0; $i < $len; ++$i) {
7174  $a = ($a + 1) % 256;
7175  $t = $rc4[$a];
7176  $b = ($b + $t) % 256;
7177  $rc4[$a] = $rc4[$b];
7178  $rc4[$b] = $t;
7179  $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
7180  $out .= chr(ord($text{$i}) ^ $k);
7181  }
7182  return $out;
7183  }
7184 
7193  protected function _md5_16($str) {
7194  return pack('H*', md5($str));
7195  }
7196 
7206  protected function _Ovalue($user_pass, $owner_pass) {
7207  $tmp = $this->_md5_16($owner_pass);
7208  $owner_RC4_key = substr($tmp, 0, 5);
7209  return $this->_RC4($owner_RC4_key, $user_pass);
7210  }
7211 
7219  protected function _Uvalue() {
7220  return $this->_RC4($this->encryption_key, $this->padding);
7221  }
7222 
7232  protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
7233  // Pad passwords
7234  $user_pass = substr($user_pass.$this->padding, 0, 32);
7235  $owner_pass = substr($owner_pass.$this->padding, 0, 32);
7236  // Compute O value
7237  $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
7238  // Compute encyption key
7239  $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
7240  $this->encryption_key = substr($tmp, 0, 5);
7241  // Compute U value
7242  $this->Uvalue = $this->_Uvalue();
7243  // Compute P value
7244  $this->Pvalue = -(($protection^255) + 1);
7245  }
7246 
7264  public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
7265  $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
7266  $protection = 192;
7267  foreach ($permissions as $permission) {
7268  if (!isset($options[$permission])) {
7269  $this->Error('Incorrect permission: '.$permission);
7270  }
7271  $protection += $options[$permission];
7272  }
7273  if ($owner_pass === null) {
7274  $owner_pass = uniqid(rand());
7275  }
7276  $this->encrypted = true;
7277  $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
7278  }
7279 
7280  // END OF ENCRYPTION FUNCTIONS -------------------------
7281 
7282  // START TRANSFORMATIONS SECTION -----------------------
7283 
7292  public function StartTransform() {
7293  $this->_out('q');
7294  $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
7295  }
7296 
7305  public function StopTransform() {
7306  $this->_out('Q');
7307  if (isset($this->transfmatrix)) {
7308  array_pop($this->transfmatrix);
7309  }
7310  array_pop($this->transfmrk[$this->page]);
7311  }
7321  public function ScaleX($s_x, $x='', $y='') {
7322  $this->Scale($s_x, 100, $x, $y);
7323  }
7324 
7334  public function ScaleY($s_y, $x='', $y='') {
7335  $this->Scale(100, $s_y, $x, $y);
7336  }
7337 
7347  public function ScaleXY($s, $x='', $y='') {
7348  $this->Scale($s, $s, $x, $y);
7349  }
7350 
7361  public function Scale($s_x, $s_y, $x='', $y='') {
7362  if ($x === '') {
7363  $x = $this->x;
7364  }
7365  if ($y === '') {
7366  $y = $this->y;
7367  }
7368  if ($this->rtl) {
7369  $x = $this->w - $x;
7370  }
7371  if (($s_x == 0) OR ($s_y == 0)) {
7372  $this->Error('Please do not use values equal to zero for scaling');
7373  }
7374  $y = ($this->h - $y) * $this->k;
7375  $x *= $this->k;
7376  //calculate elements of transformation matrix
7377  $s_x /= 100;
7378  $s_y /= 100;
7379  $tm[0] = $s_x;
7380  $tm[1] = 0;
7381  $tm[2] = 0;
7382  $tm[3] = $s_y;
7383  $tm[4] = $x * (1 - $s_x);
7384  $tm[5] = $y * (1 - $s_y);
7385  //scale the coordinate system
7386  $this->Transform($tm);
7387  }
7388 
7396  public function MirrorH($x='') {
7397  $this->Scale(-100, 100, $x);
7398  }
7399 
7407  public function MirrorV($y='') {
7408  $this->Scale(100, -100, '', $y);
7409  }
7410 
7419  public function MirrorP($x='',$y='') {
7420  $this->Scale(-100, -100, $x, $y);
7421  }
7422 
7432  public function MirrorL($angle=0, $x='',$y='') {
7433  $this->Scale(-100, 100, $x, $y);
7434  $this->Rotate(-2*($angle-90), $x, $y);
7435  }
7436 
7444  public function TranslateX($t_x) {
7445  $this->Translate($t_x, 0);
7446  }
7447 
7455  public function TranslateY($t_y) {
7456  $this->Translate(0, $t_y);
7457  }
7458 
7467  public function Translate($t_x, $t_y) {
7468  if ($this->rtl) {
7469  $t_x = -$t_x;
7470  }
7471  //calculate elements of transformation matrix
7472  $tm[0] = 1;
7473  $tm[1] = 0;
7474  $tm[2] = 0;
7475  $tm[3] = 1;
7476  $tm[4] = $t_x * $this->k;
7477  $tm[5] = -$t_y * $this->k;
7478  //translate the coordinate system
7479  $this->Transform($tm);
7480  }
7481 
7491  public function Rotate($angle, $x='', $y='') {
7492  if ($x === '') {
7493  $x = $this->x;
7494  }
7495  if ($y === '') {
7496  $y = $this->y;
7497  }
7498  if ($this->rtl) {
7499  $x = $this->w - $x;
7500  $angle = -$angle;
7501  }
7502  $y = ($this->h - $y) * $this->k;
7503  $x *= $this->k;
7504  //calculate elements of transformation matrix
7505  $tm[0] = cos(deg2rad($angle));
7506  $tm[1] = sin(deg2rad($angle));
7507  $tm[2] = -$tm[1];
7508  $tm[3] = $tm[0];
7509  $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
7510  $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
7511  //rotate the coordinate system around ($x,$y)
7512  $this->Transform($tm);
7513  }
7514 
7524  public function SkewX($angle_x, $x='', $y='') {
7525  $this->Skew($angle_x, 0, $x, $y);
7526  }
7527 
7537  public function SkewY($angle_y, $x='', $y='') {
7538  $this->Skew(0, $angle_y, $x, $y);
7539  }
7540 
7551  public function Skew($angle_x, $angle_y, $x='', $y='') {
7552  if ($x === '') {
7553  $x = $this->x;
7554  }
7555  if ($y === '') {
7556  $y = $this->y;
7557  }
7558  if ($this->rtl) {
7559  $x = $this->w - $x;
7560  $angle_x = -$angle_x;
7561  }
7562  if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
7563  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
7564  }
7565  $x *= $this->k;
7566  $y = ($this->h - $y) * $this->k;
7567  //calculate elements of transformation matrix
7568  $tm[0] = 1;
7569  $tm[1] = tan(deg2rad($angle_y));
7570  $tm[2] = tan(deg2rad($angle_x));
7571  $tm[3] = 1;
7572  $tm[4] = -$tm[2] * $y;
7573  $tm[5] = -$tm[1] * $x;
7574  //skew the coordinate system
7575  $this->Transform($tm);
7576  }
7577 
7584  protected function Transform($tm) {
7585  $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
7586  // store transformation matrix
7587  $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
7588  // update tranformation mark
7589  if (end($this->transfmrk[$this->page]) !== false) {
7590  $key = key($this->transfmrk[$this->page]);
7591  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
7592  }
7593  }
7594 
7595  // END TRANSFORMATIONS SECTION -------------------------
7596 
7597 
7598  // START GRAPHIC FUNCTIONS SECTION ---------------------
7599  // The following section is based on the code provided by David Hernandez Sanz
7600 
7608  public function SetLineWidth($width) {
7609  //Set line width
7610  $this->LineWidth = $width;
7611  $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
7612  if ($this->page > 0) {
7613  $this->_out($this->linestyleWidth);
7614  }
7615  }
7616 
7624  public function GetLineWidth() {
7625  return $this->LineWidth;
7626  }
7627 
7649  public function SetLineStyle($style) {
7650  extract($style);
7651  if (isset($width)) {
7652  $width_prev = $this->LineWidth;
7653  $this->SetLineWidth($width);
7654  $this->LineWidth = $width_prev;
7655  }
7656  if (isset($cap)) {
7657  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
7658  if (isset($ca[$cap])) {
7659  $this->linestyleCap = $ca[$cap].' J';
7660  $this->_out($this->linestyleCap);
7661  }
7662  }
7663  if (isset($join)) {
7664  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
7665  if (isset($ja[$join])) {
7666  $this->linestyleJoin = $ja[$join].' j';
7667  $this->_out($this->linestyleJoin);
7668  }
7669  }
7670  if (isset($dash)) {
7671  $dash_string = '';
7672  if ($dash) {
7673  if (preg_match('/^.+,/', $dash) > 0) {
7674  $tab = explode(',', $dash);
7675  } else {
7676  $tab = array($dash);
7677  }
7678  $dash_string = '';
7679  foreach ($tab as $i => $v) {
7680  if ($i) {
7681  $dash_string .= ' ';
7682  }
7683  $dash_string .= sprintf("%.2F", $v);
7684  }
7685  }
7686  if (!isset($phase) OR !$dash) {
7687  $phase = 0;
7688  }
7689  $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
7690  $this->_out($this->linestyleDash);
7691  }
7692  if (isset($color)) {
7693  $this->SetDrawColorArray($color);
7694  }
7695  }
7696 
7697  /*
7698  * Set a draw point.
7699  * @param float $x Abscissa of point.
7700  * @param float $y Ordinate of point.
7701  * @access protected
7702  * @since 2.1.000 (2008-01-08)
7703  */
7704  protected function _outPoint($x, $y) {
7705  if ($this->rtl) {
7706  $x = $this->w - $x;
7707  }
7708  $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
7709  }
7710 
7711  /*
7712  * Draws a line from last draw point.
7713  * @param float $x Abscissa of end point.
7714  * @param float $y Ordinate of end point.
7715  * @access protected
7716  * @since 2.1.000 (2008-01-08)
7717  */
7718  protected function _outLine($x, $y) {
7719  if ($this->rtl) {
7720  $x = $this->w - $x;
7721  }
7722  $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
7723  }
7724 
7735  protected function _outRect($x, $y, $w, $h, $op) {
7736  if ($this->rtl) {
7737  $x = $this->w - $x - $w;
7738  }
7739  $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
7740  }
7741 
7742  /*
7743  * Draws a Bezier curve from last draw point.
7744  * The Bezier curve is a tangent to the line between the control points at either end of the curve.
7745  * @param float $x1 Abscissa of control point 1.
7746  * @param float $y1 Ordinate of control point 1.
7747  * @param float $x2 Abscissa of control point 2.
7748  * @param float $y2 Ordinate of control point 2.
7749  * @param float $x3 Abscissa of end point.
7750  * @param float $y3 Ordinate of end point.
7751  * @access protected
7752  * @since 2.1.000 (2008-01-08)
7753  */
7754  protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
7755  if ($this->rtl) {
7756  $x1 = $this->w - $x1;
7757  $x2 = $this->w - $x2;
7758  $x3 = $this->w - $x3;
7759  }
7760  $this->_out(sprintf("%.2F %.2F %.2F %.2F %.2F %.2F c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
7761  }
7762 
7774  public function Line($x1, $y1, $x2, $y2, $style=array()) {
7775  if ($style) {
7776  $this->SetLineStyle($style);
7777  }
7778  $this->_outPoint($x1, $y1);
7779  $this->_outLine($x2, $y2);
7780  $this->_out(' S');
7781  }
7782 
7809  public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
7810  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7811  $this->SetFillColorArray($fill_color);
7812  }
7813  switch ($style) {
7814  case 'F': {
7815  $op = 'f';
7816  $border_style = array();
7817  $this->_outRect($x, $y, $w, $h, $op);
7818  break;
7819  }
7820  case 'DF':
7821  case 'FD': {
7822  if ((!$border_style) OR (isset($border_style['all']))) {
7823  $op = 'B';
7824  if (isset($border_style['all'])) {
7825  $this->SetLineStyle($border_style['all']);
7826  $border_style = array();
7827  }
7828  } else {
7829  $op = 'f';
7830  }
7831  $this->_outRect($x, $y, $w, $h, $op);
7832  break;
7833  }
7834  case 'CNZ': {
7835  $op = 'W n';
7836  $this->_outRect($x, $y, $w, $h, $op);
7837  break;
7838  }
7839  case 'CEO': {
7840  $op = 'W* n';
7841  $this->_outRect($x, $y, $w, $h, $op);
7842  break;
7843  }
7844  default: {
7845  $op = 'S';
7846  if ((!$border_style) OR (isset($border_style['all']))) {
7847  if (isset($border_style['all']) AND $border_style['all']) {
7848  $this->SetLineStyle($border_style['all']);
7849  $border_style = array();
7850  }
7851  $this->_outRect($x, $y, $w, $h, $op);
7852  }
7853  break;
7854  }
7855  }
7856  if ($border_style) {
7857  $border_style2 = array();
7858  foreach ($border_style as $line => $value) {
7859  $lenght = strlen($line);
7860  for ($i = 0; $i < $lenght; ++$i) {
7861  $border_style2[$line[$i]] = $value;
7862  }
7863  }
7864  $border_style = $border_style2;
7865  if (isset($border_style['L']) AND $border_style['L']) {
7866  $this->Line($x, $y, $x, $y + $h, $border_style['L']);
7867  }
7868  if (isset($border_style['T']) AND $border_style['T']) {
7869  $this->Line($x, $y, $x + $w, $y, $border_style['T']);
7870  }
7871  if (isset($border_style['R']) AND $border_style['R']) {
7872  $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
7873  }
7874  if (isset($border_style['B']) AND $border_style['B']) {
7875  $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
7876  }
7877  }
7878  }
7879 
7880 
7907  public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
7908  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7909  $this->SetFillColorArray($fill_color);
7910  }
7911  switch ($style) {
7912  case 'F': {
7913  $op = 'f';
7914  $line_style = array();
7915  break;
7916  }
7917  case 'FD':
7918  case 'DF': {
7919  $op = 'B';
7920  break;
7921  }
7922  case 'CNZ': {
7923  $op = 'W n';
7924  break;
7925  }
7926  case 'CEO': {
7927  $op = 'W* n';
7928  break;
7929  }
7930  default: {
7931  $op = 'S';
7932  break;
7933  }
7934  }
7935  if ($line_style) {
7936  $this->SetLineStyle($line_style);
7937  }
7938  $this->_outPoint($x0, $y0);
7939  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7940  $this->_out($op);
7941  }
7942 
7964  public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
7965  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7966  $this->SetFillColorArray($fill_color);
7967  }
7968  switch ($style) {
7969  case 'F': {
7970  $op = 'f';
7971  $line_style = array();
7972  break;
7973  }
7974  case 'FD':
7975  case 'DF': {
7976  $op = 'B';
7977  break;
7978  }
7979  case 'CNZ': {
7980  $op = 'W n';
7981  break;
7982  }
7983  case 'CEO': {
7984  $op = 'W* n';
7985  break;
7986  }
7987  default: {
7988  $op = 'S';
7989  break;
7990  }
7991  }
7992  if ($line_style) {
7993  $this->SetLineStyle($line_style);
7994  }
7995  $this->_outPoint($x0, $y0);
7996  foreach ($segments as $segment) {
7997  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
7998  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7999  }
8000  $this->_out($op);
8001  }
8002 
8028  public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
8029  if ($angle) {
8030  $this->StartTransform();
8031  $this->Rotate($angle, $x0, $y0);
8032  $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8033  $this->StopTransform();
8034  return;
8035  }
8036  if ($rx) {
8037  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8038  $this->SetFillColorArray($fill_color);
8039  }
8040  switch ($style) {
8041  case 'F': {
8042  $op = 'f';
8043  $line_style = array();
8044  break;
8045  }
8046  case 'FD':
8047  case 'DF': {
8048  $op = 'B';
8049  break;
8050  }
8051  case 'C': {
8052  $op = 's'; // Small 's' signifies closing the path as well
8053  break;
8054  }
8055  case 'CNZ': {
8056  $op = 'W n';
8057  break;
8058  }
8059  case 'CEO': {
8060  $op = 'W* n';
8061  break;
8062  }
8063  default: {
8064  $op = 'S';
8065  break;
8066  }
8067  }
8068  if ($line_style) {
8069  $this->SetLineStyle($line_style);
8070  }
8071  if (!$ry) {
8072  $ry = $rx;
8073  }
8074  $rx *= $this->k;
8075  $ry *= $this->k;
8076  if ($nc < 2) {
8077  $nc = 2;
8078  }
8079  $astart = deg2rad((float) $astart);
8080  $afinish = deg2rad((float) $afinish);
8081  $total_angle = $afinish - $astart;
8082  $dt = $total_angle / $nc;
8083  $dtm = $dt / 3;
8084  $x0 *= $this->k;
8085  $y0 = ($this->h - $y0) * $this->k;
8086  $t1 = $astart;
8087  $a0 = $x0 + ($rx * cos($t1));
8088  $b0 = $y0 + ($ry * sin($t1));
8089  $c0 = -$rx * sin($t1);
8090  $d0 = $ry * cos($t1);
8091  $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
8092  for ($i = 1; $i <= $nc; ++$i) {
8093  // Draw this bit of the total curve
8094  $t1 = ($i * $dt) + $astart;
8095  $a1 = $x0 + ($rx * cos($t1));
8096  $b1 = $y0 + ($ry * sin($t1));
8097  $c1 = -$rx * sin($t1);
8098  $d1 = $ry * cos($t1);
8099  $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k));
8100  $a0 = $a1;
8101  $b0 = $b1;
8102  $c0 = $c1;
8103  $d0 = $d1;
8104  }
8105  $this->_out($op);
8106  }
8107  }
8108 
8132  public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
8133  $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8134  }
8135 
8157  public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) {
8158  $np = count($p) / 2;
8159  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8160  $this->SetFillColorArray($fill_color);
8161  }
8162  switch ($style) {
8163  case 'F': {
8164  $line_style = array();
8165  $op = 'f';
8166  break;
8167  }
8168  case 'FD':
8169  case 'DF': {
8170  $op = 'B';
8171  break;
8172  }
8173  case 'CNZ': {
8174  $op = 'W n';
8175  break;
8176  }
8177  case 'CEO': {
8178  $op = 'W* n';
8179  break;
8180  }
8181  default: {
8182  $op = 'S';
8183  break;
8184  }
8185  }
8186  $draw = true;
8187  if ($line_style) {
8188  if (isset($line_style['all'])) {
8189  $this->SetLineStyle($line_style['all']);
8190  } else { // 0 .. (np - 1), op = {B, S}
8191  $draw = false;
8192  if ('B' == $op) {
8193  $op = 'f';
8194  $this->_outPoint($p[0], $p[1]);
8195  for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8196  $this->_outLine($p[$i], $p[$i + 1]);
8197  }
8198  $this->_outLine($p[0], $p[1]);
8199  $this->_out($op);
8200  }
8201  $p[($np * 2)] = $p[0];
8202  $p[(($np * 2) + 1)] = $p[1];
8203  for ($i = 0; $i < $np; ++$i) {
8204  if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
8205  $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
8206  }
8207  }
8208  }
8209  }
8210  if ($draw) {
8211  $this->_outPoint($p[0], $p[1]);
8212  for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8213  $this->_outLine($p[$i], $p[$i + 1]);
8214  }
8215  $this->_outLine($p[0], $p[1]);
8216  $this->_out($op);
8217  }
8218  }
8219 
8256  public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
8257  if (3 > $ns) {
8258  $ns = 3;
8259  }
8260  if ($draw_circle) {
8261  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8262  }
8263  $p = array();
8264  for ($i = 0; $i < $ns; ++$i) {
8265  $a = $angle + ($i * 360 / $ns);
8266  $a_rad = deg2rad((float) $a);
8267  $p[] = $x0 + ($r * sin($a_rad));
8268  $p[] = $y0 + ($r * cos($a_rad));
8269  }
8270  $this->Polygon($p, $style, $line_style, $fill_color);
8271  }
8272 
8311  public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
8312  if (2 > $nv) {
8313  $nv = 2;
8314  }
8315  if ($draw_circle) {
8316  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8317  }
8318  $p2 = array();
8319  $visited = array();
8320  for ($i = 0; $i < $nv; ++$i) {
8321  $a = $angle + ($i * 360 / $nv);
8322  $a_rad = deg2rad((float) $a);
8323  $p2[] = $x0 + ($r * sin($a_rad));
8324  $p2[] = $y0 + ($r * cos($a_rad));
8325  $visited[] = false;
8326  }
8327  $p = array();
8328  $i = 0;
8329  do {
8330  $p[] = $p2[$i * 2];
8331  $p[] = $p2[($i * 2) + 1];
8332  $visited[$i] = true;
8333  $i += $ng;
8334  $i %= $nv;
8335  } while (!$visited[$i]);
8336  $this->Polygon($p, $style, $line_style, $fill_color);
8337  }
8338 
8360  public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
8361  if ('0000' == $round_corner) { // Not rounded
8362  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
8363  } else { // Rounded
8364  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8365  $this->SetFillColorArray($fill_color);
8366  }
8367  switch ($style) {
8368  case 'F': {
8369  $border_style = array();
8370  $op = 'f';
8371  break;
8372  }
8373  case 'FD':
8374  case 'DF': {
8375  $op = 'B';
8376  break;
8377  }
8378  case 'CNZ': {
8379  $op = 'W n';
8380  break;
8381  }
8382  case 'CEO': {
8383  $op = 'W* n';
8384  break;
8385  }
8386  default: {
8387  $op = 'S';
8388  break;
8389  }
8390  }
8391  if ($border_style) {
8392  $this->SetLineStyle($border_style);
8393  }
8394  $MyArc = 4 / 3 * (sqrt(2) - 1);
8395  $this->_outPoint($x + $r, $y);
8396  $xc = $x + $w - $r;
8397  $yc = $y + $r;
8398  $this->_outLine($xc, $y);
8399  if ($round_corner[0]) {
8400  $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
8401  } else {
8402  $this->_outLine($x + $w, $y);
8403  }
8404  $xc = $x + $w - $r;
8405  $yc = $y + $h - $r;
8406  $this->_outLine($x + $w, $yc);
8407  if ($round_corner[1]) {
8408  $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
8409  } else {
8410  $this->_outLine($x + $w, $y + $h);
8411  }
8412  $xc = $x + $r;
8413  $yc = $y + $h - $r;
8414  $this->_outLine($xc, $y + $h);
8415  if ($round_corner[2]) {
8416  $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
8417  } else {
8418  $this->_outLine($x, $y + $h);
8419  }
8420  $xc = $x + $r;
8421  $yc = $y + $r;
8422  $this->_outLine($x, $yc);
8423  if ($round_corner[3]) {
8424  $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
8425  } else {
8426  $this->_outLine($x, $y);
8427  $this->_outLine($x + $r, $y);
8428  }
8429  $this->_out($op);
8430  }
8431  }
8432 
8445  public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
8446  //main arrow line / shaft
8447  $this->Line($x0, $y0, $x1, $y1);
8448  //getting arrow direction angle
8449  $dir_angle = rad2deg(atan2(($y0 - $y1), ($x0 - $x1)));
8450  //0 angle is when both arms go along X axis. angle grows clockwise.
8451  //left arrowhead arm tip
8452  $x2L = $x1 + ($arm_size * cos(deg2rad($dir_angle + $arm_angle)));
8453  $y2L = $y1 + ($arm_size * sin(deg2rad($dir_angle + $arm_angle)));
8454  //right arrowhead arm tip
8455  $x2R = $x1 + ($arm_size * cos(deg2rad($dir_angle - $arm_angle)));
8456  $y2R = $y1 + ($arm_size * sin(deg2rad($dir_angle - $arm_angle)));
8457  if($head_style > 0) {
8458  //closed arrowhead
8459  $this->Polygon(array($x1, $y1, $x2L, $y2L, $x2R, $y2R), (($head_style === 1) ? 'D' : 'DF'), array(), array());
8460  } else { //just arms
8461  //left arm
8462  $this->Line($x1, $y1, $x2L, $y2L);
8463  //right arm
8464  $this->Line($x1, $y1, $x2R, $y2R);
8465  }
8466  }
8467 
8468  // END GRAPHIC FUNCTIONS SECTION -----------------------
8469 
8470  // BIDIRECTIONAL TEXT SECTION --------------------------
8480  protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
8481  return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
8482  }
8483 
8494  protected function utf8Bidi($ta, $str='', $forcertl=false) {
8496  // paragraph embedding level
8497  $pel = 0;
8498  // max level
8499  $maxlevel = 0;
8500  if ($this->empty_string($str)) {
8501  // create string from array
8502  $str = $this->UTF8ArrSubString($ta);
8503  }
8504  // check if string contains arabic text
8505  if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
8506  $arabic = true;
8507  } else {
8508  $arabic = false;
8509  }
8510  // check if string contains RTL text
8511  if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
8512  return $ta;
8513  }
8514 
8515  // get number of chars
8516  $numchars = count($ta);
8517 
8518  if ($forcertl == 'R') {
8519  $pel = 1;
8520  } elseif ($forcertl == 'L') {
8521  $pel = 0;
8522  } else {
8523  // P2. In each paragraph, find the first character of type L, AL, or R.
8524  // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero.
8525  for ($i=0; $i < $numchars; ++$i) {
8526  $type = $unicode[$ta[$i]];
8527  if ($type == 'L') {
8528  $pel = 0;
8529  break;
8530  } elseif (($type == 'AL') OR ($type == 'R')) {
8531  $pel = 1;
8532  break;
8533  }
8534  }
8535  }
8536 
8537  // Current Embedding Level
8538  $cel = $pel;
8539  // directional override status
8540  $dos = 'N';
8541  $remember = array();
8542  // start-of-level-run
8543  $sor = $pel % 2 ? 'R' : 'L';
8544  $eor = $sor;
8545 
8546  // Array of characters data
8547  $chardata = Array();
8548 
8549  // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
8550  // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
8551  for ($i=0; $i < $numchars; ++$i) {
8552  if ($ta[$i] == K_RLE) {
8553  // X2. With each RLE, compute the least greater odd embedding level.
8554  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
8555  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8556  $next_level = $cel + ($cel % 2) + 1;
8557  if ($next_level < 62) {
8558  $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
8559  $cel = $next_level;
8560  $dos = 'N';
8561  $sor = $eor;
8562  $eor = $cel % 2 ? 'R' : 'L';
8563  }
8564  } elseif ($ta[$i] == K_LRE) {
8565  // X3. With each LRE, compute the least greater even embedding level.
8566  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
8567  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8568  $next_level = $cel + 2 - ($cel % 2);
8569  if ( $next_level < 62 ) {
8570  $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
8571  $cel = $next_level;
8572  $dos = 'N';
8573  $sor = $eor;
8574  $eor = $cel % 2 ? 'R' : 'L';
8575  }
8576  } elseif ($ta[$i] == K_RLO) {
8577  // X4. With each RLO, compute the least greater odd embedding level.
8578  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
8579  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8580  $next_level = $cel + ($cel % 2) + 1;
8581  if ($next_level < 62) {
8582  $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
8583  $cel = $next_level;
8584  $dos = 'R';
8585  $sor = $eor;
8586  $eor = $cel % 2 ? 'R' : 'L';
8587  }
8588  } elseif ($ta[$i] == K_LRO) {
8589  // X5. With each LRO, compute the least greater even embedding level.
8590  // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
8591  // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8592  $next_level = $cel + 2 - ($cel % 2);
8593  if ( $next_level < 62 ) {
8594  $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
8595  $cel = $next_level;
8596  $dos = 'L';
8597  $sor = $eor;
8598  $eor = $cel % 2 ? 'R' : 'L';
8599  }
8600  } elseif ($ta[$i] == K_PDF) {
8601  // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
8602  if (count($remember)) {
8603  $last = count($remember ) - 1;
8604  if (($remember[$last]['num'] == K_RLE) OR
8605  ($remember[$last]['num'] == K_LRE) OR
8606  ($remember[$last]['num'] == K_RLO) OR
8607  ($remember[$last]['num'] == K_LRO)) {
8608  $match = array_pop($remember);
8609  $cel = $match['cel'];
8610  $dos = $match['dos'];
8611  $sor = $eor;
8612  $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
8613  }
8614  }
8615  } elseif (($ta[$i] != K_RLE) AND
8616  ($ta[$i] != K_LRE) AND
8617  ($ta[$i] != K_RLO) AND
8618  ($ta[$i] != K_LRO) AND
8619  ($ta[$i] != K_PDF)) {
8620  // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
8621  // a. Set the level of the current character to the current embedding level.
8622  // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
8623  if ($dos != 'N') {
8624  $chardir = $dos;
8625  } else {
8626  if (isset($unicode[$ta[$i]])) {
8627  $chardir = $unicode[$ta[$i]];
8628  } else {
8629  $chardir = 'L';
8630  }
8631  }
8632  // stores string characters and other information
8633  $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
8634  }
8635  } // end for each char
8636 
8637  // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
8638  // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
8639  // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
8640 
8641  // 3.3.3 Resolving Weak Types
8642  // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
8643  // Nonspacing marks are now resolved based on the previous characters.
8644  $numchars = count($chardata);
8645 
8646  // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
8647  $prevlevel = -1; // track level changes
8648  $levcount = 0; // counts consecutive chars at the same level
8649  for ($i=0; $i < $numchars; ++$i) {
8650  if ($chardata[$i]['type'] == 'NSM') {
8651  if ($levcount) {
8652  $chardata[$i]['type'] = $chardata[$i]['sor'];
8653  } elseif ($i > 0) {
8654  $chardata[$i]['type'] = $chardata[($i-1)]['type'];
8655  }
8656  }
8657  if ($chardata[$i]['level'] != $prevlevel) {
8658  $levcount = 0;
8659  } else {
8660  ++$levcount;
8661  }
8662  $prevlevel = $chardata[$i]['level'];
8663  }
8664 
8665  // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.
8666  $prevlevel = -1;
8667  $levcount = 0;
8668  for ($i=0; $i < $numchars; ++$i) {
8669  if ($chardata[$i]['char'] == 'EN') {
8670  for ($j=$levcount; $j >= 0; $j--) {
8671  if ($chardata[$j]['type'] == 'AL') {
8672  $chardata[$i]['type'] = 'AN';
8673  } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
8674  break;
8675  }
8676  }
8677  }
8678  if ($chardata[$i]['level'] != $prevlevel) {
8679  $levcount = 0;
8680  } else {
8681  ++$levcount;
8682  }
8683  $prevlevel = $chardata[$i]['level'];
8684  }
8685 
8686  // W3. Change all ALs to R.
8687  for ($i=0; $i < $numchars; ++$i) {
8688  if ($chardata[$i]['type'] == 'AL') {
8689  $chardata[$i]['type'] = 'R';
8690  }
8691  }
8692 
8693  // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
8694  $prevlevel = -1;
8695  $levcount = 0;
8696  for ($i=0; $i < $numchars; ++$i) {
8697  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8698  if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8699  $chardata[$i]['type'] = 'EN';
8700  } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8701  $chardata[$i]['type'] = 'EN';
8702  } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
8703  $chardata[$i]['type'] = 'AN';
8704  }
8705  }
8706  if ($chardata[$i]['level'] != $prevlevel) {
8707  $levcount = 0;
8708  } else {
8709  ++$levcount;
8710  }
8711  $prevlevel = $chardata[$i]['level'];
8712  }
8713 
8714  // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
8715  $prevlevel = -1;
8716  $levcount = 0;
8717  for ($i=0; $i < $numchars; ++$i) {
8718  if ($chardata[$i]['type'] == 'ET') {
8719  if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
8720  $chardata[$i]['type'] = 'EN';
8721  } else {
8722  $j = $i+1;
8723  while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
8724  if ($chardata[$j]['type'] == 'EN') {
8725  $chardata[$i]['type'] = 'EN';
8726  break;
8727  } elseif ($chardata[$j]['type'] != 'ET') {
8728  break;
8729  }
8730  ++$j;
8731  }
8732  }
8733  }
8734  if ($chardata[$i]['level'] != $prevlevel) {
8735  $levcount = 0;
8736  } else {
8737  ++$levcount;
8738  }
8739  $prevlevel = $chardata[$i]['level'];
8740  }
8741 
8742  // W6. Otherwise, separators and terminators change to Other Neutral.
8743  $prevlevel = -1;
8744  $levcount = 0;
8745  for ($i=0; $i < $numchars; ++$i) {
8746  if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
8747  $chardata[$i]['type'] = 'ON';
8748  }
8749  if ($chardata[$i]['level'] != $prevlevel) {
8750  $levcount = 0;
8751  } else {
8752  ++$levcount;
8753  }
8754  $prevlevel = $chardata[$i]['level'];
8755  }
8756 
8757  //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
8758  $prevlevel = -1;
8759  $levcount = 0;
8760  for ($i=0; $i < $numchars; ++$i) {
8761  if ($chardata[$i]['char'] == 'EN') {
8762  for ($j=$levcount; $j >= 0; $j--) {
8763  if ($chardata[$j]['type'] == 'L') {
8764  $chardata[$i]['type'] = 'L';
8765  } elseif ($chardata[$j]['type'] == 'R') {
8766  break;
8767  }
8768  }
8769  }
8770  if ($chardata[$i]['level'] != $prevlevel) {
8771  $levcount = 0;
8772  } else {
8773  ++$levcount;
8774  }
8775  $prevlevel = $chardata[$i]['level'];
8776  }
8777 
8778  // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
8779  $prevlevel = -1;
8780  $levcount = 0;
8781  for ($i=0; $i < $numchars; ++$i) {
8782  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8783  if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8784  $chardata[$i]['type'] = 'L';
8785  } elseif (($chardata[$i]['type'] == 'N') AND
8786  (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8787  (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8788  $chardata[$i]['type'] = 'R';
8789  } elseif ($chardata[$i]['type'] == 'N') {
8790  // N2. Any remaining neutrals take the embedding direction
8791  $chardata[$i]['type'] = $chardata[$i]['sor'];
8792  }
8793  } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8794  // first char
8795  if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8796  $chardata[$i]['type'] = 'L';
8797  } elseif (($chardata[$i]['type'] == 'N') AND
8798  (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
8799  (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8800  $chardata[$i]['type'] = 'R';
8801  } elseif ($chardata[$i]['type'] == 'N') {
8802  // N2. Any remaining neutrals take the embedding direction
8803  $chardata[$i]['type'] = $chardata[$i]['sor'];
8804  }
8805  } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
8806  //last char
8807  if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
8808  $chardata[$i]['type'] = 'L';
8809  } elseif (($chardata[$i]['type'] == 'N') AND
8810  (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8811  (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
8812  $chardata[$i]['type'] = 'R';
8813  } elseif ($chardata[$i]['type'] == 'N') {
8814  // N2. Any remaining neutrals take the embedding direction
8815  $chardata[$i]['type'] = $chardata[$i]['sor'];
8816  }
8817  } elseif ($chardata[$i]['type'] == 'N') {
8818  // N2. Any remaining neutrals take the embedding direction
8819  $chardata[$i]['type'] = $chardata[$i]['sor'];
8820  }
8821  if ($chardata[$i]['level'] != $prevlevel) {
8822  $levcount = 0;
8823  } else {
8824  ++$levcount;
8825  }
8826  $prevlevel = $chardata[$i]['level'];
8827  }
8828 
8829  // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
8830  // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
8831  for ($i=0; $i < $numchars; ++$i) {
8832  $odd = $chardata[$i]['level'] % 2;
8833  if ($odd) {
8834  if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8835  $chardata[$i]['level'] += 1;
8836  }
8837  } else {
8838  if ($chardata[$i]['type'] == 'R') {
8839  $chardata[$i]['level'] += 1;
8840  } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8841  $chardata[$i]['level'] += 2;
8842  }
8843  }
8844  $maxlevel = max($chardata[$i]['level'],$maxlevel);
8845  }
8846 
8847  // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
8848  // 1. Segment separators,
8849  // 2. Paragraph separators,
8850  // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
8851  // 4. Any sequence of white space characters at the end of the line.
8852  for ($i=0; $i < $numchars; ++$i) {
8853  if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
8854  $chardata[$i]['level'] = $pel;
8855  } elseif ($chardata[$i]['type'] == 'WS') {
8856  $j = $i+1;
8857  while ($j < $numchars) {
8858  if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
8859  (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
8860  $chardata[$i]['level'] = $pel;
8861  break;
8862  } elseif ($chardata[$j]['type'] != 'WS') {
8863  break;
8864  }
8865  ++$j;
8866  }
8867  }
8868  }
8869 
8870  // Arabic Shaping
8871  // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run.
8872  if ($arabic) {
8873  $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
8874  $alfletter = array(1570,1571,1573,1575);
8875  $chardata2 = $chardata;
8876  $laaletter = false;
8877  $charAL = array();
8878  $x = 0;
8879  for ($i=0; $i < $numchars; ++$i) {
8880  if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
8881  $charAL[$x] = $chardata[$i];
8882  $charAL[$x]['i'] = $i;
8883  $chardata[$i]['x'] = $x;
8884  ++$x;
8885  }
8886  }
8887  $numAL = $x;
8888  for ($i=0; $i < $numchars; ++$i) {
8889  $thischar = $chardata[$i];
8890  if ($i > 0) {
8891  $prevchar = $chardata[($i-1)];
8892  } else {
8893  $prevchar = false;
8894  }
8895  if (($i+1) < $numchars) {
8896  $nextchar = $chardata[($i+1)];
8897  } else {
8898  $nextchar = false;
8899  }
8900  if ($unicode[$thischar['char']] == 'AL') {
8901  $x = $thischar['x'];
8902  if ($x > 0) {
8903  $prevchar = $charAL[($x-1)];
8904  } else {
8905  $prevchar = false;
8906  }
8907  if (($x+1) < $numAL) {
8908  $nextchar = $charAL[($x+1)];
8909  } else {
8910  $nextchar = false;
8911  }
8912  // if laa letter
8913  if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
8914  $arabicarr = $laa_array;
8915  $laaletter = true;
8916  if ($x > 1) {
8917  $prevchar = $charAL[($x-2)];
8918  } else {
8919  $prevchar = false;
8920  }
8921  } else {
8922  $arabicarr = $unicode_arlet;
8923  $laaletter = false;
8924  }
8925  if (($prevchar !== false) AND ($nextchar !== false) AND
8926  (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8927  (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8928  ($prevchar['type'] == $thischar['type']) AND
8929  ($nextchar['type'] == $thischar['type']) AND
8930  ($nextchar['char'] != 1567)) {
8931  if (in_array($prevchar['char'], $endedletter)) {
8932  if (isset($arabicarr[$thischar['char']][2])) {
8933  // initial
8934  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8935  }
8936  } else {
8937  if (isset($arabicarr[$thischar['char']][3])) {
8938  // medial
8939  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
8940  }
8941  }
8942  } elseif (($nextchar !== false) AND
8943  (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8944  ($nextchar['type'] == $thischar['type']) AND
8945  ($nextchar['char'] != 1567)) {
8946  if (isset($arabicarr[$chardata[$i]['char']][2])) {
8947  // initial
8948  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8949  }
8950  } elseif ((($prevchar !== false) AND
8951  (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8952  ($prevchar['type'] == $thischar['type'])) OR
8953  (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
8954  // final
8955  if (($i > 1) AND ($thischar['char'] == 1607) AND
8956  ($chardata[$i-1]['char'] == 1604) AND
8957  ($chardata[$i-2]['char'] == 1604)) {
8958  //Allah Word
8959  // mark characters to delete with false
8960  $chardata2[$i-2]['char'] = false;
8961  $chardata2[$i-1]['char'] = false;
8962  $chardata2[$i]['char'] = 65010;
8963  } else {
8964  if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
8965  if (isset($arabicarr[$thischar['char']][0])) {
8966  // isolated
8967  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8968  }
8969  } else {
8970  if (isset($arabicarr[$thischar['char']][1])) {
8971  // final
8972  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
8973  }
8974  }
8975  }
8976  } elseif (isset($arabicarr[$thischar['char']][0])) {
8977  // isolated
8978  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8979  }
8980  // if laa letter
8981  if ($laaletter) {
8982  // mark characters to delete with false
8983  $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
8984  }
8985  } // end if AL (Arabic Letter)
8986  } // end for each char
8987  /*
8988  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594.
8989  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
8990  */
8991  $cw = &$this->CurrentFont['cw'];
8992  for ($i = 0; $i < ($numchars-1); ++$i) {
8993  if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
8994  // check if the subtitution font is defined on current font
8995  if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
8996  $chardata2[$i]['char'] = false;
8997  $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
8998  }
8999  }
9000  }
9001  // remove marked characters
9002  foreach ($chardata2 as $key => $value) {
9003  if ($value['char'] === false) {
9004  unset($chardata2[$key]);
9005  }
9006  }
9007  $chardata = array_values($chardata2);
9008  $numchars = count($chardata);
9009  unset($chardata2);
9010  unset($arabicarr);
9011  unset($laaletter);
9012  unset($charAL);
9013  }
9014 
9015  // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
9016  for ($j=$maxlevel; $j > 0; $j--) {
9017  $ordarray = Array();
9018  $revarr = Array();
9019  $onlevel = false;
9020  for ($i=0; $i < $numchars; ++$i) {
9021  if ($chardata[$i]['level'] >= $j) {
9022  $onlevel = true;
9023  if (isset($unicode_mirror[$chardata[$i]['char']])) {
9024  // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
9025  $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
9026  }
9027  $revarr[] = $chardata[$i];
9028  } else {
9029  if ($onlevel) {
9030  $revarr = array_reverse($revarr);
9031  $ordarray = array_merge($ordarray, $revarr);
9032  $revarr = Array();
9033  $onlevel = false;
9034  }
9035  $ordarray[] = $chardata[$i];
9036  }
9037  }
9038  if ($onlevel) {
9039  $revarr = array_reverse($revarr);
9040  $ordarray = array_merge($ordarray, $revarr);
9041  }
9042  $chardata = $ordarray;
9043  }
9044 
9045  $ordarray = array();
9046  for ($i=0; $i < $numchars; ++$i) {
9047  $ordarray[] = $chardata[$i]['char'];
9048  }
9049 
9050  return $ordarray;
9051  }
9052 
9053  // END OF BIDIRECTIONAL TEXT SECTION -------------------
9054 
9055  /*
9056  * Adds a bookmark.
9057  * @param string $txt bookmark description.
9058  * @param int $level bookmark level (minimum value is 0).
9059  * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
9060  * @param int $page target page number (leave empty for current page).
9061  * @access public
9062  * @author Olivier Plathey, Nicola Asuni
9063  * @since 2.1.002 (2008-02-12)
9064  */
9065  public function Bookmark($txt, $level=0, $y=-1, $page='') {
9066  if ($level < 0) {
9067  $level = 0;
9068  }
9069  if (isset($this->outlines[0])) {
9070  $lastoutline = end($this->outlines);
9071  $maxlevel = $lastoutline['l'] + 1;
9072  } else {
9073  $maxlevel = 0;
9074  }
9075  if ($level > $maxlevel) {
9076  $level = $maxlevel;
9077  }
9078  if ($y == -1) {
9079  $y = $this->GetY();
9080  }
9081  if (empty($page)) {
9082  $page = $this->PageNo();
9083  }
9084  $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
9085  }
9086 
9087  /*
9088  * Create a bookmark PDF string.
9089  * @access protected
9090  * @author Olivier Plathey, Nicola Asuni
9091  * @since 2.1.002 (2008-02-12)
9092  */
9093  protected function _putbookmarks() {
9094  $nb = count($this->outlines);
9095  if ($nb == 0) {
9096  return;
9097  }
9098  $lru = array();
9099  $level = 0;
9100  foreach ($this->outlines as $i => $o) {
9101  if ($o['l'] > 0) {
9102  $parent = $lru[($o['l'] - 1)];
9103  //Set parent and last pointers
9104  $this->outlines[$i]['parent'] = $parent;
9105  $this->outlines[$parent]['last'] = $i;
9106  if ($o['l'] > $level) {
9107  //Level increasing: set first pointer
9108  $this->outlines[$parent]['first'] = $i;
9109  }
9110  } else {
9111  $this->outlines[$i]['parent'] = $nb;
9112  }
9113  if (($o['l'] <= $level) AND ($i > 0)) {
9114  //Set prev and next pointers
9115  $prev = $lru[$o['l']];
9116  $this->outlines[$prev]['next'] = $i;
9117  $this->outlines[$i]['prev'] = $prev;
9118  }
9119  $lru[$o['l']] = $i;
9120  $level = $o['l'];
9121  }
9122  //Outline items
9123  $n = $this->n + 1;
9124  foreach ($this->outlines as $i => $o) {
9125  $this->_newobj();
9126  $this->_out('<</Title '.$this->_textstring($o['t']));
9127  $this->_out('/Parent '.($n + $o['parent']).' 0 R');
9128  if (isset($o['prev']))
9129  $this->_out('/Prev '.($n + $o['prev']).' 0 R');
9130  if (isset($o['next']))
9131  $this->_out('/Next '.($n + $o['next']).' 0 R');
9132  if (isset($o['first']))
9133  $this->_out('/First '.($n + $o['first']).' 0 R');
9134  if (isset($o['last']))
9135  $this->_out('/Last '.($n + $o['last']).' 0 R');
9136  $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
9137  $this->_out('/Count 0>>');
9138  $this->_out('endobj');
9139  }
9140  //Outline root
9141  $this->_newobj();
9142  $this->OutlineRoot = $this->n;
9143  $this->_out('<</Type /Outlines /First '.$n.' 0 R');
9144  $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
9145  $this->_out('endobj');
9146  }
9147 
9148 
9149  // --- JAVASCRIPT - FORMS ------------------------------
9150 
9151  /*
9152  * Adds a javascript
9153  * @access public
9154  * @author Johannes Güntert, Nicola Asuni
9155  * @since 2.1.002 (2008-02-12)
9156  */
9157  public function IncludeJS($script) {
9158  $this->javascript .= $script;
9159  }
9160 
9161  /*
9162  * Create a javascript PDF string.
9163  * @access protected
9164  * @author Johannes Güntert, Nicola Asuni
9165  * @since 2.1.002 (2008-02-12)
9166  */
9167  protected function _putjavascript() {
9168  if (empty($this->javascript)) {
9169  return;
9170  }
9171  // the following two lines are uded to avoid form fields duplication after saving
9172  $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
9173  $js2 = "getField('tcpdfdocsaved').value = 'saved';";
9174  $this->_newobj();
9175  $this->n_js = $this->n;
9176  $this->_out('<<');
9177  $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
9178  $this->_out('>>');
9179  $this->_out('endobj');
9180  $this->_newobj();
9181  $this->_out('<<');
9182  $this->_out('/S /JavaScript');
9183  $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
9184  $this->_out('>>');
9185  $this->_out('endobj');
9186  }
9187 
9188  /*
9189  * Convert color to javascript color.
9190  * @param string $color color name or #RRGGBB
9191  * @access protected
9192  * @author Denis Van Nuffelen, Nicola Asuni
9193  * @since 2.1.002 (2008-02-12)
9194  */
9195  protected function _JScolor($color) {
9196  static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
9197  if (substr($color,0,1) == '#') {
9198  return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
9199  }
9200  if (!in_array($color,$aColors)) {
9201  $this->Error('Invalid color: '.$color);
9202  }
9203  return 'color.'.$color;
9204  }
9205 
9206  /*
9207  * Adds a javascript form field.
9208  * @param string $type field type
9209  * @param string $name field name
9210  * @param int $x horizontal position
9211  * @param int $y vertical position
9212  * @param int $w width
9213  * @param int $h height
9214  * @param array $prop array of properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9215  * @access protected
9216  * @author Denis Van Nuffelen, Nicola Asuni
9217  * @since 2.1.002 (2008-02-12)
9218  */
9219  protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
9220  if ($this->rtl) {
9221  $x = $x - $w;
9222  }
9223  // the followind avoid fields duplication after saving the document
9224  $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
9225  $k = $this->k;
9226  $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
9227  $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
9228  while (list($key, $val) = each($prop)) {
9229  if (strcmp(substr($key, -5), 'Color') == 0) {
9230  $val = $this->_JScolor($val);
9231  } else {
9232  $val = "'".$val."'";
9233  }
9234  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
9235  }
9236  if ($this->rtl) {
9237  $this->x -= $w;
9238  } else {
9239  $this->x += $w;
9240  }
9241  $this->javascript .= '}';
9242  }
9243 
9244  /*
9245  * Creates a text field
9246  * @param string $name field name
9247  * @param int $w width
9248  * @param int $h height
9249  * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9250  * @access public
9251  * @author Denis Van Nuffelen, Nicola Asuni
9252  * @since 2.1.002 (2008-02-12)
9253  */
9254  public function TextField($name, $w, $h, $prop=array()) {
9255  $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
9256  }
9257 
9258  /*
9259  * Creates a RadioButton field
9260  * @param string $name field name
9261  * @param int $w width
9262  * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9263  * @access public
9264  * @author Nicola Asuni
9265  * @since 2.2.003 (2008-03-03)
9266  */
9267  public function RadioButton($name, $w, $prop=array()) {
9268  if (!isset($prop['strokeColor'])) {
9269  $prop['strokeColor']='black';
9270  }
9271  $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
9272  }
9273 
9274  /*
9275  * Creates a List-box field
9276  * @param string $name field name
9277  * @param int $w width
9278  * @param int $h height
9279  * @param array $values array containing the list of values.
9280  * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9281  * @access public
9282  * @author Nicola Asuni
9283  * @since 2.2.003 (2008-03-03)
9284  */
9285  public function ListBox($name, $w, $h, $values, $prop=array()) {
9286  if (!isset($prop['strokeColor'])) {
9287  $prop['strokeColor'] = 'ltGray';
9288  }
9289  $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
9290  $s = '';
9291  foreach ($values as $value) {
9292  $s .= "'".addslashes($value)."',";
9293  }
9294  $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9295  }
9296 
9297  /*
9298  * Creates a Combo-box field
9299  * @param string $name field name
9300  * @param int $w width
9301  * @param int $h height
9302  * @param array $values array containing the list of values.
9303  * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9304  * @access public
9305  * @author Denis Van Nuffelen, Nicola Asuni
9306  * @since 2.1.002 (2008-02-12)
9307  */
9308  public function ComboBox($name, $w, $h, $values, $prop=array()) {
9309  $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
9310  $s = '';
9311  foreach ($values as $value) {
9312  $s .= "'".addslashes($value)."',";
9313  }
9314  $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9315  }
9316 
9317  /*
9318  * Creates a CheckBox field
9319  * @param string $name field name
9320  * @param int $w width
9321  * @param boolean $checked define the initial state (default = false).
9322  * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9323  * @access public
9324  * @author Denis Van Nuffelen, Nicola Asuni
9325  * @since 2.1.002 (2008-02-12)
9326  */
9327  public function CheckBox($name, $w, $checked=false, $prop=array()) {
9328  $prop['value'] = ($checked ? 'Yes' : 'Off');
9329  if (!isset($prop['strokeColor'])) {
9330  $prop['strokeColor'] = 'black';
9331  }
9332  $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
9333  }
9334 
9335  /*
9336  * Creates a button field
9337  * @param string $name field name
9338  * @param int $w width
9339  * @param int $h height
9340  * @param string $caption caption.
9341  * @param string $action action triggered by the button (JavaScript code).
9342  * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9343  * @access public
9344  * @author Denis Van Nuffelen, Nicola Asuni
9345  * @since 2.1.002 (2008-02-12)
9346  */
9347  public function Button($name, $w, $h, $caption, $action, $prop=array()) {
9348  if (!isset($prop['strokeColor'])) {
9349  $prop['strokeColor'] = 'black';
9350  }
9351  if (!isset($prop['borderStyle'])) {
9352  $prop['borderStyle'] = 'beveled';
9353  }
9354  $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
9355  $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
9356  $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
9357  $this->javascript .= 'f'.$name.".highlight='push';\n";
9358  $this->javascript .= 'f'.$name.".print=false;\n";
9359  }
9360 
9361  // END JAVASCRIPT - FORMS ------------------------------
9362 
9363  /*
9364  * Enable Write permissions for PDF Reader.
9365  * WARNING: This works only using the Adobe private key with the setSignature() method.
9366  * @access protected
9367  * @author Nicola Asuni
9368  * @since 2.9.000 (2008-03-26)
9369  */
9370  protected function _putursignature() {
9371  if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) {
9372  return;
9373  }
9374  $this->_out('/Type/Sig');
9375  $this->_out('/Filter/Adobe.PPKLite');
9376  $this->_out('/SubFilter/adbe.pkcs7.detached');
9377  $this->_out('/ByteRange[0 ********** ********** **********]');
9378  $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9379  if ($this->ur) {
9380  $this->_out('/Reference');
9381  $this->_out('[');
9382  $this->_out('<<');
9383  $this->_out('/Type/SigRef');
9384  $this->_out('/TransformMethod/UR3');
9385  $this->_out('/TransformParams');
9386  $this->_out('<<');
9387  $this->_out('/Type/TransformParams');
9388  $this->_out('/V/2.2');
9389  if (!$this->empty_string($this->ur_document)) {
9390  $this->_out('/Document['.$this->ur_document.']');
9391  }
9392  if (!$this->empty_string($this->ur_annots)) {
9393  $this->_out('/Annots['.$this->ur_annots.']');
9394  }
9395  if (!$this->empty_string($this->ur_form)) {
9396  $this->_out('/Form['.$this->ur_form.']');
9397  }
9398  if (!$this->empty_string($this->ur_signature)) {
9399  $this->_out('/Signature['.$this->ur_signature.']');
9400  }
9401  $this->_out('>>');
9402  $this->_out('>>');
9403  $this->_out(']');
9404  }
9405  $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9406  }
9407 
9408  /*
9409  * Add certification signature (DocMDP)
9410  * @access protected
9411  * @author Nicola Asuni
9412  * @since 4.6.008 (2009-05-07)
9413  */
9414  protected function _putsignature() {
9415  if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) {
9416  return;
9417  }
9418  $this->_out('/Type/Sig');
9419  $this->_out('/Filter/Adobe.PPKLite');
9420  $this->_out('/SubFilter/adbe.pkcs7.detached');
9421  $this->_out('/ByteRange[0 ********** ********** **********]');
9422  $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9423  $this->_out('/Reference');
9424  $this->_out('[');
9425  $this->_out('<<');
9426  $this->_out('/Type/SigRef');
9427  $this->_out('/TransformMethod/DocMDP');
9428  $this->_out('/TransformParams');
9429  $this->_out('<<');
9430  $this->_out('/Type/TransformParams');
9431  $this->_out('/V/1.2');
9432  $this->_out('/P '.$this->signature_data['cert_type'].'');
9433  $this->_out('>>');
9434  $this->_out('>>');
9435  $this->_out(']');
9436  $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9437  if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
9438  $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
9439  }
9440  if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
9441  $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
9442  }
9443  if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
9444  $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
9445  }
9446  if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
9447  $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
9448  }
9449  }
9450 
9451  /*
9452  * Set User's Rights for PDF Reader
9453  * WARNING: This works only using the Adobe private key with the setSignature() method!.
9454  * Check the PDF Reference 8.7.1 Transform Methods,
9455  * Table 8.105 Entries in the UR transform parameters dictionary
9456  * @param boolean $enable if true enable user's rights on PDF reader
9457  * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
9458  * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
9459  * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
9460  * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
9461  * @access public
9462  * @author Nicola Asuni
9463  * @since 2.9.000 (2008-03-26)
9464  */
9465  public function setUserRights(
9466  $enable=true,
9467  $document='/FullSave',
9468  $annots='/Create/Delete/Modify/Copy/Import/Export',
9469  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
9470  $signature='/Modify') {
9471  $this->ur = $enable;
9472  $this->ur_document = $document;
9473  $this->ur_annots = $annots;
9474  $this->ur_form = $form;
9475  $this->ur_signature = $signature;
9476  }
9477 
9478  /*
9479  * Enable document signature (requires the OpenSSL Library).
9480  * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
9481  * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
9482  * @param mixed $private_key private key (string or filename prefixed with 'file://')
9483  * @param string $private_key_password password
9484  * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
9485  * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
9486  * @parm array $info array of option information: Name, Location, Reason, ContactInfo.
9487  * @access public
9488  * @author Nicola Asuni
9489  * @since 4.6.005 (2009-04-24)
9490  */
9491  public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
9492  // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem
9493  $this->sign = true;
9494  $this->signature_data = array();
9495  if (strlen($signing_cert) == 0) {
9496  $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem';
9497  }
9498  if (strlen($private_key) == 0) {
9499  $private_key = $signing_cert;
9500  }
9501  $this->signature_data['signcert'] = $signing_cert;
9502  $this->signature_data['privkey'] = $private_key;
9503  $this->signature_data['password'] = $private_key_password;
9504  $this->signature_data['extracerts'] = $extracerts;
9505  $this->signature_data['cert_type'] = $cert_type;
9506  $this->signature_data['info'] = $info;
9507  }
9508 
9509  /*
9510  * Create a new page group.
9511  * NOTE: call this function before calling AddPage()
9512  * @param int $page starting group page (leave empty for next page).
9513  * @access public
9514  * @since 3.0.000 (2008-03-27)
9515  */
9516  public function startPageGroup($page='') {
9517  if (empty($page)) {
9518  $page = $this->page + 1;
9519  }
9520  $this->newpagegroup[$page] = true;
9521  }
9522 
9531  public function AliasNbPages($alias='{nb}') {
9532  $this->AliasNbPages = $alias;
9533  }
9534 
9543  public function getAliasNbPages() {
9544  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9545  return '{'.$this->AliasNbPages.'}';
9546  }
9547  return $this->AliasNbPages;
9548  }
9549 
9558  public function AliasNumPage($alias='{pnb}') {
9559  //Define an alias for total number of pages
9560  $this->AliasNumPage = $alias;
9561  }
9562 
9571  public function getAliasNumPage() {
9572  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9573  return '{'.$this->AliasNumPage.'}';
9574  }
9575  return $this->AliasNumPage;
9576  }
9577 
9578  /*
9579  * Return the current page in the group.
9580  * @return current page in the group
9581  * @access public
9582  * @since 3.0.000 (2008-03-27)
9583  */
9584  public function getGroupPageNo() {
9585  return $this->pagegroups[$this->currpagegroup];
9586  }
9587 
9594  public function getGroupPageNoFormatted() {
9595  return $this->formatPageNumber($this->getGroupPageNo());
9596  }
9597 
9598  /*
9599  * Return the alias of the current page group
9600  * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9601  * (will be replaced by the total number of pages in this group).
9602  * @return alias of the current page group
9603  * @access public
9604  * @since 3.0.000 (2008-03-27)
9605  */
9606  public function getPageGroupAlias() {
9607  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9608  return '{'.$this->currpagegroup.'}';
9609  }
9610  return $this->currpagegroup;
9611  }
9612 
9613  /*
9614  * Return the alias for the page number on the current page group
9615  * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9616  * (will be replaced by the total number of pages in this group).
9617  * @return alias of the current page group
9618  * @access public
9619  * @since 4.5.000 (2009-01-02)
9620  */
9621  public function getPageNumGroupAlias() {
9622  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9623  return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
9624  }
9625  return str_replace('{nb', '{pnb', $this->currpagegroup);
9626  }
9627 
9635  protected function formatPageNumber($num) {
9636  return number_format((float)$num, 0, '', '.');
9637  }
9638 
9647  protected function formatTOCPageNumber($num) {
9648  return number_format((float)$num, 0, '', '.');
9649  }
9650 
9657  public function PageNoFormatted() {
9658  return $this->formatPageNumber($this->PageNo());
9659  }
9660 
9661  /*
9662  * Put visibility settings.
9663  * @access protected
9664  * @since 3.0.000 (2008-03-27)
9665  */
9666  protected function _putocg() {
9667  $this->_newobj();
9668  $this->n_ocg_print = $this->n;
9669  $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
9670  $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
9671  $this->_out('endobj');
9672  $this->_newobj();
9673  $this->n_ocg_view=$this->n;
9674  $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
9675  $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
9676  $this->_out('endobj');
9677  }
9678 
9679  /*
9680  * Set the visibility of the successive elements.
9681  * This can be useful, for instance, to put a background
9682  * image or color that will show on screen but won't print.
9683  * @param string $v visibility mode. Legal values are: all, print, screen.
9684  * @access public
9685  * @since 3.0.000 (2008-03-27)
9686  */
9687  public function setVisibility($v) {
9688  if ($this->openMarkedContent) {
9689  // close existing open marked-content
9690  $this->_out('EMC');
9691  $this->openMarkedContent = false;
9692  }
9693  switch($v) {
9694  case 'print': {
9695  $this->_out('/OC /OC1 BDC');
9696  $this->openMarkedContent = true;
9697  break;
9698  }
9699  case 'screen': {
9700  $this->_out('/OC /OC2 BDC');
9701  $this->openMarkedContent = true;
9702  break;
9703  }
9704  case 'all': {
9705  $this->_out('');
9706  break;
9707  }
9708  default: {
9709  $this->Error('Incorrect visibility: '.$v);
9710  break;
9711  }
9712  }
9713  $this->visibility = $v;
9714  }
9715 
9716  /*
9717  * Add transparency parameters to the current extgstate
9718  * @param array $params parameters
9719  * @return the number of extgstates
9720  * @access protected
9721  * @since 3.0.000 (2008-03-27)
9722  */
9723  protected function addExtGState($parms) {
9724  $n = count($this->extgstates) + 1;
9725  $this->extgstates[$n]['parms'] = $parms;
9726  return $n;
9727  }
9728 
9729  /*
9730  * Add an extgstate
9731  * @param array $gs extgstate
9732  * @access protected
9733  * @since 3.0.000 (2008-03-27)
9734  */
9735  protected function setExtGState($gs) {
9736  $this->_out(sprintf('/GS%d gs', $gs));
9737  }
9738 
9739  /*
9740  * Put extgstates for object transparency
9741  * @param array $gs extgstate
9742  * @access protected
9743  * @since 3.0.000 (2008-03-27)
9744  */
9745  protected function _putextgstates() {
9746  $ne = count($this->extgstates);
9747  for ($i = 1; $i <= $ne; ++$i) {
9748  $this->_newobj();
9749  $this->extgstates[$i]['n'] = $this->n;
9750  $this->_out('<</Type /ExtGState');
9751  foreach ($this->extgstates[$i]['parms'] as $k => $v) {
9752  $this->_out('/'.$k.' '.$v);
9753  }
9754  $this->_out('>>');
9755  $this->_out('endobj');
9756  }
9757  }
9758 
9759  /*
9760  * Set alpha for stroking (CA) and non-stroking (ca) operations.
9761  * @param float $alpha real value from 0 (transparent) to 1 (opaque)
9762  * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
9763  * @access public
9764  * @since 3.0.000 (2008-03-27)
9765  */
9766  public function setAlpha($alpha, $bm='Normal') {
9767  $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
9768  $this->setExtGState($gs);
9769  }
9770 
9771  /*
9772  * Set the default JPEG compression quality (1-100)
9773  * @param int $quality JPEG quality, integer between 1 and 100
9774  * @access public
9775  * @since 3.0.000 (2008-03-27)
9776  */
9777  public function setJPEGQuality($quality) {
9778  if (($quality < 1) OR ($quality > 100)) {
9779  $quality = 75;
9780  }
9781  $this->jpeg_quality = intval($quality);
9782  }
9783 
9784  /*
9785  * Set the default number of columns in a row for HTML tables.
9786  * @param int $cols number of columns
9787  * @access public
9788  * @since 3.0.014 (2008-06-04)
9789  */
9790  public function setDefaultTableColumns($cols=4) {
9791  $this->default_table_columns = intval($cols);
9792  }
9793 
9794  /*
9795  * Set the height of the cell (line height) respect the font height.
9796  * @param int $h cell proportion respect font height (typical value = 1.25).
9797  * @access public
9798  * @since 3.0.014 (2008-06-04)
9799  */
9800  public function setCellHeightRatio($h) {
9801  $this->cell_height_ratio = $h;
9802  }
9803 
9804  /*
9805  * return the height of cell repect font height.
9806  * @access public
9807  * @since 4.0.012 (2008-07-24)
9808  */
9809  public function getCellHeightRatio() {
9810  return $this->cell_height_ratio;
9811  }
9812 
9813  /*
9814  * Set the PDF version (check PDF reference for valid values).
9815  * Default value is 1.t
9816  * @access public
9817  * @since 3.1.000 (2008-06-09)
9818  */
9819  public function setPDFVersion($version='1.7') {
9820  $this->PDFVersion = $version;
9821  }
9822 
9823  /*
9824  * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
9825  * (see Section 8.1 of PDF reference, "Viewer Preferences").
9826  * <ul>
9827  * <li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li>
9828  * <li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li>
9829  * <li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li>
9830  * <li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li>
9831  * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
9832  * <li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li>
9833  * <li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li><ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li>
9834  * <li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9835  * <li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9836  * <li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9837  * <li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9838  * <li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li><ul></li>
9839  * <li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li>
9840  * <li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li>
9841  * <li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li>
9842  * <li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li>
9843  * </ul>
9844  * @param array $preferences array of options.
9845  * @author Nicola Asuni
9846  * @access public
9847  * @since 3.1.000 (2008-06-09)
9848  */
9849  public function setViewerPreferences($preferences) {
9850  $this->viewer_preferences = $preferences;
9851  }
9852 
9866  public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
9867  $this->Clip($x, $y, $w, $h);
9868  $this->Gradient(2, $col1, $col2, $coords);
9869  }
9870 
9884  public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
9885  $this->Clip($x, $y, $w, $h);
9886  $this->Gradient(3, $col1, $col2, $coords);
9887  }
9888 
9906  public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1) {
9907  $this->Clip($x, $y, $w, $h);
9908  $n = count($this->gradients) + 1;
9909  $this->gradients[$n]['type'] = 6; //coons patch mesh
9910  //check the coords array if it is the simple array or the multi patch array
9911  if (!isset($coords[0]['f'])) {
9912  //simple array -> convert to multi patch array
9913  if (!isset($col1[1])) {
9914  $col1[1] = $col1[2] = $col1[0];
9915  }
9916  if (!isset($col2[1])) {
9917  $col2[1] = $col2[2] = $col2[0];
9918  }
9919  if (!isset($col3[1])) {
9920  $col3[1] = $col3[2] = $col3[0];
9921  }
9922  if (!isset($col4[1])) {
9923  $col4[1] = $col4[2] = $col4[0];
9924  }
9925  $patch_array[0]['f'] = 0;
9926  $patch_array[0]['points'] = $coords;
9927  $patch_array[0]['colors'][0]['r'] = $col1[0];
9928  $patch_array[0]['colors'][0]['g'] = $col1[1];
9929  $patch_array[0]['colors'][0]['b'] = $col1[2];
9930  $patch_array[0]['colors'][1]['r'] = $col2[0];
9931  $patch_array[0]['colors'][1]['g'] = $col2[1];
9932  $patch_array[0]['colors'][1]['b'] = $col2[2];
9933  $patch_array[0]['colors'][2]['r'] = $col3[0];
9934  $patch_array[0]['colors'][2]['g'] = $col3[1];
9935  $patch_array[0]['colors'][2]['b'] = $col3[2];
9936  $patch_array[0]['colors'][3]['r'] = $col4[0];
9937  $patch_array[0]['colors'][3]['g'] = $col4[1];
9938  $patch_array[0]['colors'][3]['b'] = $col4[2];
9939  } else {
9940  //multi patch array
9941  $patch_array = $coords;
9942  }
9943  $bpcd = 65535; //16 BitsPerCoordinate
9944  //build the data stream
9945  $this->gradients[$n]['stream'] = '';
9946  $count_patch = count($patch_array);
9947  for ($i=0; $i < $count_patch; ++$i) {
9948  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
9949  $count_points = count($patch_array[$i]['points']);
9950  for ($j=0; $j < $count_points; ++$j) {
9951  //each point as 16 bit
9952  $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
9953  if ($patch_array[$i]['points'][$j] < 0) {
9954  $patch_array[$i]['points'][$j] = 0;
9955  }
9956  if ($patch_array[$i]['points'][$j] > $bpcd) {
9957  $patch_array[$i]['points'][$j] = $bpcd;
9958  }
9959  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
9960  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
9961  }
9962  $count_cols = count($patch_array[$i]['colors']);
9963  for ($j=0; $j < $count_cols; ++$j) {
9964  //each color component as 8 bit
9965  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
9966  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
9967  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
9968  }
9969  }
9970  //paint the gradient
9971  $this->_out('/Sh'.$n.' sh');
9972  //restore previous Graphic State
9973  $this->_out('Q');
9974  }
9975 
9986  protected function Clip($x, $y, $w, $h) {
9987  if ($this->rtl) {
9988  $x = $this->w - $x - $w;
9989  }
9990  //save current Graphic State
9991  $s = 'q';
9992  //set clipping area
9993  $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
9994  //set up transformation matrix for gradient
9995  $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
9996  $this->_out($s);
9997  }
9998 
10009  protected function Gradient($type, $col1, $col2, $coords) {
10010  $n = count($this->gradients) + 1;
10011  $this->gradients[$n]['type'] = $type;
10012  if (!isset($col1[1])) {
10013  $col1[1]=$col1[2]=$col1[0];
10014  }
10015  $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
10016  if (!isset($col2[1])) {
10017  $col2[1] = $col2[2] = $col2[0];
10018  }
10019  $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
10020  $this->gradients[$n]['coords'] = $coords;
10021  //paint the gradient
10022  $this->_out('/Sh'.$n.' sh');
10023  //restore previous Graphic State
10024  $this->_out('Q');
10025  }
10026 
10033  function _putshaders() {
10034  foreach ($this->gradients as $id => $grad) {
10035  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
10036  $this->_newobj();
10037  $this->_out('<<');
10038  $this->_out('/FunctionType 2');
10039  $this->_out('/Domain [0.0 1.0]');
10040  $this->_out('/C0 ['.$grad['col1'].']');
10041  $this->_out('/C1 ['.$grad['col2'].']');
10042  $this->_out('/N 1');
10043  $this->_out('>>');
10044  $this->_out('endobj');
10045  $f1 = $this->n;
10046  }
10047  $this->_newobj();
10048  $this->_out('<<');
10049  $this->_out('/ShadingType '.$grad['type']);
10050  $this->_out('/ColorSpace /DeviceRGB');
10051  if ($grad['type'] == 2) {
10052  $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
10053  $this->_out('/Function '.$f1.' 0 R');
10054  $this->_out('/Extend [true true] ');
10055  $this->_out('>>');
10056  } elseif ($grad['type'] == 3) {
10057  //x0, y0, r0, x1, y1, r1
10058  //at this this time radius of inner circle is 0
10059  $this->_out(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
10060  $this->_out('/Function '.$f1.' 0 R');
10061  $this->_out('/Extend [true true] ');
10062  $this->_out('>>');
10063  } elseif ($grad['type'] == 6) {
10064  $this->_out('/BitsPerCoordinate 16');
10065  $this->_out('/BitsPerComponent 8');
10066  $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
10067  $this->_out('/BitsPerFlag 8');
10068  $this->_out('/Length '.strlen($grad['stream']));
10069  $this->_out('>>');
10070  $this->_putstream($grad['stream']);
10071  }
10072  $this->_out('endobj');
10073  $this->gradients[$id]['id'] = $this->n;
10074  }
10075  }
10076 
10083  protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
10084  $h = $this->h;
10085  $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1*$this->k, ($h-$y1)*$this->k, $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k));
10086  }
10087 
10103  public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
10104  if ($this->rtl) {
10105  $xc = $this->w - $xc;
10106  }
10107  if ($cw) {
10108  $d = $b;
10109  $b = $o - $a;
10110  $a = $o - $d;
10111  } else {
10112  $b += $o;
10113  $a += $o;
10114  }
10115  $a = ($a % 360) + 360;
10116  $b = ($b % 360) + 360;
10117  if ($a > $b) {
10118  $b +=360;
10119  }
10120  $b = $b / 360 * 2 * M_PI;
10121  $a = $a / 360 * 2 * M_PI;
10122  $d = $b - $a;
10123  if ($d == 0 ) {
10124  $d = 2 * M_PI;
10125  }
10126  $k = $this->k;
10127  $hp = $this->h;
10128  if ($style=='F') {
10129  $op = 'f';
10130  } elseif ($style=='FD' or $style=='DF') {
10131  $op = 'b';
10132  } else {
10133  $op = 's';
10134  }
10135  if (sin($d/2)) {
10136  $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
10137  }
10138  //first put the center
10139  $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
10140  //put the first point
10141  $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
10142  //draw the arc
10143  if ($d < (M_PI/2)) {
10144  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10145  } else {
10146  $b = $a + $d/4;
10147  $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
10148  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10149  $a = $b;
10150  $b = $a + $d/4;
10151  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10152  $a = $b;
10153  $b = $a + $d/4;
10154  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b) );
10155  $a = $b;
10156  $b = $a + $d/4;
10157  $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10158  }
10159  //terminate drawing
10160  $this->_out($op);
10161  }
10162 
10181  public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
10182  if ($x === '') {
10183  $x = $this->x;
10184  }
10185  if ($y === '') {
10186  $y = $this->y;
10187  }
10188  $k = $this->k;
10189  $data = file_get_contents($file);
10190  if ($data === false) {
10191  $this->Error('EPS file not found: '.$file);
10192  }
10193  $regs = array();
10194  // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
10195  preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
10196  if (count($regs) > 1) {
10197  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
10198  if (strpos($version_str, 'Adobe Illustrator') !== false) {
10199  $versexp = explode(' ', $version_str);
10200  $version = (float)array_pop($versexp);
10201  if ($version >= 9) {
10202  $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
10203  }
10204  }
10205  }
10206  // strip binary bytes in front of PS-header
10207  $start = strpos($data, '%!PS-Adobe');
10208  if ($start > 0) {
10209  $data = substr($data, $start);
10210  }
10211  // find BoundingBox params
10212  preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
10213  if (count($regs) > 1) {
10214  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
10215  } else {
10216  $this->Error('No BoundingBox found in EPS file: '.$file);
10217  }
10218  $start = strpos($data, '%%EndSetup');
10219  if ($start === false) {
10220  $start = strpos($data, '%%EndProlog');
10221  }
10222  if ($start === false) {
10223  $start = strpos($data, '%%BoundingBox');
10224  }
10225  $data = substr($data, $start);
10226  $end = strpos($data, '%%PageTrailer');
10227  if ($end===false) {
10228  $end = strpos($data, 'showpage');
10229  }
10230  if ($end) {
10231  $data = substr($data, 0, $end);
10232  }
10233  if ($w > 0) {
10234  $scale_x = $w / (($x2 - $x1) / $k);
10235  if ($h > 0) {
10236  $scale_y = $h / (($y2 - $y1) / $k);
10237  } else {
10238  $scale_y = $scale_x;
10239  $h = ($y2 - $y1) / $k * $scale_y;
10240  }
10241  } else {
10242  if ($h > 0) {
10243  $scale_y = $h / (($y2 - $y1) / $k);
10244  $scale_x = $scale_y;
10245  $w = ($x2-$x1) / $k * $scale_x;
10246  } else {
10247  $w = ($x2 - $x1) / $k;
10248  $h = ($y2 - $y1) / $k;
10249  }
10250  }
10251  // Check whether we need a new page first as this does not fit
10252  if ($this->checkPageBreak($h, $y)) {
10253  $y = $this->GetY() + $this->cMargin;
10254  }
10255  // set bottomcoordinates
10256  $this->img_rb_y = $y + $h;
10257  // set alignment
10258  if ($this->rtl) {
10259  if ($palign == 'L') {
10260  $ximg = $this->lMargin;
10261  // set right side coordinate
10262  $this->img_rb_x = $ximg + $w;
10263  } elseif ($palign == 'C') {
10264  $ximg = ($this->w - $x - $w) / 2;
10265  // set right side coordinate
10266  $this->img_rb_x = $ximg + $w;
10267  } else {
10268  $ximg = $this->w - $x - $w;
10269  // set left side coordinate
10270  $this->img_rb_x = $ximg;
10271  }
10272  } else {
10273  if ($palign == 'R') {
10274  $ximg = $this->w - $this->rMargin - $w;
10275  // set left side coordinate
10276  $this->img_rb_x = $ximg;
10277  } elseif ($palign == 'C') {
10278  $ximg = ($this->w - $x - $w) / 2;
10279  // set right side coordinate
10280  $this->img_rb_x = $ximg + $w;
10281  } else {
10282  $ximg = $x;
10283  // set right side coordinate
10284  $this->img_rb_x = $ximg + $w;
10285  }
10286  }
10287  if ($useBoundingBox) {
10288  $dx = $ximg * $k - $x1;
10289  $dy = $y * $k - $y1;
10290  } else {
10291  $dx = $ximg * $k;
10292  $dy = $y * $k;
10293  }
10294  // save the current graphic state
10295  $this->_out('q'.$this->epsmarker);
10296  // translate
10297  $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
10298  // scale
10299  if (isset($scale_x)) {
10300  $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
10301  }
10302  // handle pc/unix/mac line endings
10303  preg_match('/[\r\n]+/s', $data, $regs);
10304  $lines = explode($regs[0], $data);
10305  $u=0;
10306  $cnt = count($lines);
10307  for ($i=0; $i < $cnt; ++$i) {
10308  $line = $lines[$i];
10309  if (($line == '') OR ($line{0} == '%')) {
10310  continue;
10311  }
10312  $len = strlen($line);
10313  $chunks = explode(' ', $line);
10314  $cmd = array_pop($chunks);
10315  // RGB
10316  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
10317  $b = array_pop($chunks);
10318  $g = array_pop($chunks);
10319  $r = array_pop($chunks);
10320  $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
10321  continue;
10322  }
10323  switch ($cmd) {
10324  case 'm':
10325  case 'l':
10326  case 'v':
10327  case 'y':
10328  case 'c':
10329  case 'k':
10330  case 'K':
10331  case 'g':
10332  case 'G':
10333  case 's':
10334  case 'S':
10335  case 'J':
10336  case 'j':
10337  case 'w':
10338  case 'M':
10339  case 'd':
10340  case 'n':
10341  case 'v': {
10342  $this->_out($line);
10343  break;
10344  }
10345  case 'x': {// custom fill color
10346  list($c,$m,$y,$k) = $chunks;
10347  $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
10348  break;
10349  }
10350  case 'X': { // custom stroke color
10351  list($c,$m,$y,$k) = $chunks;
10352  $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
10353  break;
10354  }
10355  case 'Y':
10356  case 'N':
10357  case 'V':
10358  case 'L':
10359  case 'C': {
10360  $line{$len-1} = strtolower($cmd);
10361  $this->_out($line);
10362  break;
10363  }
10364  case 'b':
10365  case 'B': {
10366  $this->_out($cmd . '*');
10367  break;
10368  }
10369  case 'f':
10370  case 'F': {
10371  if ($u > 0) {
10372  $isU = false;
10373  $max = min($i+5, $cnt);
10374  for ($j=$i+1; $j < $max; ++$j)
10375  $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
10376  if ($isU) {
10377  $this->_out('f*');
10378  }
10379  } else {
10380  $this->_out('f*');
10381  }
10382  break;
10383  }
10384  case '*u': {
10385  ++$u;
10386  break;
10387  }
10388  case '*U': {
10389  --$u;
10390  break;
10391  }
10392  }
10393  }
10394  // restore previous graphic state
10395  $this->_out($this->epsmarker.'Q');
10396  if (!empty($border)) {
10397  $bx = $x;
10398  $by = $y;
10399  $this->x = $x;
10400  $this->y = $y;
10401  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
10402  $this->x = $bx;
10403  $this->y = $by;
10404  }
10405  if ($link) {
10406  $this->Link($ximg, $y, $w, $h, $link, 0);
10407  }
10408  // set pointer to align the successive text/objects
10409  switch($align) {
10410  case 'T':{
10411  $this->y = $y;
10412  $this->x = $this->img_rb_x;
10413  break;
10414  }
10415  case 'M':{
10416  $this->y = $y + round($h/2);
10417  $this->x = $this->img_rb_x;
10418  break;
10419  }
10420  case 'B':{
10421  $this->y = $this->img_rb_y;
10422  $this->x = $this->img_rb_x;
10423  break;
10424  }
10425  case 'N':{
10426  $this->SetY($this->img_rb_y);
10427  break;
10428  }
10429  default:{
10430  break;
10431  }
10432  }
10433  $this->endlinex = $this->img_rb_x;
10434  }
10435 
10441  public function setBarcode($bc='') {
10442  $this->barcode = $bc;
10443  }
10444 
10451  public function getBarcode() {
10452  return $this->barcode;
10453  }
10454 
10470  public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
10471  if ($this->empty_string($code)) {
10472  return;
10473  }
10474  require_once(dirname(__FILE__).'/barcodes.php');
10475  // save current graphic settings
10476  $gvars = $this->getGraphicVars();
10477  // create new barcode object
10478  $barcodeobj = new TCPDFBarcode($code, $type);
10479  $arrcode = $barcodeobj->getBarcodeArray();
10480  if ($arrcode === false) {
10481  $this->Error('Error in 1D barcode string');
10482  }
10483  // set default values
10484  if (!isset($style['position'])) {
10485  if ($this->rtl) {
10486  $style['position'] = 'R';
10487  } else {
10488  $style['position'] = 'L';
10489  }
10490  }
10491  if (!isset($style['padding'])) {
10492  $style['padding'] = 0;
10493  }
10494  if (!isset($style['fgcolor'])) {
10495  $style['fgcolor'] = array(0,0,0); // default black
10496  }
10497  if (!isset($style['bgcolor'])) {
10498  $style['bgcolor'] = false; // default transparent
10499  }
10500  if (!isset($style['border'])) {
10501  $style['border'] = false;
10502  }
10503  if (!isset($style['text'])) {
10504  $style['text'] = false;
10505  $fontsize = 0;
10506  }
10507  if ($style['text'] AND isset($style['font'])) {
10508  if (isset($style['fontsize'])) {
10509  $fontsize = $style['fontsize'];
10510  } else {
10511  $fontsize = 0;
10512  }
10513  $this->SetFont($style['font'], '', $fontsize);
10514  }
10515  if (!isset($style['stretchtext'])) {
10516  $style['stretchtext'] = 4;
10517  }
10518  // set foreground color
10519  $this->SetDrawColorArray($style['fgcolor']);
10520  $this->SetTextColorArray($style['fgcolor']);
10521  if ($this->empty_string($w) OR ($w <= 0)) {
10522  if ($this->rtl) {
10523  $w = $this->x - $this->lMargin;
10524  } else {
10525  $w = $this->w - $this->rMargin - $this->x;
10526  }
10527  }
10528  if ($this->empty_string($x)) {
10529  $x = $this->GetX();
10530  }
10531  if ($this->rtl) {
10532  $x = $this->w - $x;
10533  }
10534  if ($this->empty_string($y)) {
10535  $y = $this->GetY();
10536  }
10537  if ($this->empty_string($xres)) {
10538  $xres = 0.4;
10539  }
10540  $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
10541  $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
10542  if ($this->empty_string($h) OR ($h <= 0)) {
10543  $h = 10 + $extraspace;
10544  }
10545  if ($this->checkPageBreak($h)) {
10546  $y = $this->y;
10547  }
10548  // maximum bar heigth
10549  $barh = $h - $extraspace;
10550  switch ($style['position']) {
10551  case 'L': { // left
10552  if ($this->rtl) {
10553  $xpos = $x - $w;
10554  } else {
10555  $xpos = $x;
10556  }
10557  break;
10558  }
10559  case 'C': { // center
10560  $xdiff = (($w - $fbw) / 2);
10561  if ($this->rtl) {
10562  $xpos = $x - $w + $xdiff;
10563  } else {
10564  $xpos = $x + $xdiff;
10565  }
10566  break;
10567  }
10568  case 'R': { // right
10569  if ($this->rtl) {
10570  $xpos = $x - $fbw;
10571  } else {
10572  $xpos = $x + $w - $fbw;
10573  }
10574  break;
10575  }
10576  case 'S': { // stretch
10577  $fbw = $w;
10578  $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
10579  if ($this->rtl) {
10580  $xpos = $x - $w;
10581  } else {
10582  $xpos = $x;
10583  }
10584  break;
10585  }
10586  }
10587  $xpos_rect = $xpos;
10588  $xpos = $xpos_rect + $style['padding'];
10589  $xpos_text = $xpos;
10590  // barcode is always printed in LTR direction
10591  $tempRTL = $this->rtl;
10592  $this->rtl = false;
10593  // print background color
10594  if ($style['bgcolor']) {
10595  $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']);
10596  } elseif ($style['border']) {
10597  $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
10598  }
10599  // print bars
10600  if ($arrcode !== false) {
10601  foreach ($arrcode['bcode'] as $k => $v) {
10602  $bw = ($v['w'] * $xres);
10603  if ($v['t']) {
10604  // draw a vertical bar
10605  $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
10606  $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
10607  }
10608  $xpos += $bw;
10609  }
10610  }
10611  // print text
10612  if ($style['text']) {
10613  // print text
10614  $this->x = $xpos_text;
10615  $this->y = $y + $style['padding'] + $barh;
10616  $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
10617  }
10618  // restore original direction
10619  $this->rtl = $tempRTL;
10620  // restore previous settings
10621  $this->setGraphicVars($gvars);
10622  // set bottomcoordinates
10623  $this->img_rb_y = $y + $h;
10624  if ($this->rtl) {
10625  // set left side coordinate
10626  $this->img_rb_x = ($this->w - $x - $w);
10627  } else {
10628  // set right side coordinate
10629  $this->img_rb_x = $x + $w;
10630  }
10631  // set pointer to align the successive text/objects
10632  switch($align) {
10633  case 'T':{
10634  $this->y = $y;
10635  $this->x = $this->img_rb_x;
10636  break;
10637  }
10638  case 'M':{
10639  $this->y = $y + round($h/2);
10640  $this->x = $this->img_rb_x;
10641  break;
10642  }
10643  case 'B':{
10644  $this->y = $this->img_rb_y;
10645  $this->x = $this->img_rb_x;
10646  break;
10647  }
10648  case 'N':{
10649  $this->SetY($this->img_rb_y);
10650  break;
10651  }
10652  default:{
10653  break;
10654  }
10655  }
10656  }
10657 
10673  public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
10674  // convert old settings for the new write1DBarcode() function.
10675  $xres = 1 / $xres;
10676  $newstyle = array(
10677  'position' => 'L',
10678  'border' => false,
10679  'padding' => 0,
10680  'fgcolor' => array(0,0,0),
10681  'bgcolor' => false,
10682  'text' => true,
10683  'font' => $font,
10684  'fontsize' => 8,
10685  'stretchtext' => 4
10686  );
10687  if ($style & 1) {
10688  $newstyle['border'] = true;
10689  }
10690  if ($style & 2) {
10691  $newstyle['bgcolor'] = false;
10692  }
10693  if ($style & 4) {
10694  $newstyle['position'] = 'C';
10695  } elseif ($style & 8) {
10696  $newstyle['position'] = 'L';
10697  } elseif ($style & 16) {
10698  $newstyle['position'] = 'R';
10699  }
10700  if ($style & 128) {
10701  $newstyle['text'] = true;
10702  }
10703  if ($style & 256) {
10704  $newstyle['stretchtext'] = 4;
10705  }
10706  $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
10707  }
10708 
10723  public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
10724  if ($this->empty_string($code)) {
10725  return;
10726  }
10727  require_once(dirname(__FILE__).'/2dbarcodes.php');
10728  // save current graphic settings
10729  $gvars = $this->getGraphicVars();
10730  // create new barcode object
10731  $barcodeobj = new TCPDF2DBarcode($code, $type);
10732  $arrcode = $barcodeobj->getBarcodeArray();
10733  if ($arrcode === false) {
10734  $this->Error('Error in 2D barcode string');
10735  }
10736  // set default values
10737  if (!isset($style['padding'])) {
10738  $style['padding'] = 0;
10739  }
10740  if (!isset($style['fgcolor'])) {
10741  $style['fgcolor'] = array(0,0,0); // default black
10742  }
10743  if (!isset($style['bgcolor'])) {
10744  $style['bgcolor'] = false; // default transparent
10745  }
10746  if (!isset($style['border'])) {
10747  $style['border'] = false;
10748  }
10749  // set foreground color
10750  $this->SetDrawColorArray($style['fgcolor']);
10751  if ($this->empty_string($x)) {
10752  $x = $this->GetX();
10753  }
10754  if ($this->rtl) {
10755  $x = $this->w - $x;
10756  }
10757  if ($this->empty_string($y)) {
10758  $y = $this->GetY();
10759  }
10760  if ($this->empty_string($w) OR ($w <= 0)) {
10761  if ($this->rtl) {
10762  $w = $x - $this->lMargin;
10763  } else {
10764  $w = $this->w - $this->rMargin - $x;
10765  }
10766  }
10767  if ($this->empty_string($h) OR ($h <= 0)) {
10768  // 2d barcodes are square by default
10769  $h = $w;
10770  }
10771  if ($this->checkPageBreak($h)) {
10772  $y = $this->y;
10773  }
10774  // calculate barcode size (excluding padding)
10775  $bw = $w - (2 * $style['padding']);
10776  $bh = $h - (2 * $style['padding']);
10777  // calculate starting coordinates
10778  if ($this->rtl) {
10779  $xpos = $x - $w;
10780  } else {
10781  $xpos = $x;
10782  }
10783  $xpos += $style['padding'];
10784  $ypos = $y + $style['padding'];
10785  // barcode is always printed in LTR direction
10786  $tempRTL = $this->rtl;
10787  $this->rtl = false;
10788  // print background color
10789  if ($style['bgcolor']) {
10790  $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']);
10791  } elseif ($style['border']) {
10792  $this->Rect($x, $y, $w, $h, 'D');
10793  }
10794  // print barcode cells
10795  if ($arrcode !== false) {
10796  $rows = $arrcode['num_rows'];
10797  $cols = $arrcode['num_cols'];
10798  // calculate dimension of single barcode cell
10799  $cw = $bw / $cols;
10800  $ch = $bh / $rows;
10801  // for each row
10802  for ($r = 0; $r < $rows; ++$r) {
10803  $xr = $xpos;
10804  // for each column
10805  for ($c = 0; $c < $cols; ++$c) {
10806  if ($arrcode['bcode'][$r][$c] == 1) {
10807  // draw a single barcode cell
10808  $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
10809  }
10810  $xr += $cw;
10811  }
10812  $ypos += $ch;
10813  }
10814  }
10815  // restore original direction
10816  $this->rtl = $tempRTL;
10817  // restore previous settings
10818  $this->setGraphicVars($gvars);
10819  // set bottomcoordinates
10820  $this->img_rb_y = $y + $h;
10821  if ($this->rtl) {
10822  // set left side coordinate
10823  $this->img_rb_x = ($this->w - $x - $w);
10824  } else {
10825  // set right side coordinate
10826  $this->img_rb_x = $x + $w;
10827  }
10828  // set pointer to align the successive text/objects
10829  switch($align) {
10830  case 'T':{
10831  $this->y = $y;
10832  $this->x = $this->img_rb_x;
10833  break;
10834  }
10835  case 'M':{
10836  $this->y = $y + round($h/2);
10837  $this->x = $this->img_rb_x;
10838  break;
10839  }
10840  case 'B':{
10841  $this->y = $this->img_rb_y;
10842  $this->x = $this->img_rb_x;
10843  break;
10844  }
10845  case 'N':{
10846  $this->SetY($this->img_rb_y);
10847  break;
10848  }
10849  default:{
10850  break;
10851  }
10852  }
10853  }
10854 
10870  public function getMargins() {
10871  $ret = array(
10872  'left' => $this->lMargin,
10873  'right' => $this->rMargin,
10874  'top' => $this->tMargin,
10875  'bottom' => $this->bMargin,
10876  'header' => $this->header_margin,
10877  'footer' => $this->footer_margin,
10878  'cell' => $this->cMargin,
10879  );
10880  return $ret;
10881  }
10882 
10893  public function getOriginalMargins() {
10894  $ret = array(
10895  'left' => $this->original_lMargin,
10896  'right' => $this->original_rMargin
10897  );
10898  return $ret;
10899  }
10900 
10907  public function getFontSize() {
10908  return $this->FontSize;
10909  }
10910 
10917  public function getFontSizePt() {
10918  return $this->FontSizePt;
10919  }
10920 
10927  public function getFontFamily() {
10928  return $this->FontFamily;
10929  }
10930 
10937  public function getFontStyle() {
10938  return $this->FontStyle;
10939  }
10940 
10961  public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
10962  return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
10963  }
10964 
10973  protected function getHtmlDomArray($html) {
10974  // remove all unsupported tags (the line below lists all supported tags)
10975  $html = strip_tags($html, '<marker/><a><b><blockquote><br><br/><dd><del><div><dl><dt><em><font><h1><h2><h3><h4><h5><h6><hr><i><img><li><ol><p><pre><small><span><strong><sub><sup><table><tablehead><tcpdf><td><th><thead><tr><tt><u><ul>');
10976  //replace some blank characters
10977  $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
10978  $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
10979  $html = preg_replace('@(\r\n|\r)@', "\n", $html);
10980  $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
10981  $html = strtr($html, $repTable);
10982  $offset = 0;
10983  while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
10984  $html_a = substr($html, 0, $offset);
10985  $html_b = substr($html, $offset, ($pos - $offset + 6));
10986  while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
10987  // preserve newlines on <pre> tag
10988  $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
10989  }
10990  $html = $html_a.$html_b.substr($html, $pos + 6);
10991  $offset = strlen($html_a.$html_b);
10992  }
10993  $html = str_replace("\n", ' ', $html);
10994  // remove extra spaces from code
10995  $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
10996  $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
10997  $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
10998  $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
10999  $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
11000  $html = preg_replace('/<img/', ' <img', $html);
11001  $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span></span>', $html);
11002  $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
11003  // trim string
11004  $html = preg_replace('/^[\s]+/', '', $html);
11005  $html = preg_replace('/[\s]+$/', '', $html);
11006  // pattern for generic tag
11007  $tagpattern = '/(<[^>]+>)/';
11008  // explodes the string
11009  $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
11010  // count elements
11011  $maxel = count($a);
11012  $elkey = 0;
11013  $key = 0;
11014  // create an array of elements
11015  $dom = array();
11016  $dom[$key] = array();
11017  // set first void element
11018  $dom[$key]['tag'] = false;
11019  $dom[$key]['value'] = '';
11020  $dom[$key]['parent'] = 0;
11021  $dom[$key]['fontname'] = $this->FontFamily;
11022  $dom[$key]['fontstyle'] = $this->FontStyle;
11023  $dom[$key]['fontsize'] = $this->FontSizePt;
11024  $dom[$key]['bgcolor'] = false;
11025  $dom[$key]['fgcolor'] = $this->fgcolor;
11026  $dom[$key]['align'] = '';
11027  $dom[$key]['listtype'] = '';
11028  $thead = false; // true when we are inside the THEAD tag
11029  ++$key;
11030  $level = array();
11031  array_push($level, 0); // root
11032  while ($elkey < $maxel) {
11033  $dom[$key] = array();
11034  $element = $a[$elkey];
11035  $dom[$key]['elkey'] = $elkey;
11036  if (preg_match($tagpattern, $element)) {
11037  // html tag
11038  $element = substr($element, 1, -1);
11039  // get tag name
11040  preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
11041  $tagname = strtolower($tag[1]);
11042  // check if we are inside a table header
11043  if ($tagname == 'thead') {
11044  if ($element{0} == '/') {
11045  $thead = false;
11046  } else {
11047  $thead = true;
11048  }
11049  ++$elkey;
11050  continue;
11051  }
11052  $dom[$key]['tag'] = true;
11053  $dom[$key]['value'] = $tagname;
11054  if ($element{0} == '/') {
11055  // closing html tag
11056  $dom[$key]['opening'] = false;
11057  $dom[$key]['parent'] = end($level);
11058  array_pop($level);
11059  $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
11060  $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
11061  $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
11062  $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
11063  $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
11064  $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
11065  if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
11066  $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
11067  }
11068  // set the number of columns in table tag
11069  if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
11070  $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
11071  }
11072  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11073  $dom[($dom[$key]['parent'])]['content'] = '';
11074  for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
11075  $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
11076  }
11077  $key = $i;
11078  }
11079  // store header rows on a new table
11080  if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) {
11081  if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
11082  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
11083  }
11084  for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
11085  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
11086  }
11087  }
11088  if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
11089  $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
11090  }
11091  } else {
11092  // opening html tag
11093  $dom[$key]['opening'] = true;
11094  $dom[$key]['parent'] = end($level);
11095  if (substr($element, -1, 1) != '/') {
11096  // not self-closing tag
11097  array_push($level, $key);
11098  $dom[$key]['self'] = false;
11099  } else {
11100  $dom[$key]['self'] = true;
11101  }
11102  // copy some values from parent
11103  $parentkey = 0;
11104  if ($key > 0) {
11105  $parentkey = $dom[$key]['parent'];
11106  $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
11107  $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
11108  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
11109  $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
11110  $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
11111  $dom[$key]['align'] = $dom[$parentkey]['align'];
11112  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11113  }
11114  // get attributes
11115  preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
11116  $dom[$key]['attribute'] = array(); // reset attribute array
11117  while (list($id, $name) = each($attr_array[1])) {
11118  $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
11119  }
11120  // split style attributes
11121  if (isset($dom[$key]['attribute']['style'])) {
11122  // get style attributes
11123  preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
11124  $dom[$key]['style'] = array(); // reset style attribute array
11125  while (list($id, $name) = each($style_array[1])) {
11126  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
11127  }
11128  // --- get some style attributes ---
11129  if (isset($dom[$key]['style']['font-family'])) {
11130  // font family
11131  if (isset($dom[$key]['style']['font-family'])) {
11132  $fontslist = preg_split('/[,]/', strtolower($dom[$key]['style']['font-family']));
11133  foreach ($fontslist as $font) {
11134  $font = trim(strtolower($font));
11135  if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11136  $dom[$key]['fontname'] = $font;
11137  break;
11138  }
11139  }
11140  }
11141  }
11142  // list-style-type
11143  if (isset($dom[$key]['style']['list-style-type'])) {
11144  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
11145  if ($dom[$key]['listtype'] == 'inherit') {
11146  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11147  }
11148  }
11149  // font size
11150  if (isset($dom[$key]['style']['font-size'])) {
11151  $fsize = trim($dom[$key]['style']['font-size']);
11152  switch ($fsize) {
11153  // absolute-size
11154  case 'xx-small': {
11155  $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
11156  break;
11157  }
11158  case 'x-small': {
11159  $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
11160  break;
11161  }
11162  case 'small': {
11163  $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
11164  break;
11165  }
11166  case 'medium': {
11167  $dom[$key]['fontsize'] = $dom[0]['fontsize'];
11168  break;
11169  }
11170  case 'large': {
11171  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
11172  break;
11173  }
11174  case 'x-large': {
11175  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
11176  break;
11177  }
11178  case 'xx-large': {
11179  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
11180  break;
11181  }
11182  // relative-size
11183  case 'smaller': {
11184  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
11185  break;
11186  }
11187  case 'larger': {
11188  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
11189  break;
11190  }
11191  default: {
11192  $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
11193  }
11194  }
11195  }
11196  // font style
11197  if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
11198  $dom[$key]['fontstyle'] .= 'B';
11199  }
11200  if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
11201  $dom[$key]['fontstyle'] .= '"I';
11202  }
11203  // font color
11204  if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
11205  $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
11206  }
11207  // background color
11208  if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
11209  $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
11210  }
11211  // text-decoration
11212  if (isset($dom[$key]['style']['text-decoration'])) {
11213  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
11214  foreach ($decors as $dec) {
11215  $dec = trim($dec);
11216  if (!$this->empty_string($dec)) {
11217  if ($dec{0} == 'u') {
11218  $dom[$key]['fontstyle'] .= 'U';
11219  } elseif ($dec{0} == 'l') {
11220  $dom[$key]['fontstyle'] .= 'D';
11221  }
11222  }
11223  }
11224  }
11225  // check for width attribute
11226  if (isset($dom[$key]['style']['width'])) {
11227  $dom[$key]['width'] = $dom[$key]['style']['width'];
11228  }
11229  // check for height attribute
11230  if (isset($dom[$key]['style']['height'])) {
11231  $dom[$key]['height'] = $dom[$key]['style']['height'];
11232  }
11233  // check for text alignment
11234  if (isset($dom[$key]['style']['text-align'])) {
11235  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
11236  }
11237  // check for border attribute
11238  if (isset($dom[$key]['style']['border'])) {
11239  $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
11240  }
11241  }
11242  // check for font tag
11243  if ($dom[$key]['value'] == 'font') {
11244  // font family
11245  if (isset($dom[$key]['attribute']['face'])) {
11246  $fontslist = preg_split('/[,]/', strtolower($dom[$key]['attribute']['face']));
11247  foreach ($fontslist as $font) {
11248  $font = trim(strtolower($font));
11249  if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11250  $dom[$key]['fontname'] = $font;
11251  break;
11252  }
11253  }
11254  }
11255  // font size
11256  if (isset($dom[$key]['attribute']['size'])) {
11257  if ($key > 0) {
11258  if ($dom[$key]['attribute']['size']{0} == '+') {
11259  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
11260  } elseif ($dom[$key]['attribute']['size']{0} == '-') {
11261  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
11262  } else {
11263  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11264  }
11265  } else {
11266  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11267  }
11268  }
11269  }
11270  // force natural alignment for lists
11271  if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
11272  AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
11273  if ($this->rtl) {
11274  $dom[$key]['align'] = 'R';
11275  } else {
11276  $dom[$key]['align'] = 'L';
11277  }
11278  }
11279  if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
11280  $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
11281  }
11282  if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
11283  $dom[$key]['fontstyle'] .= 'B';
11284  }
11285  if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
11286  $dom[$key]['fontstyle'] .= 'I';
11287  }
11288  if ($dom[$key]['value'] == 'u') {
11289  $dom[$key]['fontstyle'] .= 'U';
11290  }
11291  if ($dom[$key]['value'] == 'del') {
11292  $dom[$key]['fontstyle'] .= 'D';
11293  }
11294  if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
11295  $dom[$key]['fontname'] = $this->default_monospaced_font;
11296  }
11297  if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
11298  $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
11299  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
11300  $dom[$key]['fontstyle'] .= 'B';
11301  }
11302  if (($dom[$key]['value'] == 'table')) {
11303  $dom[$key]['rows'] = 0; // number of rows
11304  $dom[$key]['trids'] = array(); // IDs of TR elements
11305  $dom[$key]['thead'] = ''; // table header rows
11306  }
11307  if (($dom[$key]['value'] == 'tr')) {
11308  $dom[$key]['cols'] = 0;
11309  // store the number of rows on table element
11310  ++$dom[($dom[$key]['parent'])]['rows'];
11311  // store the TR elements IDs on table element
11312  array_push($dom[($dom[$key]['parent'])]['trids'], $key);
11313  if ($thead) {
11314  $dom[$key]['thead'] = true;
11315  } else {
11316  $dom[$key]['thead'] = false;
11317  }
11318  }
11319  if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
11320  if (isset($dom[$key]['attribute']['colspan'])) {
11321  $colspan = intval($dom[$key]['attribute']['colspan']);
11322  } else {
11323  $colspan = 1;
11324  }
11325  $dom[$key]['attribute']['colspan'] = $colspan;
11326  $dom[($dom[$key]['parent'])]['cols'] += $colspan;
11327  }
11328  // set foreground color attribute
11329  if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
11330  $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
11331  }
11332  // set background color attribute
11333  if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
11334  $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
11335  }
11336  // check for width attribute
11337  if (isset($dom[$key]['attribute']['width'])) {
11338  $dom[$key]['width'] = $dom[$key]['attribute']['width'];
11339  }
11340  // check for height attribute
11341  if (isset($dom[$key]['attribute']['height'])) {
11342  $dom[$key]['height'] = $dom[$key]['attribute']['height'];
11343  }
11344  // check for text alignment
11345  if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
11346  $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
11347  }
11348  } // end opening tag
11349  } else {
11350  // text
11351  $dom[$key]['tag'] = false;
11352  $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
11353  $dom[$key]['parent'] = end($level);
11354  }
11355  ++$elkey;
11356  ++$key;
11357  }
11358  return $dom;
11359  }
11360 
11373  public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
11374  $gvars = $this->getGraphicVars();
11375  // store current values
11376  $prevPage = $this->page;
11377  $prevlMargin = $this->lMargin;
11378  $prevrMargin = $this->rMargin;
11379  $curfontname = $this->FontFamily;
11380  $curfontstyle = $this->FontStyle;
11381  $curfontsize = $this->FontSizePt;
11382  $this->newline = true;
11383  $minstartliney = $this->y;
11384  $yshift = 0;
11385  $startlinepage = $this->page;
11386  $newline = true;
11387  $loop = 0;
11388  $curpos = 0;
11389  $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf');
11390  $this->premode = false;
11391  if (isset($this->PageAnnots[$this->page])) {
11392  $pask = count($this->PageAnnots[$this->page]);
11393  } else {
11394  $pask = 0;
11395  }
11396  if (isset($this->footerlen[$this->page])) {
11397  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11398  } else {
11399  $this->footerpos[$this->page] = $this->pagelen[$this->page];
11400  }
11401  $startlinepos = $this->footerpos[$this->page];
11402  $lalign = $align;
11403  $plalign = $align;
11404  if ($this->rtl) {
11405  $w = $this->x - $this->lMargin;
11406  } else {
11407  $w = $this->w - $this->rMargin - $this->x;
11408  }
11409  $w -= (2 * $this->cMargin);
11410  if ($cell) {
11411  if ($this->rtl) {
11412  $this->x -= $this->cMargin;
11413  } else {
11414  $this->x += $this->cMargin;
11415  }
11416  }
11417  if ($this->customlistindent >= 0) {
11418  $this->listindent = $this->customlistindent;
11419  } else {
11420  $this->listindent = $this->GetStringWidth('0000');
11421  }
11422  $this->listnum = 0;
11423  if (($this->empty_string($this->lasth)) OR ($reseth)) {
11424  //set row height
11425  $this->lasth = $this->FontSize * $this->cell_height_ratio;
11426  }
11427  $dom = $this->getHtmlDomArray($html);
11428  $maxel = count($dom);
11429  $key = 0;
11430  while ($key < $maxel) {
11431  if ($dom[$key]['tag'] OR ($key == 0)) {
11432  if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
11433  $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
11434  }
11435  // vertically align image in line
11436  if ((!$this->newline)
11437  AND ($dom[$key]['value'] == 'img')
11438  AND (isset($dom[$key]['attribute']['height']))
11439  AND ($dom[$key]['attribute']['height'] > 0)) {
11440  // get image height
11441  $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
11442  if (!$this->InFooter) {
11443  // check for page break
11444  $this->checkPageBreak($imgh);
11445  }
11446  if ($this->page > $startlinepage) {
11447  // fix line splitted over two pages
11448  if (isset($this->footerlen[$startlinepage])) {
11449  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11450  }
11451  // line to be moved one page forward
11452  $pagebuff = $this->getPageBuffer($startlinepage);
11453  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11454  $tstart = substr($pagebuff, 0, $startlinepos);
11455  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11456  // remove line from previous page
11457  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11458  $pagebuff = $this->getPageBuffer($this->page);
11459  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
11460  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
11461  // add line start to current page
11462  $yshift = $minstartliney - $this->y;
11463  $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11464  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11465  // shift the annotations and links
11466  if (isset($this->PageAnnots[$this->page])) {
11467  $next_pask = count($this->PageAnnots[$this->page]);
11468  } else {
11469  $next_pask = 0;
11470  }
11471  if (isset($this->PageAnnots[$startlinepage])) {
11472  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11473  if ($pak >= $pask) {
11474  $this->PageAnnots[$this->page][] = $pac;
11475  unset($this->PageAnnots[$startlinepage][$pak]);
11476  $npak = count($this->PageAnnots[$this->page]) - 1;
11477  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11478  }
11479  }
11480 
11481  }
11482  $pask = $next_pask;
11483  $startlinepos = $this->cntmrk[$this->page];
11484  $startlinepage = $this->page;
11485  $startliney = $this->y;
11486  }
11487  $this->y += (($curfontsize / $this->k) - $imgh);
11488  $minstartliney = min($this->y, $minstartliney);
11489  } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
11490  // account for different font size
11491  $pfontname = $curfontname;
11492  $pfontstyle = $curfontstyle;
11493  $pfontsize = $curfontsize;
11494  $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
11495  $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
11496  $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
11497  if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
11498  $this->SetFont($fontname, $fontstyle, $fontsize);
11499  $this->lasth = $this->FontSize * $this->cell_height_ratio;
11500  if (is_numeric($fontsize) AND ($fontsize > 0)
11501  AND is_numeric($curfontsize) AND ($curfontsize > 0)
11502  AND ($fontsize != $curfontsize) AND (!$this->newline)
11503  AND ($key < ($maxel - 1))
11504  ) {
11505  if ((!$this->newline) AND ($this->page > $startlinepage)) {
11506  // fix lines splitted over two pages
11507  if (isset($this->footerlen[$startlinepage])) {
11508  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11509  }
11510  // line to be moved one page forward
11511  $pagebuff = $this->getPageBuffer($startlinepage);
11512  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11513  $tstart = substr($pagebuff, 0, $startlinepos);
11514  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11515  // remove line start from previous page
11516  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11517  $pagebuff = $this->getPageBuffer($this->page);
11518  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
11519  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
11520  // add line start to current page
11521  $yshift = $minstartliney - $this->y;
11522  $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11523  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11524  // shift the annotations and links
11525  if (isset($this->PageAnnots[$this->page])) {
11526  $next_pask = count($this->PageAnnots[$this->page]);
11527  } else {
11528  $next_pask = 0;
11529  }
11530  if (isset($this->PageAnnots[$startlinepage])) {
11531  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11532  if ($pak >= $pask) {
11533  $this->PageAnnots[$this->page][] = $pac;
11534  unset($this->PageAnnots[$startlinepage][$pak]);
11535  $npak = count($this->PageAnnots[$this->page]) - 1;
11536  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11537  }
11538  }
11539  }
11540  $pask = $next_pask;
11541  }
11542  $this->y += (($curfontsize - $fontsize) / $this->k);
11543  $minstartliney = min($this->y, $minstartliney);
11544  }
11545  $curfontname = $fontname;
11546  $curfontstyle = $fontstyle;
11547  $curfontsize = $fontsize;
11548  }
11549  }
11550  if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
11551  $plalign = '';
11552  }
11553  // get current position on page buffer
11554  $curpos = $this->pagelen[$startlinepage];
11555  if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
11556  $this->SetFillColorArray($dom[$key]['bgcolor']);
11557  $wfill = true;
11558  } else {
11559  $wfill = $fill | false;
11560  }
11561  if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
11562  $this->SetTextColorArray($dom[$key]['fgcolor']);
11563  }
11564  if (isset($dom[$key]['align'])) {
11565  $lalign = $dom[$key]['align'];
11566  }
11567  if ($this->empty_string($lalign)) {
11568  $lalign = $align;
11569  }
11570  }
11571  // align lines
11572  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
11573  $newline = true;
11574  // we are at the beginning of a new line
11575  if (isset($startlinex)) {
11576  $yshift = $minstartliney - $startliney;
11577  if (($yshift > 0) OR ($this->page > $startlinepage)) {
11578  $yshift = 0;
11579  }
11580  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11581  // the last line must be shifted to be aligned as requested
11582  $linew = abs($this->endlinex - $startlinex);
11583  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11584  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11585  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11586  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11587  } elseif (isset($opentagpos)) {
11588  $midpos = $opentagpos;
11589  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11590  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11591  $midpos = $this->footerpos[$startlinepage];
11592  } else {
11593  $midpos = 0;
11594  }
11595  if ($midpos > 0) {
11596  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11597  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11598  } else {
11599  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11600  $pend = '';
11601  }
11602  // calculate shifting amount
11603  $tw = $w;
11604  if ($this->lMargin != $prevlMargin) {
11605  $tw += ($prevlMargin - $this->lMargin);
11606  }
11607  if ($this->rMargin != $prevrMargin) {
11608  $tw += ($prevrMargin - $this->rMargin);
11609  }
11610  $mdiff = abs($tw - $linew);
11611  $t_x = 0;
11612  if ($plalign == 'C') {
11613  if ($this->rtl) {
11614  $t_x = -($mdiff / 2);
11615  } else {
11616  $t_x = ($mdiff / 2);
11617  }
11618  } elseif (($plalign == 'R') AND (!$this->rtl)) {
11619  // right alignment on LTR document
11620  $t_x = $mdiff;
11621  } elseif (($plalign == 'L') AND ($this->rtl)) {
11622  // left alignment on RTL document
11623  $t_x = -$mdiff;
11624  } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
11625  // Justification
11626  if ($this->rtl OR $this->tmprtl) {
11627  $t_x = $this->lMargin - $this->endlinex;
11628  }
11629  $no = 0;
11630  $ns = 0;
11631  $pmidtemp = $pmid;
11632  // escape special characters
11633  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11634  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11635  // search spaces
11636  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
11637  $maxkk = count($lnstring[1]) - 1;
11638  for ($kk=0; $kk <= $maxkk; ++$kk) {
11639  // restore special characters
11640  $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
11641  $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
11642  if ($kk == $maxkk) {
11643  if ($this->rtl OR $this->tmprtl) {
11644  $tvalue = ltrim($lnstring[1][$kk]);
11645  } else {
11646  $tvalue = rtrim($lnstring[1][$kk]);
11647  }
11648  } else {
11649  $tvalue = $lnstring[1][$kk];
11650  }
11651  // count spaces on line
11652  $no += substr_count($lnstring[1][$kk], chr(32));
11653  $ns += substr_count($tvalue, chr(32));
11654  }
11655  if ($this->rtl OR $this->tmprtl) {
11656  $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
11657  }
11658  // calculate additional space to add to each space
11659  $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k;
11660  $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k;
11661  $nsmax = $ns;
11662  $ns = 0;
11663  reset($lnstring);
11664  $offset = 0;
11665  $strcount = 0;
11666  $prev_epsposbeg = 0;
11667  global $spacew;
11668  while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
11669  if ($this->rtl OR $this->tmprtl) {
11670  $spacew = ($spacewidth * ($nsmax - $ns));
11671  } else {
11672  $spacew = ($spacewidth * $ns);
11673  }
11674  $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
11675  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
11676  $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
11677  if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
11678  OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
11679  // shift EPS images
11680  $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
11681  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
11682  $pmid_b = substr($pmid, 0, $epsposbeg);
11683  $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
11684  $pmid_e = substr($pmid, $epsposend);
11685  $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
11686  $offset = $epsposend;
11687  continue;
11688  }
11689  $prev_epsposbeg = $epsposbeg;
11690  $currentxpos = 0;
11691  // shift blocks of code
11692  switch ($strpiece[2][0]) {
11693  case 'Td':
11694  case 'cm':
11695  case 'm':
11696  case 'l': {
11697  // get current X position
11698  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11699  $currentxpos = $xmatches[1];
11700  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
11701  if ($strcount == $maxkk) {
11702  if ($this->rtl OR $this->tmprtl) {
11703  $tvalue = $lnstring[1][$strcount];
11704  } else {
11705  $tvalue = rtrim($lnstring[1][$strcount]);
11706  }
11707  } else {
11708  $tvalue = $lnstring[1][$strcount];
11709  }
11710  $ns += substr_count($tvalue, chr(32));
11711  ++$strcount;
11712  }
11713  if ($this->rtl OR $this->tmprtl) {
11714  $spacew = ($spacewidth * ($nsmax - $ns));
11715  }
11716  // justify block
11717  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11718  create_function('$matches', 'global $spacew;
11719  $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11720  return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
11721  break;
11722  }
11723  case 're': {
11724  // get current X position
11725  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11726  $currentxpos = $xmatches[1];
11727  // justify block
11728  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11729  create_function('$matches', 'global $spacew;
11730  $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11731  return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
11732  break;
11733  }
11734  case 'c': {
11735  // get current X position
11736  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11737  $currentxpos = $xmatches[1];
11738  // justify block
11739  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11740  create_function('$matches', 'global $spacew;
11741  $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
11742  $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
11743  $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
11744  return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
11745  break;
11746  }
11747  }
11748  // shift the annotations and links
11749  if (isset($this->PageAnnots[$this->page])) {
11750  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11751  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
11752  $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
11753  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
11754  break;
11755  }
11756  }
11757  }
11758  } // end of while
11759  // remove markers
11760  $pmid = str_replace('x*#!#*x', '', $pmid);
11761  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11762  // multibyte characters
11763  $spacew = $spacewidthu;
11764  $pmidtemp = $pmid;
11765  // escape special characters
11766  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11767  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11768  $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
11769  create_function('$matches', 'global $spacew;
11770  $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
11771  $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
11772  return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp);
11773  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
11774  $endlinepos = strlen($pstart."\n".$pmid."\n");
11775  } else {
11776  // non-unicode (single-byte characters)
11777  $rs = sprintf("%.3F Tw", $spacewidth);
11778  $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
11779  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
11780  $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
11781  }
11782  }
11783  } // end of J
11784  if (($t_x != 0) OR ($yshift < 0)) {
11785  // shift the line
11786  $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11787  $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11788  $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11789  // shift the annotations and links
11790  if (isset($this->PageAnnots[$this->page])) {
11791  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11792  if ($pak >= $pask) {
11793  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
11794  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
11795  }
11796  }
11797  }
11798  $this->y -= $yshift;
11799  }
11800  }
11801  }
11802  $this->newline = false;
11803  $pbrk = $this->checkPageBreak($this->lasth);
11804  $this->SetFont($fontname, $fontstyle, $fontsize);
11805  if ($wfill) {
11806  $this->SetFillColorArray($this->bgcolor);
11807  }
11808  $startlinex = $this->x;
11809  $startliney = $this->y;
11810  $minstartliney = $this->y;
11811  $startlinepage = $this->page;
11812  if (isset($endlinepos) AND (!$pbrk)) {
11813  $startlinepos = $endlinepos;
11814  unset($endlinepos);
11815  } else {
11816  if (isset($this->footerlen[$this->page])) {
11817  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11818  } else {
11819  $this->footerpos[$this->page] = $this->pagelen[$this->page];
11820  }
11821  $startlinepos = $this->footerpos[$this->page];
11822  }
11823  $plalign = $lalign;
11824  if (isset($this->PageAnnots[$this->page])) {
11825  $pask = count($this->PageAnnots[$this->page]);
11826  } else {
11827  $pask = 0;
11828  }
11829  }
11830  if (isset($opentagpos)) {
11831  unset($opentagpos);
11832  }
11833  if ($dom[$key]['tag']) {
11834  if ($dom[$key]['opening']) {
11835  if ($dom[$key]['value'] == 'table') {
11836  if ($this->rtl) {
11837  $wtmp = $this->x - $this->lMargin;
11838  } else {
11839  $wtmp = $this->w - $this->rMargin - $this->x;
11840  }
11841  $wtmp -= (2 * $this->cMargin);
11842  // calculate cell width
11843  if (isset($dom[$key]['width'])) {
11844  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11845  } else {
11846  $table_width = $wtmp;
11847  }
11848  }
11849  // table content is handled in a special way
11850  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11851  $trid = $dom[$key]['parent'];
11852  $table_el = $dom[$trid]['parent'];
11853  if (!isset($dom[$table_el]['cols'])) {
11854  $dom[$table_el]['cols'] = $trid['cols'];
11855  }
11856  $oldmargin = $this->cMargin;
11857  if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
11858  $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
11859  } else {
11860  $currentcmargin = 0;
11861  }
11862  $this->cMargin = $currentcmargin;
11863  if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
11864  $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
11865  } else {
11866  $cellspacing = 0;
11867  }
11868  if ($this->rtl) {
11869  $cellspacingx = -$cellspacing;
11870  } else {
11871  $cellspacingx = $cellspacing;
11872  }
11873  $colspan = $dom[$key]['attribute']['colspan'];
11874  $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
11875  if (isset($dom[$key]['width'])) {
11876  $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11877  } else {
11878  $cellw = $wtmp;
11879  }
11880  if (isset($dom[$key]['height'])) {
11881  // minimum cell height
11882  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
11883  } else {
11884  $cellh = 0;
11885  }
11886  $cellw -= $cellspacing;
11887  if (isset($dom[$key]['content'])) {
11888  $cell_content = $dom[$key]['content'];
11889  } else {
11890  $cell_content = '&nbsp;';
11891  }
11892  $tagtype = $dom[$key]['value'];
11893  $parentid = $key;
11894  while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
11895  // move $key index forward
11896  ++$key;
11897  }
11898  if (!isset($dom[$trid]['startpage'])) {
11899  $dom[$trid]['startpage'] = $this->page;
11900  } else {
11901  $this->setPage($dom[$trid]['startpage']);
11902  }
11903  if (!isset($dom[$trid]['starty'])) {
11904  $dom[$trid]['starty'] = $this->y;
11905  } else {
11906  $this->y = $dom[$trid]['starty'];
11907  }
11908  if (!isset($dom[$trid]['startx'])) {
11909  $dom[$trid]['startx'] = $this->x;
11910  }
11911  $this->x += ($cellspacingx / 2);
11912  if (isset($dom[$parentid]['attribute']['rowspan'])) {
11913  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
11914  } else {
11915  $rowspan = 1;
11916  }
11917  // skip row-spanned cells started on the previous rows
11918  if (isset($dom[$table_el]['rowspans'])) {
11919  $rsk = 0;
11920  $rskmax = count($dom[$table_el]['rowspans']);
11921  while ($rsk < $rskmax) {
11922  $trwsp = $dom[$table_el]['rowspans'][$rsk];
11923  $rsstartx = $trwsp['startx'];
11924  $rsendx = $trwsp['endx'];
11925  // account for margin changes
11926  if ($trwsp['startpage'] < $this->page) {
11927  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
11928  $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
11929  $rsstartx -= $dl;
11930  $rsendx -= $dl;
11931  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
11932  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
11933  $rsstartx += $dl;
11934  $rsendx += $dl;
11935  }
11936  }
11937  if (($trwsp['rowspan'] > 0)
11938  AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
11939  AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
11940  AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
11941  // set the starting X position of the current cell
11942  $this->x = $rsendx + $cellspacingx;
11943  if (($trwsp['rowspan'] == 1)
11944  AND (isset($dom[$trid]['endy']))
11945  AND (isset($dom[$trid]['endpage']))
11946  AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
11947  // set ending Y position for row
11948  $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11949  $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
11950  }
11951  $rsk = 0;
11952  } else {
11953  ++$rsk;
11954  }
11955  }
11956  }
11957  // add rowspan information to table element
11958  if ($rowspan > 1) {
11959  if (isset($this->footerlen[$this->page])) {
11960  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11961  } else {
11962  $this->footerpos[$this->page] = $this->pagelen[$this->page];
11963  }
11964  $trintmrkpos = $this->footerpos[$this->page];
11965  $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
11966  }
11967  $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
11968  if ($rowspan > 1) {
11969  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
11970  }
11971  // push background colors
11972  if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
11973  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
11974  }
11975  $prevLastH = $this->lasth;
11976  // ****** write the cell content ******
11977  $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
11978  $this->lasth = $prevLastH;
11979  $this->cMargin = $oldmargin;
11980  $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
11981  // update the end of row position
11982  if ($rowspan <= 1) {
11983  if (isset($dom[$trid]['endy'])) {
11984  if ($this->page == $dom[$trid]['endpage']) {
11985  $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
11986  } elseif ($this->page > $dom[$trid]['endpage']) {
11987  $dom[$trid]['endy'] = $this->y;
11988  }
11989  } else {
11990  $dom[$trid]['endy'] = $this->y;
11991  }
11992  if (isset($dom[$trid]['endpage'])) {
11993  $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
11994  } else {
11995  $dom[$trid]['endpage'] = $this->page;
11996  }
11997  } else {
11998  // account for row-spanned cells
11999  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
12000  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
12001  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
12002  }
12003  if (isset($dom[$table_el]['rowspans'])) {
12004  // update endy and endpage on rowspanned cells
12005  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12006  if ($trwsp['rowspan'] > 0) {
12007  if (isset($dom[$trid]['endpage'])) {
12008  if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
12009  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
12010  } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
12011  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
12012  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
12013  } else {
12014  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
12015  }
12016  }
12017  }
12018  }
12019  }
12020  $this->x += ($cellspacingx / 2);
12021  } else {
12022  // opening tag (or self-closing tag)
12023  if (!isset($opentagpos)) {
12024  if (!$this->InFooter) {
12025  if (isset($this->footerlen[$this->page])) {
12026  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
12027  } else {
12028  $this->footerpos[$this->page] = $this->pagelen[$this->page];
12029  }
12030  $opentagpos = $this->footerpos[$this->page];
12031  }
12032  }
12033  $this->openHTMLTagHandler($dom, $key, $cell);
12034  }
12035  } else {
12036  // closing tag
12037  $this->closeHTMLTagHandler($dom, $key, $cell);
12038  }
12039  } elseif (strlen($dom[$key]['value']) > 0) {
12040  // print list-item
12041  if (!$this->empty_string($this->lispacer)) {
12042  $this->SetFont($pfontname, $pfontstyle, $pfontsize);
12043  $this->lasth = $this->FontSize * $this->cell_height_ratio;
12044  $minstartliney = $this->y;
12045  $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
12046  $this->SetFont($curfontname, $curfontstyle, $curfontsize);
12047  $this->lasth = $this->FontSize * $this->cell_height_ratio;
12048  if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
12049  $this->y += (($pfontsize - $curfontsize) / $this->k);
12050  $minstartliney = min($this->y, $minstartliney);
12051  }
12052  }
12053  // text
12054  $this->htmlvspace = 0;
12055  if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
12056  // reverse spaces order
12057  $len1 = strlen($dom[$key]['value']);
12058  $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
12059  $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
12060  $tmpstr = '';
12061  if ($rsp > 0) {
12062  $tmpstr .= substr($dom[$key]['value'], -$rsp);
12063  }
12064  $tmpstr .= trim($dom[$key]['value']);
12065  if ($lsp > 0) {
12066  $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
12067  }
12068  $dom[$key]['value'] = $tmpstr;
12069  }
12070  if ($newline) {
12071  if (!$this->premode) {
12072  if (($this->rtl OR $this->tmprtl)) {
12073  $dom[$key]['value'] = rtrim($dom[$key]['value']);
12074  } else {
12075  $dom[$key]['value'] = ltrim($dom[$key]['value']);
12076  }
12077  }
12078  $newline = false;
12079  $firstblock = true;
12080  } else {
12081  $firstblock = false;
12082  }
12083  $strrest = '';
12084  if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
12085  // HTML <a> Link
12086  $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
12087  } else {
12088  $ctmpmargin = $this->cMargin;
12089  $this->cMargin = 0;
12090  // ****** write only until the end of the line and get the rest ******
12091  $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
12092  $this->cMargin = $ctmpmargin;
12093  }
12094  if (strlen($strrest) > 0) {
12095  // store the remaining string on the previous $key position
12096  $this->newline = true;
12097  if ($cell) {
12098  if ($this->rtl) {
12099  $this->x -= $this->cMargin;
12100  } else {
12101  $this->x += $this->cMargin;
12102  }
12103  }
12104  if ($strrest == $dom[$key]['value']) {
12105  // used to avoid infinite loop
12106  ++$loop;
12107  } else {
12108  $loop = 0;
12109  }
12110  $dom[$key]['value'] = ltrim($strrest);
12111  if ($loop < 3) {
12112  --$key;
12113  }
12114  } else {
12115  $loop = 0;
12116  }
12117  }
12118  ++$key;
12119  } // end for each $key
12120  // align the last line
12121  if (isset($startlinex)) {
12122  $yshift = $minstartliney - $startliney;
12123  if (($yshift > 0) OR ($this->page > $startlinepage)) {
12124  $yshift = 0;
12125  }
12126  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
12127  // the last line must be shifted to be aligned as requested
12128  $linew = abs($this->endlinex - $startlinex);
12129  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
12130  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12131  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12132  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
12133  } elseif (isset($opentagpos)) {
12134  $midpos = $opentagpos;
12135  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12136  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12137  $midpos = $this->footerpos[$startlinepage];
12138  } else {
12139  $midpos = 0;
12140  }
12141  if ($midpos > 0) {
12142  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
12143  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
12144  } else {
12145  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
12146  $pend = '';
12147  }
12148  // calculate shifting amount
12149  $tw = $w;
12150  if ($this->lMargin != $prevlMargin) {
12151  $tw += ($prevlMargin - $this->lMargin);
12152  }
12153  if ($this->rMargin != $prevrMargin) {
12154  $tw += ($prevrMargin - $this->rMargin);
12155  }
12156  $mdiff = abs($tw - $linew);
12157  if ($plalign == 'C') {
12158  if ($this->rtl) {
12159  $t_x = -($mdiff / 2);
12160  } else {
12161  $t_x = ($mdiff / 2);
12162  }
12163  } elseif (($plalign == 'R') AND (!$this->rtl)) {
12164  // right alignment on LTR document
12165  $t_x = $mdiff;
12166  } elseif (($plalign == 'L') AND ($this->rtl)) {
12167  // left alignment on RTL document
12168  $t_x = -$mdiff;
12169  } else {
12170  $t_x = 0;
12171  }
12172  if (($t_x != 0) OR ($yshift < 0)) {
12173  // shift the line
12174  $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
12175  $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
12176  $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
12177  // shift the annotations and links
12178  if (isset($this->PageAnnots[$this->page])) {
12179  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
12180  if ($pak >= $pask) {
12181  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
12182  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
12183  }
12184  }
12185  }
12186  $this->y -= $yshift;
12187  }
12188  }
12189  }
12190  if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
12191  $this->Ln($this->lasth);
12192  }
12193  // restore previous values
12194  $this->setGraphicVars($gvars);
12195  if ($this->page > $prevPage) {
12196  $this->lMargin = $this->pagedim[$this->page]['olm'];
12197  $this->rMargin = $this->pagedim[$this->page]['orm'];
12198  }
12199  unset($dom);
12200  }
12201 
12209  protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
12210  $tag = $dom[$key];
12211  $parent = $dom[($dom[$key]['parent'])];
12212  $firstorlast = ($key == 1);
12213  // check for text direction attribute
12214  if (isset($tag['attribute']['dir'])) {
12215  $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
12216  } else {
12217  $this->tmprtl = false;
12218  }
12219  //Opening tag
12220  switch($tag['value']) {
12221  case 'table': {
12222  $cp = 0;
12223  $cs = 0;
12224  $dom[$key]['rowspans'] = array();
12225  if (!$this->empty_string($dom[$key]['thead'])) {
12226  // set table header
12227  $this->thead = $dom[$key]['thead'];
12228  if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
12229  $this->theadMargins = array();
12230  $this->theadMargins['cmargin'] = $this->cMargin;
12231  }
12232  }
12233  if (isset($tag['attribute']['cellpadding'])) {
12234  $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
12235  $this->oldcMargin = $this->cMargin;
12236  $this->cMargin = $cp;
12237  }
12238  if (isset($tag['attribute']['cellspacing'])) {
12239  $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
12240  }
12241  $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
12242  break;
12243  }
12244  case 'tr': {
12245  // array of columns positions
12246  $dom[$key]['cellpos'] = array();
12247  break;
12248  }
12249  case 'hr': {
12250  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12251  $this->htmlvspace = 0;
12252  $wtmp = $this->w - $this->lMargin - $this->rMargin;
12253  if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
12254  $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
12255  } else {
12256  $hrWidth = $wtmp;
12257  }
12258  $x = $this->GetX();
12259  $y = $this->GetY();
12260  $prevlinewidth = $this->GetLineWidth();
12261  $this->Line($x, $y, $x + $hrWidth, $y);
12262  $this->SetLineWidth($prevlinewidth);
12263  $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
12264  break;
12265  }
12266  case 'a': {
12267  if (array_key_exists('href', $tag['attribute'])) {
12268  $this->HREF['url'] = $tag['attribute']['href'];
12269  }
12270  $this->HREF['color'] = $this->htmlLinkColorArray;
12271  $this->HREF['style'] = $this->htmlLinkFontStyle;
12272  if (array_key_exists('style', $tag['attribute'])) {
12273  // get style attributes
12274  preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12275  $astyle = array();
12276  while (list($id, $name) = each($style_array[1])) {
12277  $name = strtolower($name);
12278  $astyle[$name] = trim($style_array[2][$id]);
12279  }
12280  if (isset($astyle['color'])) {
12281  $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
12282  }
12283  if (isset($astyle['text-decoration'])) {
12284  $this->HREF['style'] = '';
12285  $decors = explode(' ', strtolower($astyle['text-decoration']));
12286  foreach ($decors as $dec) {
12287  $dec = trim($dec);
12288  if (!$this->empty_string($dec)) {
12289  if ($dec{0} == 'u') {
12290  $this->HREF['style'] .= 'U';
12291  } elseif ($dec{0} == 'l') {
12292  $this->HREF['style'] .= 'D';
12293  }
12294  }
12295  }
12296  }
12297  }
12298  break;
12299  }
12300  case 'img': {
12301  if (isset($tag['attribute']['src'])) {
12302  // replace relative path with real server path
12303  if ($tag['attribute']['src'][0] == '/') {
12304  $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
12305  }
12306  $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
12307  $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
12308  if (!isset($tag['attribute']['width'])) {
12309  $tag['attribute']['width'] = 0;
12310  }
12311  if (!isset($tag['attribute']['height'])) {
12312  $tag['attribute']['height'] = 0;
12313  }
12314  //if (!isset($tag['attribute']['align'])) {
12315  // the only alignment supported is "bottom"
12316  // further development is required for other modes.
12317  $tag['attribute']['align'] = 'bottom';
12318  //}
12319  switch($tag['attribute']['align']) {
12320  case 'top': {
12321  $align = 'T';
12322  break;
12323  }
12324  case 'middle': {
12325  $align = 'M';
12326  break;
12327  }
12328  case 'bottom': {
12329  $align = 'B';
12330  break;
12331  }
12332  default: {
12333  $align = 'B';
12334  break;
12335  }
12336  }
12337  $fileinfo = pathinfo($tag['attribute']['src']);
12338  if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
12339  $type = strtolower($fileinfo['extension']);
12340  }
12341  $prevy = $this->y;
12342  $xpos = $this->GetX();
12343  if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
12344  if ($this->rtl) {
12345  $xpos += $this->GetStringWidth(' ');
12346  } else {
12347  $xpos -= $this->GetStringWidth(' ');
12348  }
12349  }
12350  $imglink = '';
12351  if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
12352  $imglink = $this->HREF['url'];
12353  if ($imglink{0} == '#') {
12354  // convert url to internal link
12355  $page = intval(substr($imglink, 1));
12356  $imglink = $this->AddLink();
12357  $this->SetLink($imglink, 0, $page);
12358  }
12359  }
12360  $border = 0;
12361  if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
12362  // currently only support 1 (frame) or a combination of 'LTRB'
12363  $border = $tag['attribute']['border'];
12364  }
12365  $iw = '';
12366  if (isset($tag['attribute']['width'])) {
12367  $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
12368  }
12369  $ih = '';
12370  if (isset($tag['attribute']['height'])) {
12371  $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
12372  }
12373  if (($type == 'eps') OR ($type == 'ai')) {
12374  $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
12375  } else {
12376  $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
12377  }
12378  switch($align) {
12379  case 'T': {
12380  $this->y = $prevy;
12381  break;
12382  }
12383  case 'M': {
12384  $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
12385  break;
12386  }
12387  case 'B': {
12388  $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
12389  break;
12390  }
12391  }
12392  }
12393  break;
12394  }
12395  case 'dl': {
12396  ++$this->listnum;
12397  $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12398  break;
12399  }
12400  case 'dt': {
12401  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12402  break;
12403  }
12404  case 'dd': {
12405  if ($this->rtl) {
12406  $this->rMargin += $this->listindent;
12407  } else {
12408  $this->lMargin += $this->listindent;
12409  }
12410  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12411  break;
12412  }
12413  case 'ul':
12414  case 'ol': {
12415  $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12416  $this->htmlvspace = 0;
12417  ++$this->listnum;
12418  if ($tag['value'] == 'ol') {
12419  $this->listordered[$this->listnum] = true;
12420  } else {
12421  $this->listordered[$this->listnum] = false;
12422  }
12423  if (isset($tag['attribute']['start'])) {
12424  $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
12425  } else {
12426  $this->listcount[$this->listnum] = 0;
12427  }
12428  if ($this->rtl) {
12429  $this->rMargin += $this->listindent;
12430  } else {
12431  $this->lMargin += $this->listindent;
12432  }
12433  $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12434  $this->htmlvspace = 0;
12435  break;
12436  }
12437  case 'li': {
12438  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12439  if ($this->listordered[$this->listnum]) {
12440  // ordered item
12441  if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12442  $this->lispacer = $parent['attribute']['type'];
12443  } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12444  $this->lispacer = $parent['listtype'];
12445  } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12446  $this->lispacer = $this->lisymbol;
12447  } else {
12448  $this->lispacer = '#';
12449  }
12450  ++$this->listcount[$this->listnum];
12451  if (isset($tag['attribute']['value'])) {
12452  $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
12453  }
12454  } else {
12455  // unordered item
12456  if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12457  $this->lispacer = $parent['attribute']['type'];
12458  } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12459  $this->lispacer = $parent['listtype'];
12460  } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12461  $this->lispacer = $this->lisymbol;
12462  } else {
12463  $this->lispacer = '!';
12464  }
12465  }
12466  break;
12467  }
12468  case 'blockquote': {
12469  if ($this->rtl) {
12470  $this->rMargin += $this->listindent;
12471  } else {
12472  $this->lMargin += $this->listindent;
12473  }
12474  $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12475  break;
12476  }
12477  case 'br': {
12478  $this->Ln('', $cell);
12479  break;
12480  }
12481  case 'div': {
12482  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12483  break;
12484  }
12485  case 'p': {
12486  $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12487  break;
12488  }
12489  case 'pre': {
12490  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12491  $this->premode = true;
12492  break;
12493  }
12494  case 'sup': {
12495  $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
12496  break;
12497  }
12498  case 'sub': {
12499  $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
12500  break;
12501  }
12502  case 'h1':
12503  case 'h2':
12504  case 'h3':
12505  case 'h4':
12506  case 'h5':
12507  case 'h6': {
12508  $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
12509  break;
12510  }
12511  case 'tcpdf': {
12512  // NOT HTML: used to call TCPDF methods
12513  if (isset($tag['attribute']['method'])) {
12514  $tcpdf_method = $tag['attribute']['method'];
12515  if (method_exists($this, $tcpdf_method)) {
12516  if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
12517  eval('$params = array('.$tag['attribute']['params'].');');
12518  call_user_func_array(array($this, $tcpdf_method), $params);
12519  } else {
12520  $this->$tcpdf_method();
12521  }
12522  $this->newline = true;
12523  }
12524  }
12525  }
12526  default: {
12527  break;
12528  }
12529  }
12530  }
12531 
12539  protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
12540  $tag = $dom[$key];
12541  $parent = $dom[($dom[$key]['parent'])];
12542  $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
12543  $in_table_head = false;
12544  //Closing tag
12545  switch($tag['value']) {
12546  case 'tr': {
12547  $table_el = $dom[($dom[$key]['parent'])]['parent'];
12548  if(!isset($parent['endy'])) {
12549  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
12550  $parent['endy'] = $this->y;
12551  }
12552  if(!isset($parent['endpage'])) {
12553  $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
12554  $parent['endpage'] = $this->page;
12555  }
12556  // update row-spanned cells
12557  if (isset($dom[$table_el]['rowspans'])) {
12558  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12559  $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
12560  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12561  if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
12562  $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
12563  } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
12564  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12565  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12566  }
12567  }
12568  }
12569  // report new endy and endpage to the rowspanned cells
12570  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12571  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12572  $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
12573  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12574  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
12575  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12576  }
12577  }
12578  // update remaining rowspanned cells
12579  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12580  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12581  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
12582  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
12583  }
12584  }
12585  }
12586  $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
12587  $this->y = $dom[($dom[$key]['parent'])]['endy'];
12588  if (isset($dom[$table_el]['attribute']['cellspacing'])) {
12589  $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
12590  $this->y += $cellspacing;
12591  }
12592  $this->Ln(0, $cell);
12593  $this->x = $parent['startx'];
12594  // account for booklet mode
12595  if ($this->page > $parent['startpage']) {
12596  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
12597  $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
12598  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
12599  $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
12600  }
12601  }
12602  break;
12603  }
12604  case 'tablehead':
12605  // closing tag used for the thead part
12606  $in_table_head = true;
12607  case 'table': {
12608  // draw borders
12609  $table_el = $parent;
12610  if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
12611  OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
12612  $border = 1;
12613  } else {
12614  $border = 0;
12615  }
12616  // fix bottom line alignment of last line before page break
12617  foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
12618  // update row-spanned cells
12619  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12620  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12621  if ($trwsp['trid'] == $trkey) {
12622  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
12623  }
12624  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
12625  $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
12626  }
12627  }
12628  }
12629  if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
12630  $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
12631  $dom[$prevtrkey]['endy'] = $pgendy;
12632  // update row-spanned cells
12633  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12634  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12635  if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
12636  $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
12637  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
12638  }
12639  }
12640  }
12641  }
12642  $prevtrkey = $trkey;
12643  $table_el = $dom[($dom[$key]['parent'])];
12644  }
12645  // for each row
12646  foreach ($table_el['trids'] as $j => $trkey) {
12647  $parent = $dom[$trkey];
12648  // for each cell on the row
12649  foreach ($parent['cellpos'] as $k => $cellpos) {
12650  if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
12651  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
12652  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
12653  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
12654  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
12655  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
12656  } else {
12657  $endy = $parent['endy'];
12658  $startpage = $parent['startpage'];
12659  $endpage = $parent['endpage'];
12660  }
12661  if ($endpage > $startpage) {
12662  // design borders around HTML cells.
12663  for ($page=$startpage; $page <= $endpage; ++$page) {
12664  $this->setPage($page);
12665  if ($page == $startpage) {
12666  $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
12667  $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
12668  $cborder = $this->getBorderMode($border, $position='start');
12669  } elseif ($page == $endpage) {
12670  $this->y = $this->tMargin; // put cursor at the beginning of last page
12671  $ch = $endy - $this->tMargin;
12672  $cborder = $this->getBorderMode($border, $position='end');
12673  } else {
12674  $this->y = $this->tMargin; // put cursor at the beginning of the current page
12675  $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
12676  $cborder = $this->getBorderMode($border, $position='middle');
12677  }
12678  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12679  $this->SetFillColorArray($cellpos['bgcolor']);
12680  $fill = true;
12681  } else {
12682  $fill = false;
12683  }
12684  $cw = abs($cellpos['endx'] - $cellpos['startx']);
12685  $this->x = $cellpos['startx'];
12686  // account for margin changes
12687  if ($page > $startpage) {
12688  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
12689  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
12690  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
12691  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
12692  }
12693  }
12694  // design a cell around the text
12695  $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
12696  if ($cborder OR $fill) {
12697  $pagebuff = $this->getPageBuffer($this->page);
12698  $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
12699  $pend = substr($pagebuff, $this->intmrk[$this->page]);
12700  $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12701  $this->intmrk[$this->page] += strlen($ccode."\n");
12702  }
12703  }
12704  } else {
12705  $this->setPage($startpage);
12706  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12707  $this->SetFillColorArray($cellpos['bgcolor']);
12708  $fill = true;
12709  } else {
12710  $fill = false;
12711  }
12712  $this->x = $cellpos['startx'];
12713  $this->y = $parent['starty'];
12714  $cw = abs($cellpos['endx'] - $cellpos['startx']);
12715  $ch = $endy - $parent['starty'];
12716  // design a cell around the text
12717  $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
12718  if ($border OR $fill) {
12719  if (end($this->transfmrk[$this->page]) !== false) {
12720  $pagemarkkey = key($this->transfmrk[$this->page]);
12721  $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
12722  } elseif ($this->InFooter) {
12723  $pagemark = &$this->footerpos[$this->page];
12724  } else {
12725  $pagemark = &$this->intmrk[$this->page];
12726  }
12727  $pagebuff = $this->getPageBuffer($this->page);
12728  $pstart = substr($pagebuff, 0, $pagemark);
12729  $pend = substr($pagebuff, $pagemark);
12730  $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12731  $pagemark += strlen($ccode."\n");
12732  }
12733  }
12734  }
12735  if (isset($table_el['attribute']['cellspacing'])) {
12736  $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
12737  $this->y += $cellspacing;
12738  }
12739  $this->Ln(0, $cell);
12740  $this->x = $parent['startx'];
12741  if ($endpage > $startpage) {
12742  if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
12743  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
12744  } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
12745  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
12746  }
12747  }
12748  }
12749  if (!$in_table_head) {
12750  // we are not inside a thead section
12751  if (isset($parent['cellpadding'])) {
12752  $this->cMargin = $this->oldcMargin;
12753  }
12754  $this->lasth = $this->FontSize * $this->cell_height_ratio;
12755  if (isset($this->theadMargins['top'])) {
12756  // restore top margin
12757  $this->tMargin = $this->theadMargins['top'];
12758  $this->pagedim[$this->page]['tm'] = $this->tMargin;
12759  }
12760  // reset table header
12761  $this->thead = '';
12762  $this->theadMargins = array();
12763  }
12764  break;
12765  }
12766  case 'a': {
12767  $this->HREF = '';
12768  break;
12769  }
12770  case 'sup': {
12771  $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
12772  break;
12773  }
12774  case 'sub': {
12775  $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
12776  break;
12777  }
12778  case 'div': {
12779  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12780  break;
12781  }
12782  case 'blockquote': {
12783  if ($this->rtl) {
12784  $this->rMargin -= $this->listindent;
12785  } else {
12786  $this->lMargin -= $this->listindent;
12787  }
12788  $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12789  break;
12790  }
12791  case 'p': {
12792  $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12793  break;
12794  }
12795  case 'pre': {
12796  $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12797  $this->premode = false;
12798  break;
12799  }
12800  case 'dl': {
12801  --$this->listnum;
12802  if ($this->listnum <= 0) {
12803  $this->listnum = 0;
12804  $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12805  }
12806  break;
12807  }
12808  case 'dt': {
12809  $this->lispacer = '';
12810  $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12811  break;
12812  }
12813  case 'dd': {
12814  $this->lispacer = '';
12815  if ($this->rtl) {
12816  $this->rMargin -= $this->listindent;
12817  } else {
12818  $this->lMargin -= $this->listindent;
12819  }
12820  $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12821  break;
12822  }
12823  case 'ul':
12824  case 'ol': {
12825  --$this->listnum;
12826  $this->lispacer = '';
12827  if ($this->rtl) {
12828  $this->rMargin -= $this->listindent;
12829  } else {
12830  $this->lMargin -= $this->listindent;
12831  }
12832  if ($this->listnum <= 0) {
12833  $this->listnum = 0;
12834  $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12835  }
12836  $this->lasth = $this->FontSize * $this->cell_height_ratio;
12837  break;
12838  }
12839  case 'li': {
12840  $this->lispacer = '';
12841  $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12842  break;
12843  }
12844  case 'h1':
12845  case 'h2':
12846  case 'h3':
12847  case 'h4':
12848  case 'h5':
12849  case 'h6': {
12850  $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
12851  break;
12852  }
12853  default : {
12854  break;
12855  }
12856  }
12857  $this->tmprtl = false;
12858  }
12859 
12870  protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
12871  if ($firstorlast) {
12872  $this->Ln(0, $cell);
12873  $this->htmlvspace = 0;
12874  return;
12875  }
12876  if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
12877  $n = $this->tagvspaces[$tag][intval($closing)]['n'];
12878  }
12879  if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
12880  $h = $this->tagvspaces[$tag][intval($closing)]['h'];
12881  }
12882  if (is_string($h)) {
12883  $vsize = $n * $this->lasth;
12884  } else {
12885  $vsize = $n * $h;
12886  }
12887  if ($vsize > $this->htmlvspace) {
12888  $this->Ln(($vsize - $this->htmlvspace), $cell);
12889  $this->htmlvspace = $vsize;
12890  }
12891  }
12892 
12899  public function setLIsymbol($symbol='!') {
12900  $symbol = strtolower($symbol);
12901  switch ($symbol) {
12902  case '!' :
12903  case '#' :
12904  case 'disc' :
12905  case 'disc' :
12906  case 'circle' :
12907  case 'square' :
12908  case '1':
12909  case 'decimal':
12910  case 'decimal-leading-zero':
12911  case 'i':
12912  case 'lower-roman':
12913  case 'I':
12914  case 'upper-roman':
12915  case 'a':
12916  case 'lower-alpha':
12917  case 'lower-latin':
12918  case 'A':
12919  case 'upper-alpha':
12920  case 'upper-latin':
12921  case 'lower-greek': {
12922  $this->lisymbol = $symbol;
12923  break;
12924  }
12925  default : {
12926  $this->lisymbol = '';
12927  }
12928  }
12929  }
12930 
12939  public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
12940  $this->booklet = $booklet;
12941  if ($inner >= 0) {
12942  $this->lMargin = $inner;
12943  }
12944  if ($outer >= 0) {
12945  $this->rMargin = $outer;
12946  }
12947  }
12948 
12955  protected function swapMargins($reverse=true) {
12956  if ($reverse) {
12957  // swap left and right margins
12958  $mtemp = $this->original_lMargin;
12959  $this->original_lMargin = $this->original_rMargin;
12960  $this->original_rMargin = $mtemp;
12961  $deltam = $this->original_lMargin - $this->original_rMargin;
12962  $this->lMargin += $deltam;
12963  $this->rMargin -= $deltam;
12964  }
12965  }
12966 
12979  public function setHtmlVSpace($tagvs) {
12980  $this->tagvspaces = $tagvs;
12981  }
12982 
12989  public function setListIndentWidth($width) {
12990  return $this->customlistindent = floatval($width);
12991  }
12992 
12999  public function setOpenCell($isopen) {
13000  $this->opencell = $isopen;
13001  }
13002 
13010  public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
13011  $this->htmlLinkColorArray = $color;
13012  $this->htmlLinkFontStyle = $fontstyle;
13013  }
13014 
13025  public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
13026  $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
13027  $retval = 0;
13028  $value = 0;
13029  $unit = 'px';
13030  $k = $this->k;
13031  if ($points) {
13032  $k = 1;
13033  }
13034  if (in_array($defaultunit, $supportedunits)) {
13035  $unit = $defaultunit;
13036  }
13037  if (is_numeric($htmlval)) {
13038  $value = floatval($htmlval);
13039  } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
13040  $value = floatval($mnum[1]);
13041  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
13042  if (in_array($munit[1], $supportedunits)) {
13043  $unit = $munit[1];
13044  }
13045  }
13046  }
13047  switch ($unit) {
13048  // percentage
13049  case '%': {
13050  $retval = (($value * $refsize) / 100);
13051  break;
13052  }
13053  // relative-size
13054  case 'em': {
13055  $retval = ($value * $refsize);
13056  break;
13057  }
13058  case 'ex': {
13059  $retval = $value * ($refsize / 2);
13060  break;
13061  }
13062  // absolute-size
13063  case 'in': {
13064  $retval = ($value * $this->dpi) / $k;
13065  break;
13066  }
13067  case 'cm': {
13068  $retval = ($value / 2.54 * $this->dpi) / $k;
13069  break;
13070  }
13071  case 'mm': {
13072  $retval = ($value / 25.4 * $this->dpi) / $k;
13073  break;
13074  }
13075  case 'pc': {
13076  // one pica is 12 points
13077  $retval = ($value * 12) / $k;
13078  break;
13079  }
13080  case 'pt': {
13081  $retval = $value / $k;
13082  break;
13083  }
13084  case 'px': {
13085  $retval = $this->pixelsToUnits($value);
13086  break;
13087  }
13088  }
13089  return $retval;
13090  }
13091 
13099  public function intToRoman($number) {
13100  $roman = '';
13101  while ($number >= 1000) {
13102  $roman .= 'M';
13103  $number -= 1000;
13104  }
13105  while ($number >= 900) {
13106  $roman .= 'CM';
13107  $number -= 900;
13108  }
13109  while ($number >= 500) {
13110  $roman .= 'D';
13111  $number -= 500;
13112  }
13113  while ($number >= 400) {
13114  $roman .= 'CD';
13115  $number -= 400;
13116  }
13117  while ($number >= 100) {
13118  $roman .= 'C';
13119  $number -= 100;
13120  }
13121  while ($number >= 90) {
13122  $roman .= 'XC';
13123  $number -= 90;
13124  }
13125  while ($number >= 50) {
13126  $roman .= 'L';
13127  $number -= 50;
13128  }
13129  while ($number >= 40) {
13130  $roman .= 'XL';
13131  $number -= 40;
13132  }
13133  while ($number >= 10) {
13134  $roman .= 'X';
13135  $number -= 10;
13136  }
13137  while ($number >= 9) {
13138  $roman .= 'IX';
13139  $number -= 9;
13140  }
13141  while ($number >= 5) {
13142  $roman .= 'V';
13143  $number -= 5;
13144  }
13145  while ($number >= 4) {
13146  $roman .= 'IV';
13147  $number -= 4;
13148  }
13149  while ($number >= 1) {
13150  $roman .= 'I';
13151  --$number;
13152  }
13153  return $roman;
13154  }
13155 
13164  protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
13165  $size /= $this->k;
13166  $fill = '';
13167  $color = $this->fgcolor;
13168  $width = 0;
13169  $textitem = '';
13170  $tmpx = $this->x;
13171  $lspace = $this->GetStringWidth(' ');
13172  if ($listtype == '!') {
13173  // set default list type for unordered list
13174  $deftypes = array('disc', 'circle', 'square');
13175  $listtype = $deftypes[($listdepth - 1) % 3];
13176  } elseif ($listtype == '#') {
13177  // set default list type for ordered list
13178  $listtype = 'decimal';
13179  }
13180  switch ($listtype) {
13181  // unordered types
13182  case 'none': {
13183  break;
13184  }
13185  case 'disc': {
13186  $fill = 'F';
13187  }
13188  case 'circle': {
13189  $fill .= 'D';
13190  $r = $size / 6;
13191  $lspace += (2 * $r);
13192  if ($this->rtl) {
13193  $this->x = $this->w - $this->x - $lspace;
13194  } else {
13195  $this->x -= $lspace;
13196  }
13197  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
13198  break;
13199  }
13200  case 'square': {
13201  $l = $size / 3;
13202  $lspace += $l;
13203  if ($this->rtl) {
13204  $this->x = $this->w - $this->x - $lspace;
13205  } else {
13206  $this->x -= $lspace;
13207  }
13208  $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
13209  break;
13210  }
13211  // ordered types
13212 
13213  // $this->listcount[$this->listnum];
13214  // $textitem
13215  case '1':
13216  case 'decimal': {
13217  $textitem = $this->listcount[$this->listnum];
13218  break;
13219  }
13220  case 'decimal-leading-zero': {
13221  $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
13222  break;
13223  }
13224  case 'i':
13225  case 'lower-roman': {
13226  $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
13227  break;
13228  }
13229  case 'I':
13230  case 'upper-roman': {
13231  $textitem = $this->intToRoman($this->listcount[$this->listnum]);
13232  break;
13233  }
13234  case 'a':
13235  case 'lower-alpha':
13236  case 'lower-latin': {
13237  $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
13238  break;
13239  }
13240  case 'A':
13241  case 'upper-alpha':
13242  case 'upper-latin': {
13243  $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
13244  break;
13245  }
13246  case 'lower-greek': {
13247  $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
13248  break;
13249  }
13250  /*
13251  // Types to be implemented (special handling)
13252  case 'hebrew': {
13253  break;
13254  }
13255  case 'armenian': {
13256  break;
13257  }
13258  case 'georgian': {
13259  break;
13260  }
13261  case 'cjk-ideographic': {
13262  break;
13263  }
13264  case 'hiragana': {
13265  break;
13266  }
13267  case 'katakana': {
13268  break;
13269  }
13270  case 'hiragana-iroha': {
13271  break;
13272  }
13273  case 'katakana-iroha': {
13274  break;
13275  }
13276  */
13277  default: {
13278  $textitem = $this->listcount[$this->listnum];
13279  }
13280  }
13281  if (!$this->empty_string($textitem)) {
13282  // print ordered item
13283  if ($this->rtl) {
13284  $textitem = '.'.$textitem;
13285  } else {
13286  $textitem = $textitem.'.';
13287  }
13288  $lspace += $this->GetStringWidth($textitem);
13289  if ($this->rtl) {
13290  $this->x += $lspace;
13291  } else {
13292  $this->x -= $lspace;
13293  }
13294  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
13295  }
13296  $this->x = $tmpx;
13297  $this->lispacer = '';
13298  }
13299 
13306  protected function getGraphicVars() {
13307  $grapvars = array(
13308  'FontFamily' => $this->FontFamily,
13309  'FontStyle' => $this->FontStyle,
13310  'FontSizePt' => $this->FontSizePt,
13311  'rMargin' => $this->rMargin,
13312  'lMargin' => $this->lMargin,
13313  'cMargin' => $this->cMargin,
13314  'LineWidth' => $this->LineWidth,
13315  'linestyleWidth' => $this->linestyleWidth,
13316  'linestyleCap' => $this->linestyleCap,
13317  'linestyleJoin' => $this->linestyleJoin,
13318  'linestyleDash' => $this->linestyleDash,
13319  'DrawColor' => $this->DrawColor,
13320  'FillColor' => $this->FillColor,
13321  'TextColor' => $this->TextColor,
13322  'ColorFlag' => $this->ColorFlag,
13323  'bgcolor' => $this->bgcolor,
13324  'fgcolor' => $this->fgcolor,
13325  'htmlvspace' => $this->htmlvspace,
13326  'lasth' => $this->lasth
13327  );
13328  return $grapvars;
13329  }
13330 
13337  protected function setGraphicVars($gvars) {
13338  $this->FontFamily = $gvars['FontFamily'];
13339  $this->FontStyle = $gvars['FontStyle'];
13340  $this->FontSizePt = $gvars['FontSizePt'];
13341  $this->rMargin = $gvars['rMargin'];
13342  $this->lMargin = $gvars['lMargin'];
13343  $this->cMargin = $gvars['cMargin'];
13344  $this->LineWidth = $gvars['LineWidth'];
13345  $this->linestyleWidth = $gvars['linestyleWidth'];
13346  $this->linestyleCap = $gvars['linestyleCap'];
13347  $this->linestyleJoin = $gvars['linestyleJoin'];
13348  $this->linestyleDash = $gvars['linestyleDash'];
13349  $this->DrawColor = $gvars['DrawColor'];
13350  $this->FillColor = $gvars['FillColor'];
13351  $this->TextColor = $gvars['TextColor'];
13352  $this->ColorFlag = $gvars['ColorFlag'];
13353  $this->bgcolor = $gvars['bgcolor'];
13354  $this->fgcolor = $gvars['fgcolor'];
13355  $this->htmlvspace = $gvars['htmlvspace'];
13356  //$this->lasth = $gvars['lasth'];
13357  $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
13358  if (!$this->empty_string($this->FontFamily)) {
13359  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
13360  }
13361  }
13362 
13370  protected function getObjFilename($name) {
13371  return tempnam(K_PATH_CACHE, $name.'_');
13372  }
13373 
13382  protected function writeDiskCache($filename, $data, $append=false) {
13383  if ($append) {
13384  $fmode = 'ab+';
13385  } else {
13386  $fmode = 'wb+';
13387  }
13388  $f = @fopen($filename, $fmode);
13389  if (!$f) {
13390  $this->Error('Unable to write cache file: '.$filename);
13391  } else {
13392  fwrite($f, $data);
13393  fclose($f);
13394  }
13395  // update file lenght (needed for transactions)
13396  if (!isset($this->cache_file_lenght['_'.$filename])) {
13397  $this->cache_file_lenght['_'.$filename] = strlen($data);
13398  } else {
13399  $this->cache_file_lenght['_'.$filename] += strlen($data);
13400  }
13401  }
13402 
13410  protected function readDiskCache($filename) {
13411  return file_get_contents($filename);
13412  }
13413 
13420  protected function setBuffer($data) {
13421  $this->bufferlen += strlen($data);
13422  if ($this->diskcache) {
13423  if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
13424  $this->buffer = $this->getObjFilename('buffer');
13425  }
13426  $this->writeDiskCache($this->buffer, $data, true);
13427  } else {
13428  $this->buffer .= $data;
13429  }
13430  }
13431 
13438  protected function getBuffer() {
13439  if ($this->diskcache) {
13440  return $this->readDiskCache($this->buffer);
13441  } else {
13442  return $this->buffer;
13443  }
13444  }
13445 
13454  protected function setPageBuffer($page, $data, $append=false) {
13455  if ($this->diskcache) {
13456  if (!isset($this->pages[$page])) {
13457  $this->pages[$page] = $this->getObjFilename('page'.$page);
13458  }
13459  $this->writeDiskCache($this->pages[$page], $data, $append);
13460  } else {
13461  if ($append) {
13462  $this->pages[$page] .= $data;
13463  } else {
13464  $this->pages[$page] = $data;
13465  }
13466  }
13467  if ($append AND isset($this->pagelen[$page])) {
13468  $this->pagelen[$page] += strlen($data);
13469  } else {
13470  $this->pagelen[$page] = strlen($data);
13471  }
13472  }
13473 
13481  protected function getPageBuffer($page) {
13482  if ($this->diskcache) {
13483  return $this->readDiskCache($this->pages[$page]);
13484  } elseif (isset($this->pages[$page])) {
13485  return $this->pages[$page];
13486  }
13487  return false;
13488  }
13489 
13497  protected function setImageBuffer($image, $data) {
13498  if ($this->diskcache) {
13499  if (!isset($this->images[$image])) {
13500  $this->images[$image] = $this->getObjFilename('image'.$image);
13501  }
13502  $this->writeDiskCache($this->images[$image], serialize($data));
13503  } else {
13504  $this->images[$image] = $data;
13505  }
13506  if (!in_array($image, $this->imagekeys)) {
13507  $this->imagekeys[] = $image;
13508  }
13509  ++$this->numimages;
13510  }
13511 
13520  protected function setImageSubBuffer($image, $key, $data) {
13521  if (!isset($this->images[$image])) {
13522  $this->setImageBuffer($image, array());
13523  }
13524  if ($this->diskcache) {
13525  $tmpimg = $this->getImageBuffer($image);
13526  $tmpimg[$key] = $data;
13527  $this->writeDiskCache($this->images[$image], serialize($tmpimg));
13528  } else {
13529  $this->images[$image][$key] = $data;
13530  }
13531  }
13532 
13540  protected function getImageBuffer($image) {
13541  if ($this->diskcache AND isset($this->images[$image])) {
13542  return unserialize($this->readDiskCache($this->images[$image]));
13543  } elseif (isset($this->images[$image])) {
13544  return $this->images[$image];
13545  }
13546  return false;
13547  }
13548 
13556  protected function setFontBuffer($font, $data) {
13557  if ($this->diskcache) {
13558  if (!isset($this->fonts[$font])) {
13559  $this->fonts[$font] = $this->getObjFilename('font');
13560  }
13561  $this->writeDiskCache($this->fonts[$font], serialize($data));
13562  } else {
13563  $this->fonts[$font] = $data;
13564  }
13565  if (!in_array($font, $this->fontkeys)) {
13566  $this->fontkeys[] = $font;
13567  }
13568  }
13569 
13578  protected function setFontSubBuffer($font, $key, $data) {
13579  if (!isset($this->fonts[$font])) {
13580  $this->setFontBuffer($font, array());
13581  }
13582  if ($this->diskcache) {
13583  $tmpfont = $this->getFontBuffer($font);
13584  $tmpfont[$key] = $data;
13585  $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
13586  } else {
13587  $this->fonts[$font][$key] = $data;
13588  }
13589  }
13590 
13598  protected function getFontBuffer($font) {
13599  if ($this->diskcache AND isset($this->fonts[$font])) {
13600  return unserialize($this->readDiskCache($this->fonts[$font]));
13601  } elseif (isset($this->fonts[$font])) {
13602  return $this->fonts[$font];
13603  }
13604  return false;
13605  }
13606 
13615  public function movePage($frompage, $topage) {
13616  if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
13617  return false;
13618  }
13619  if ($frompage == $this->page) {
13620  // close the page before moving it
13621  $this->endPage();
13622  }
13623  // move all page-related states
13624  $tmppage = $this->pages[$frompage];
13625  $tmppagedim = $this->pagedim[$frompage];
13626  $tmppagelen = $this->pagelen[$frompage];
13627  $tmpintmrk = $this->intmrk[$frompage];
13628  if (isset($this->footerpos[$frompage])) {
13629  $tmpfooterpos = $this->footerpos[$frompage];
13630  }
13631  if (isset($this->footerlen[$frompage])) {
13632  $tmpfooterlen = $this->footerlen[$frompage];
13633  }
13634  if (isset($this->transfmrk[$frompage])) {
13635  $tmptransfmrk = $this->transfmrk[$frompage];
13636  }
13637  if (isset($this->PageAnnots[$frompage])) {
13638  $tmpannots = $this->PageAnnots[$frompage];
13639  }
13640  if (isset($this->newpagegroup[$frompage])) {
13641  $tmpnewpagegroup = $this->newpagegroup[$frompage];
13642  }
13643  for ($i = $frompage; $i > $topage; --$i) {
13644  $j = $i - 1;
13645  // shift pages down
13646  $this->pages[$i] = $this->pages[$j];
13647  $this->pagedim[$i] = $this->pagedim[$j];
13648  $this->pagelen[$i] = $this->pagelen[$j];
13649  $this->intmrk[$i] = $this->intmrk[$j];
13650  if (isset($this->footerpos[$j])) {
13651  $this->footerpos[$i] = $this->footerpos[$j];
13652  } elseif (isset($this->footerpos[$i])) {
13653  unset($this->footerpos[$i]);
13654  }
13655  if (isset($this->footerlen[$j])) {
13656  $this->footerlen[$i] = $this->footerlen[$j];
13657  } elseif (isset($this->footerlen[$i])) {
13658  unset($this->footerlen[$i]);
13659  }
13660  if (isset($this->transfmrk[$j])) {
13661  $this->transfmrk[$i] = $this->transfmrk[$j];
13662  } elseif (isset($this->transfmrk[$i])) {
13663  unset($this->transfmrk[$i]);
13664  }
13665  if (isset($this->PageAnnots[$j])) {
13666  $this->PageAnnots[$i] = $this->PageAnnots[$j];
13667  } elseif (isset($this->PageAnnots[$i])) {
13668  unset($this->PageAnnots[$i]);
13669  }
13670  if (isset($this->newpagegroup[$j])) {
13671  $this->newpagegroup[$i] = $this->newpagegroup[$j];
13672  } elseif (isset($this->newpagegroup[$i])) {
13673  unset($this->newpagegroup[$i]);
13674  }
13675  }
13676  $this->pages[$topage] = $tmppage;
13677  $this->pagedim[$topage] = $tmppagedim;
13678  $this->pagelen[$topage] = $tmppagelen;
13679  $this->intmrk[$topage] = $tmpintmrk;
13680  if (isset($tmpfooterpos)) {
13681  $this->footerpos[$topage] = $tmpfooterpos;
13682  } elseif (isset($this->footerpos[$topage])) {
13683  unset($this->footerpos[$topage]);
13684  }
13685  if (isset($tmpfooterlen)) {
13686  $this->footerlen[$topage] = $tmpfooterlen;
13687  } elseif (isset($this->footerlen[$topage])) {
13688  unset($this->footerlen[$topage]);
13689  }
13690  if (isset($tmptransfmrk)) {
13691  $this->transfmrk[$topage] = $tmptransfmrk;
13692  } elseif (isset($this->transfmrk[$topage])) {
13693  unset($this->transfmrk[$topage]);
13694  }
13695  if (isset($tmpannots)) {
13696  $this->PageAnnots[$topage] = $tmpannots;
13697  } elseif (isset($this->PageAnnots[$topage])) {
13698  unset($this->PageAnnots[$topage]);
13699  }
13700  if (isset($tmpnewpagegroup)) {
13701  $this->newpagegroup[$topage] = $tmpnewpagegroup;
13702  } elseif (isset($this->newpagegroup[$topage])) {
13703  unset($this->newpagegroup[$topage]);
13704  }
13705  // adjust outlines
13706  $tmpoutlines = $this->outlines;
13707  foreach ($tmpoutlines as $key => $outline) {
13708  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
13709  $this->outlines[$key]['p'] = $outline['p'] + 1;
13710  } elseif ($outline['p'] == $frompage) {
13711  $this->outlines[$key]['p'] = $topage;
13712  }
13713  }
13714  // adjust links
13715  $tmplinks = $this->links;
13716  foreach ($tmplinks as $key => $link) {
13717  if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
13718  $this->links[$key][0] = $link[0] + 1;
13719  } elseif ($link[0] == $frompage) {
13720  $this->links[$key][0] = $topage;
13721  }
13722  }
13723  // adjust javascript
13724  $tmpjavascript = $this->javascript;
13725  global $jfrompage, $jtopage;
13726  $jfrompage = $frompage;
13727  $jtopage = $topage;
13728  $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13729  create_function('$matches', 'global $jfrompage, $jtopage;
13730  $pagenum = intval($matches[3]) + 1;
13731  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
13732  $newpage = ($pagenum + 1);
13733  } elseif ($pagenum == $jfrompage) {
13734  $newpage = $jtopage;
13735  } else {
13736  $newpage = $pagenum;
13737  }
13738  --$newpage;
13739  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13740  // return to last page
13741  $this->lastPage(true);
13742  return true;
13743  }
13744 
13752  public function deletePage($page) {
13753  if ($page > $this->numpages) {
13754  return false;
13755  }
13756  // delete current page
13757  unset($this->pages[$page]);
13758  unset($this->pagedim[$page]);
13759  unset($this->pagelen[$page]);
13760  unset($this->intmrk[$page]);
13761  if (isset($this->footerpos[$page])) {
13762  unset($this->footerpos[$page]);
13763  }
13764  if (isset($this->footerlen[$page])) {
13765  unset($this->footerlen[$page]);
13766  }
13767  if (isset($this->transfmrk[$page])) {
13768  unset($this->transfmrk[$page]);
13769  }
13770  if (isset($this->PageAnnots[$page])) {
13771  unset($this->PageAnnots[$page]);
13772  }
13773  if (isset($this->newpagegroup[$page])) {
13774  unset($this->newpagegroup[$page]);
13775  }
13776  if (isset($this->pageopen[$page])) {
13777  unset($this->pageopen[$page]);
13778  }
13779  // update remaining pages
13780  for ($i = $page; $i < $this->numpages; ++$i) {
13781  $j = $i + 1;
13782  // shift pages
13783  $this->pages[$i] = $this->pages[$j];
13784  $this->pagedim[$i] = $this->pagedim[$j];
13785  $this->pagelen[$i] = $this->pagelen[$j];
13786  $this->intmrk[$i] = $this->intmrk[$j];
13787  if (isset($this->footerpos[$j])) {
13788  $this->footerpos[$i] = $this->footerpos[$j];
13789  } elseif (isset($this->footerpos[$i])) {
13790  unset($this->footerpos[$i]);
13791  }
13792  if (isset($this->footerlen[$j])) {
13793  $this->footerlen[$i] = $this->footerlen[$j];
13794  } elseif (isset($this->footerlen[$i])) {
13795  unset($this->footerlen[$i]);
13796  }
13797  if (isset($this->transfmrk[$j])) {
13798  $this->transfmrk[$i] = $this->transfmrk[$j];
13799  } elseif (isset($this->transfmrk[$i])) {
13800  unset($this->transfmrk[$i]);
13801  }
13802  if (isset($this->PageAnnots[$j])) {
13803  $this->PageAnnots[$i] = $this->PageAnnots[$j];
13804  } elseif (isset($this->PageAnnots[$i])) {
13805  unset($this->PageAnnots[$i]);
13806  }
13807  if (isset($this->newpagegroup[$j])) {
13808  $this->newpagegroup[$i] = $this->newpagegroup[$j];
13809  } elseif (isset($this->newpagegroup[$i])) {
13810  unset($this->newpagegroup[$i]);
13811  }
13812  if (isset($this->pageopen[$j])) {
13813  $this->pageopen[$i] = $this->pageopen[$j];
13814  } elseif (isset($this->pageopen[$i])) {
13815  unset($this->pageopen[$i]);
13816  }
13817  }
13818  // remove last page
13819  unset($this->pages[$this->numpages]);
13820  unset($this->pagedim[$this->numpages]);
13821  unset($this->pagelen[$this->numpages]);
13822  unset($this->intmrk[$this->numpages]);
13823  if (isset($this->footerpos[$this->numpages])) {
13824  unset($this->footerpos[$this->numpages]);
13825  }
13826  if (isset($this->footerlen[$this->numpages])) {
13827  unset($this->footerlen[$this->numpages]);
13828  }
13829  if (isset($this->transfmrk[$this->numpages])) {
13830  unset($this->transfmrk[$this->numpages]);
13831  }
13832  if (isset($this->PageAnnots[$this->numpages])) {
13833  unset($this->PageAnnots[$this->numpages]);
13834  }
13835  if (isset($this->newpagegroup[$this->numpages])) {
13836  unset($this->newpagegroup[$this->numpages]);
13837  }
13838  if (isset($this->pageopen[$this->numpages])) {
13839  unset($this->pageopen[$this->numpages]);
13840  }
13841  --$this->numpages;
13842  $this->page = $this->numpages;
13843  // adjust outlines
13844  $tmpoutlines = $this->outlines;
13845  foreach ($tmpoutlines as $key => $outline) {
13846  if ($outline['p'] > $page) {
13847  $this->outlines[$key]['p'] = $outline['p'] - 1;
13848  } elseif ($outline['p'] == $page) {
13849  unset($this->outlines[$key]);
13850  }
13851  }
13852  // adjust links
13853  $tmplinks = $this->links;
13854  foreach ($tmplinks as $key => $link) {
13855  if ($link[0] > $page) {
13856  $this->links[$key][0] = $link[0] - 1;
13857  } elseif ($link[0] == $page) {
13858  unset($this->links[$key]);
13859  }
13860  }
13861  // adjust javascript
13862  $tmpjavascript = $this->javascript;
13863  global $jpage;
13864  $jpage = $page;
13865  $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13866  create_function('$matches', 'global $jpage;
13867  $pagenum = intval($matches[3]) + 1;
13868  if ($pagenum >= $jpage) {
13869  $newpage = ($pagenum - 1);
13870  } elseif ($pagenum == $jpage) {
13871  $newpage = 1;
13872  } else {
13873  $newpage = $pagenum;
13874  }
13875  --$newpage;
13876  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13877  // return to last page
13878  $this->lastPage(true);
13879  return true;
13880  }
13881 
13892  public function addTOC($page='', $numbersfont='', $filler='.') {
13893  $fontsize = $this->FontSizePt;
13894  $fontfamily = $this->FontFamily;
13895  $fontstyle = $this->FontStyle;
13896  $w = $this->w - $this->lMargin - $this->rMargin;
13897  $spacer = $this->GetStringWidth(' ') * 4;
13898  $page_first = $this->getPage();
13899  $lmargin = $this->lMargin;
13900  $rmargin = $this->rMargin;
13901  $x_start = $this->GetX();
13902  if ($this->empty_string($numbersfont)) {
13903  $numbersfont = $this->default_monospaced_font;
13904  }
13905  if ($this->empty_string($filler)) {
13906  $filler = ' ';
13907  }
13908  if ($this->empty_string($page)) {
13909  $gap = ' ';
13910  } else {
13911  $gap = '';
13912  }
13913  foreach ($this->outlines as $key => $outline) {
13914  if ($this->rtl) {
13915  $aligntext = 'R';
13916  $alignnum = 'L';
13917  } else {
13918  $aligntext = 'L';
13919  $alignnum = 'R';
13920  }
13921  if ($outline['l'] == 0) {
13922  $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
13923  } else {
13924  $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
13925  }
13926  $indent = ($spacer * $outline['l']);
13927  if ($this->rtl) {
13928  $this->rMargin += $indent;
13929  $this->x -= $indent;
13930  } else {
13931  $this->lMargin += $indent;
13932  $this->x += $indent;
13933  }
13934  $link = $this->AddLink();
13935  $this->SetLink($link, 0, $outline['p']);
13936  // write the text
13937  $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
13938  $this->SetFont($numbersfont, $fontstyle, $fontsize);
13939  if ($this->empty_string($page)) {
13940  $pagenum = $outline['p'];
13941  } else {
13942  // placemark to be replaced with the correct number
13943  $pagenum = '{#'.($outline['p']).'}';
13944  if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13945  $pagenum = '{'.$pagenum.'}';
13946  }
13947  }
13948  $numwidth = $this->GetStringWidth($pagenum);
13949  if ($this->rtl) {
13950  $tw = $this->x - $this->lMargin;
13951  } else {
13952  $tw = $this->w - $this->rMargin - $this->x;
13953  }
13954  $fw = $tw - $numwidth - $this->GetStringWidth(' ');
13955  $numfills = floor($fw / $this->GetStringWidth($filler));
13956  if ($numfills > 0) {
13957  $rowfill = str_repeat($filler, $numfills);
13958  } else {
13959  $rowfill = '';
13960  }
13961  if ($this->rtl) {
13962  $pagenum = $pagenum.$gap.$rowfill.' ';
13963  } else {
13964  $pagenum = ' '.$rowfill.$gap.$pagenum;
13965  }
13966  // write the number
13967  //$this->SetX($x_start);
13968  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
13969  $this->SetX($x_start);
13970  $this->lMargin = $lmargin;
13971  $this->rMargin = $rmargin;
13972  }
13973  $page_last = $this->getPage();
13974  $numpages = $page_last - $page_first + 1;
13975  if (!$this->empty_string($page)) {
13976  for ($p = $page_first; $p <= $page_last; ++$p) {
13977  // get page data
13978  $temppage = $this->getPageBuffer($p);
13979  for ($n = 1; $n <= $this->numpages; ++$n) {
13980  // update page numbers
13981  $k = '{#'.$n.'}';
13982  $ku = '{'.$k.'}';
13983  $alias_a = $this->_escape($k);
13984  $alias_au = $this->_escape('{'.$k.'}');
13985  if ($this->isunicode) {
13986  $alias_b = $this->_escape($this->UTF8ToLatin1($k));
13987  $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
13988  $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
13989  $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
13990  }
13991  if ($n >= $page) {
13992  $np = $n + $numpages;
13993  } else {
13994  $np = $n;
13995  }
13996  $ns = $this->formatTOCPageNumber($np);
13997  $nu = $ns;
13998  $sdiff = strlen($k) - strlen($ns) - 1;
13999  $sdiffu = strlen($ku) - strlen($ns) - 1;
14000  $sfill = str_repeat($filler, $sdiff);
14001  $sfillu = str_repeat($filler, $sdiffu);
14002  if ($this->rtl) {
14003  $ns = $ns.' '.$sfill;
14004  $nu = $nu.' '.$sfillu;
14005  } else {
14006  $ns = $sfill.' '.$ns;
14007  $nu = $sfillu.' '.$nu;
14008  }
14009  $nu = $this->UTF8ToUTF16BE($nu, false);
14010  $temppage = str_replace($alias_au, $nu, $temppage);
14011  if ($this->isunicode) {
14012  $temppage = str_replace($alias_bu, $nu, $temppage);
14013  $temppage = str_replace($alias_cu, $nu, $temppage);
14014  $temppage = str_replace($alias_b, $ns, $temppage);
14015  $temppage = str_replace($alias_c, $ns, $temppage);
14016  }
14017  $temppage = str_replace($alias_a, $ns, $temppage);
14018  }
14019  // save changes
14020  $this->setPageBuffer($p, $temppage);
14021  }
14022  // move pages
14023  for ($i = 0; $i < $numpages; ++$i) {
14024  $this->movePage($page_last, $page);
14025  }
14026  }
14027  $this->SetFont($fontfamily, $fontstyle, $fontsize);
14028  }
14029 
14035  public function startTransaction() {
14036  if (isset($this->objcopy)) {
14037  // remove previous copy
14038  $this->commitTransaction();
14039  }
14040  // clone current object
14041  $this->objcopy = $this->objclone($this);
14042  }
14043 
14049  public function commitTransaction() {
14050  if (isset($this->objcopy)) {
14051  $this->objcopy->_destroy(true, true);
14052  unset($this->objcopy);
14053  }
14054  }
14055 
14062  public function rollbackTransaction() {
14063  if (isset($this->objcopy)) {
14064  if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
14065  // truncate files to previous values
14066  foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
14067  $file = substr($file, 1);
14068  $handle = fopen($file, 'r+');
14069  ftruncate($handle, $lenght);
14070  }
14071  }
14072  $this->_destroy(true, true);
14073  return $this->objcopy;
14074  }
14075  return $this;
14076  }
14077 
14085  public function objclone($object) {
14086  return @clone($object);
14087  }
14088 
14096  public function empty_string($str) {
14097  return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
14098  }
14099 
14100  } // END OF TCPDF CLASS
14101 }
14102 //============================================================+
14103 // END OF FILE
14104 //============================================================+
14105 ?>