PageRenderTime 176ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 1ms

/framework/lib/classes/PHPExcel/Shared/PDF/tcpdf.php

https://bitbucket.org/designbyheart/original
PHP | 13678 lines | 8325 code | 518 blank | 4835 comment | 1728 complexity | 48aaa955d5f2857a2437e30f5e29414a MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. //============================================================+
  3. // File name : tcpdf.php
  4. // Version : 5.9.009
  5. // Begin : 2002-08-03
  6. // Last Update : 2010-10-21
  7. // Author : Nicola Asuni - Tecnick.com S.r.l - Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
  8. // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
  9. // -------------------------------------------------------------------
  10. // Copyright (C) 2002-2010 Nicola Asuni - Tecnick.com S.r.l.
  11. //
  12. // This file is part of TCPDF software library.
  13. //
  14. // TCPDF is free software: you can redistribute it and/or modify it
  15. // under the terms of the GNU Lesser General Public License as
  16. // published by the Free Software Foundation, either version 3 of the
  17. // License, or (at your option) any later version.
  18. //
  19. // TCPDF is distributed in the hope that it will be useful, but
  20. // WITHOUT ANY WARRANTY; without even the implied warranty of
  21. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  22. // See the GNU Lesser General Public License for more details.
  23. //
  24. // You should have received a copy of the GNU Lesser General Public License
  25. // along with TCPDF. If not, see <http://www.gnu.org/licenses/>.
  26. //
  27. // See LICENSE.TXT file for more information.
  28. // -------------------------------------------------------------------
  29. //
  30. // Description : This is a PHP class for generating PDF documents without
  31. // requiring external extensions.
  32. //
  33. // NOTE:
  34. // This class was originally derived in 2002 from the Public
  35. // Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
  36. // but now is almost entirely rewritten and contains thousands of
  37. // new lines of code and hundreds new features.
  38. //
  39. // Main features:
  40. // * no external libraries are required for the basic functions;
  41. // * all standard page formats, custom page formats, custom margins and units of measure;
  42. // * UTF-8 Unicode and Right-To-Left languages;
  43. // * TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
  44. // * font subsetting;
  45. // * methods to publish some XHTML + CSS code, Javascript and Forms;
  46. // * images, graphic (geometric figures) and transformation methods;
  47. // * supports JPEG, PNG and SVG 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)
  48. // * 1D and 2D barcodes: 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, QR-Code, PDF417;
  49. // * Grayscale, RGB, CMYK, Spot Colors and Transparencies;
  50. // * automatic page header and footer management;
  51. // * document encryption up to 256 bit and digital signature certifications;
  52. // * transactions to UNDO commands;
  53. // * PDF annotations, including links, text and file attachments;
  54. // * text rendering modes (fill, stroke and clipping);
  55. // * multiple columns mode;
  56. // * no-write page regions;
  57. // * bookmarks and table of content;
  58. // * text hyphenation;
  59. // * text stretching and spacing (tracking/kerning);
  60. // * automatic page break, line break and text alignments including justification;
  61. // * automatic page numbering and page groups;
  62. // * move and delete pages;
  63. // * page compression (requires php-zlib extension);
  64. // * XOBject Templates;
  65. //
  66. // -----------------------------------------------------------
  67. // THANKS TO:
  68. //
  69. // Olivier Plathey (http://www.fpdf.org) for original FPDF.
  70. // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
  71. // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
  72. // Warren Sherliker (wsherliker@gmail.com) for better image handling.
  73. // dullus for text Justification.
  74. // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
  75. // Patrick Benny for text stretch suggestion on Cell().
  76. // Johannes Güntert for JavaScript support.
  77. // Denis Van Nuffelen for Dynamic Form.
  78. // Jacek Czekaj for multibyte justification
  79. // Anthony Ferrara for the reintroduction of legacy image methods.
  80. // Sourceforge user 1707880 (hucste) for line-trough mode.
  81. // Larry Stanbery for page groups.
  82. // Martin Hall-May for transparency.
  83. // Aaron C. Spike for Polycurve method.
  84. // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
  85. // Moritz Wagner and Andreas Wurmser for graphic functions.
  86. // Andrew Whitehead for core fonts support.
  87. // Esteban Joël Marín for OpenType font conversion.
  88. // Teus Hagen for several suggestions and fixes.
  89. // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
  90. // Kosmas Papachristos for some CSS improvements.
  91. // Marcel Partap for some fixes.
  92. // Won Kyu Park for several suggestions, fixes and patches.
  93. // Dominik Dzienia for QR-code support.
  94. // Laurent Minguet for some suggestions.
  95. // Christian Deligant for some suggestions and fixes.
  96. // Anyone that has reported a bug or sent a suggestion.
  97. //============================================================+
  98. /**
  99. * This is a PHP class for generating PDF documents without requiring external extensions.<br>
  100. * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
  101. * <h3>TCPDF main features are:</h3>
  102. * <ul>
  103. * <li>no external libraries are required for the basic functions;</li>
  104. * <li>all standard page formats, custom page formats, custom margins and units of measure;</li>
  105. * <li>UTF-8 Unicode and Right-To-Left languages;</li>
  106. * <li>TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>
  107. * <li>font subsetting;</li>
  108. * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li>
  109. * <li>images, graphic (geometric figures) and transformation methods;
  110. * <li>supports JPEG, PNG and SVG 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)</li>
  111. * <li>1D and 2D barcodes: 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, QR-Code, PDF417;</li>
  112. * <li>Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
  113. * <li>automatic page header and footer management;</li>
  114. * <li>document encryption up to 256 bit and digital signature certifications;</li>
  115. * <li>transactions to UNDO commands;</li>
  116. * <li>PDF annotations, including links, text and file attachments;</li>
  117. * <li>text rendering modes (fill, stroke and clipping);</li>
  118. * <li>multiple columns mode;</li>
  119. * <li>no-write page regions;</li>
  120. * <li>bookmarks and table of content;</li>
  121. * <li>text hyphenation;</li>
  122. * <li>text stretching and spacing (tracking/kerning);</li>
  123. * <li>automatic page break, line break and text alignments including justification;</li>
  124. * <li>automatic page numbering and page groups;</li>
  125. * <li>move and delete pages;</li>
  126. * <li>page compression (requires php-zlib extension);</li>
  127. * <li>XOBject Templates;</li>
  128. * </ul>
  129. * Tools to encode your unicode fonts are on fonts/utils directory.</p>
  130. * @package com.tecnick.tcpdf
  131. * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
  132. * @author Nicola Asuni
  133. * @copyright 2002-2010 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
  134. * @link http://www.tcpdf.org
  135. * @license http://www.gnu.org/copyleft/lesser.html LGPL
  136. * @version 5.9.009
  137. */
  138. /**
  139. * main configuration file
  140. * (define the K_TCPDF_EXTERNAL_CONFIG constant to skip this file)
  141. */
  142. require_once(dirname(__FILE__).'/config/tcpdf_config.php');
  143. /**
  144. * define default PDF document producer
  145. */
  146. define('PDF_PRODUCER', 'TCPDF 5.9.009 (http://www.tcpdf.org)');
  147. /**
  148. * This is a PHP class for generating PDF documents without requiring external extensions.<br>
  149. * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
  150. * @name TCPDF
  151. * @package com.tecnick.tcpdf
  152. * @version 5.9.009
  153. * @author Nicola Asuni - info@tecnick.com
  154. * @link http://www.tcpdf.org
  155. * @license http://www.gnu.org/copyleft/lesser.html LGPL
  156. */
  157. class TCPDF {
  158. // Protected properties
  159. /**
  160. * @var current page number
  161. * @access protected
  162. */
  163. protected $page;
  164. /**
  165. * @var current object number
  166. * @access protected
  167. */
  168. protected $n;
  169. /**
  170. * @var array of object offsets
  171. * @access protected
  172. */
  173. protected $offsets;
  174. /**
  175. * @var buffer holding in-memory PDF
  176. * @access protected
  177. */
  178. protected $buffer;
  179. /**
  180. * @var array containing pages
  181. * @access protected
  182. */
  183. protected $pages = array();
  184. /**
  185. * @var current document state
  186. * @access protected
  187. */
  188. protected $state;
  189. /**
  190. * @var compression flag
  191. * @access protected
  192. */
  193. protected $compress;
  194. /**
  195. * @var current page orientation (P = Portrait, L = Landscape)
  196. * @access protected
  197. */
  198. protected $CurOrientation;
  199. /**
  200. * @var Page dimensions
  201. * @access protected
  202. */
  203. protected $pagedim = array();
  204. /**
  205. * @var scale factor (number of points in user unit)
  206. * @access protected
  207. */
  208. protected $k;
  209. /**
  210. * @var width of page format in points
  211. * @access protected
  212. */
  213. protected $fwPt;
  214. /**
  215. * @var height of page format in points
  216. * @access protected
  217. */
  218. protected $fhPt;
  219. /**
  220. * @var current width of page in points
  221. * @access protected
  222. */
  223. protected $wPt;
  224. /**
  225. * @var current height of page in points
  226. * @access protected
  227. */
  228. protected $hPt;
  229. /**
  230. * @var current width of page in user unit
  231. * @access protected
  232. */
  233. protected $w;
  234. /**
  235. * @var current height of page in user unit
  236. * @access protected
  237. */
  238. protected $h;
  239. /**
  240. * @var left margin
  241. * @access protected
  242. */
  243. protected $lMargin;
  244. /**
  245. * @var top margin
  246. * @access protected
  247. */
  248. protected $tMargin;
  249. /**
  250. * @var right margin
  251. * @access protected
  252. */
  253. protected $rMargin;
  254. /**
  255. * @var page break margin
  256. * @access protected
  257. */
  258. protected $bMargin;
  259. /**
  260. * @var array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left)
  261. * @since 5.9.000 (2010-10-03)
  262. * @access protected
  263. */
  264. protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
  265. /**
  266. * @var array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left)
  267. * @since 5.9.000 (2010-10-04)
  268. * @access protected
  269. */
  270. protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
  271. /**
  272. * @var current horizontal position in user unit for cell positioning
  273. * @access protected
  274. */
  275. protected $x;
  276. /**
  277. * @var current vertical position in user unit for cell positioning
  278. * @access protected
  279. */
  280. protected $y;
  281. /**
  282. * @var height of last cell printed
  283. * @access protected
  284. */
  285. protected $lasth;
  286. /**
  287. * @var line width in user unit
  288. * @access protected
  289. */
  290. protected $LineWidth;
  291. /**
  292. * @var array of standard font names
  293. * @access protected
  294. */
  295. protected $CoreFonts;
  296. /**
  297. * @var array of used fonts
  298. * @access protected
  299. */
  300. protected $fonts = array();
  301. /**
  302. * @var array of font files
  303. * @access protected
  304. */
  305. protected $FontFiles = array();
  306. /**
  307. * @var array of encoding differences
  308. * @access protected
  309. */
  310. protected $diffs = array();
  311. /**
  312. * @var array of used images
  313. * @access protected
  314. */
  315. protected $images = array();
  316. /**
  317. * @var array of Annotations in pages
  318. * @access protected
  319. */
  320. protected $PageAnnots = array();
  321. /**
  322. * @var array of internal links
  323. * @access protected
  324. */
  325. protected $links = array();
  326. /**
  327. * @var current font family
  328. * @access protected
  329. */
  330. protected $FontFamily;
  331. /**
  332. * @var current font style
  333. * @access protected
  334. */
  335. protected $FontStyle;
  336. /**
  337. * @var current font ascent (distance between font top and baseline)
  338. * @access protected
  339. * @since 2.8.000 (2007-03-29)
  340. */
  341. protected $FontAscent;
  342. /**
  343. * @var current font descent (distance between font bottom and baseline)
  344. * @access protected
  345. * @since 2.8.000 (2007-03-29)
  346. */
  347. protected $FontDescent;
  348. /**
  349. * @var underlining flag
  350. * @access protected
  351. */
  352. protected $underline;
  353. /**
  354. * @var overlining flag
  355. * @access protected
  356. */
  357. protected $overline;
  358. /**
  359. * @var current font info
  360. * @access protected
  361. */
  362. protected $CurrentFont;
  363. /**
  364. * @var current font size in points
  365. * @access protected
  366. */
  367. protected $FontSizePt;
  368. /**
  369. * @var current font size in user unit
  370. * @access protected
  371. */
  372. protected $FontSize;
  373. /**
  374. * @var commands for drawing color
  375. * @access protected
  376. */
  377. protected $DrawColor;
  378. /**
  379. * @var commands for filling color
  380. * @access protected
  381. */
  382. protected $FillColor;
  383. /**
  384. * @var commands for text color
  385. * @access protected
  386. */
  387. protected $TextColor;
  388. /**
  389. * @var indicates whether fill and text colors are different
  390. * @access protected
  391. */
  392. protected $ColorFlag;
  393. /**
  394. * @var automatic page breaking
  395. * @access protected
  396. */
  397. protected $AutoPageBreak;
  398. /**
  399. * @var threshold used to trigger page breaks
  400. * @access protected
  401. */
  402. protected $PageBreakTrigger;
  403. /**
  404. * @var flag set when processing footer
  405. * @access protected
  406. */
  407. protected $InFooter = false;
  408. /**
  409. * @var zoom display mode
  410. * @access protected
  411. */
  412. protected $ZoomMode;
  413. /**
  414. * @var layout display mode
  415. * @access protected
  416. */
  417. protected $LayoutMode;
  418. /**
  419. * @var title
  420. * @access protected
  421. */
  422. protected $title = '';
  423. /**
  424. * @var subject
  425. * @access protected
  426. */
  427. protected $subject = '';
  428. /**
  429. * @var author
  430. * @access protected
  431. */
  432. protected $author = '';
  433. /**
  434. * @var keywords
  435. * @access protected
  436. */
  437. protected $keywords = '';
  438. /**
  439. * @var creator
  440. * @access protected
  441. */
  442. protected $creator = '';
  443. /**
  444. * @var alias for total number of pages
  445. * @access protected
  446. */
  447. protected $AliasNbPages = '{nb}';
  448. /**
  449. * @var alias for page number
  450. * @access protected
  451. */
  452. protected $AliasNumPage = '{pnb}';
  453. /**
  454. * @var right-bottom corner X coordinate of inserted image
  455. * @since 2002-07-31
  456. * @author Nicola Asuni
  457. * @access protected
  458. */
  459. protected $img_rb_x;
  460. /**
  461. * @var right-bottom corner Y coordinate of inserted image
  462. * @since 2002-07-31
  463. * @author Nicola Asuni
  464. * @access protected
  465. */
  466. protected $img_rb_y;
  467. /**
  468. * @var adjusting factor to convert pixels to user units.
  469. * @since 2004-06-14
  470. * @author Nicola Asuni
  471. * @access protected
  472. */
  473. protected $imgscale = 1;
  474. /**
  475. * @var boolean set to true when the input text is unicode (require unicode fonts)
  476. * @since 2005-01-02
  477. * @author Nicola Asuni
  478. * @access protected
  479. */
  480. protected $isunicode = false;
  481. /**
  482. * @var object containing unicode data
  483. * @since 5.9.004 (2010-10-18)
  484. * @author Nicola Asuni
  485. * @access protected
  486. */
  487. protected $unicode;
  488. /**
  489. * @var PDF version
  490. * @since 1.5.3
  491. * @access protected
  492. */
  493. protected $PDFVersion = '1.7';
  494. /**
  495. * @var Minimum distance between header and top page margin.
  496. * @access protected
  497. */
  498. protected $header_margin;
  499. /**
  500. * @var Minimum distance between footer and bottom page margin.
  501. * @access protected
  502. */
  503. protected $footer_margin;
  504. /**
  505. * @var original left margin value
  506. * @access protected
  507. * @since 1.53.0.TC013
  508. */
  509. protected $original_lMargin;
  510. /**
  511. * @var original right margin value
  512. * @access protected
  513. * @since 1.53.0.TC013
  514. */
  515. protected $original_rMargin;
  516. /**
  517. * @var Header font.
  518. * @access protected
  519. */
  520. protected $header_font;
  521. /**
  522. * @var Footer font.
  523. * @access protected
  524. */
  525. protected $footer_font;
  526. /**
  527. * @var Language templates.
  528. * @access protected
  529. */
  530. protected $l;
  531. /**
  532. * @var Barcode to print on page footer (only if set).
  533. * @access protected
  534. */
  535. protected $barcode = false;
  536. /**
  537. * @var If true prints header
  538. * @access protected
  539. */
  540. protected $print_header = true;
  541. /**
  542. * @var If true prints footer.
  543. * @access protected
  544. */
  545. protected $print_footer = true;
  546. /**
  547. * @var Header image logo.
  548. * @access protected
  549. */
  550. protected $header_logo = '';
  551. /**
  552. * @var Header image logo width in mm.
  553. * @access protected
  554. */
  555. protected $header_logo_width = 30;
  556. /**
  557. * @var String to print as title on document header.
  558. * @access protected
  559. */
  560. protected $header_title = '';
  561. /**
  562. * @var String to print on document header.
  563. * @access protected
  564. */
  565. protected $header_string = '';
  566. /**
  567. * @var Default number of columns for html table.
  568. * @access protected
  569. */
  570. protected $default_table_columns = 4;
  571. // variables for html parser
  572. /**
  573. * @var HTML PARSER: array to store current link and rendering styles.
  574. * @access protected
  575. */
  576. protected $HREF = array();
  577. /**
  578. * @var store a list of available fonts on filesystem.
  579. * @access protected
  580. */
  581. protected $fontlist = array();
  582. /**
  583. * @var current foreground color
  584. * @access protected
  585. */
  586. protected $fgcolor;
  587. /**
  588. * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
  589. * @access protected
  590. */
  591. protected $listordered = array();
  592. /**
  593. * @var HTML PARSER: array count list items on nested lists.
  594. * @access protected
  595. */
  596. protected $listcount = array();
  597. /**
  598. * @var HTML PARSER: current list nesting level.
  599. * @access protected
  600. */
  601. protected $listnum = 0;
  602. /**
  603. * @var HTML PARSER: indent amount for lists.
  604. * @access protected
  605. */
  606. protected $listindent = 0;
  607. /**
  608. * @var HTML PARSER: current list indententation level.
  609. * @access protected
  610. */
  611. protected $listindentlevel = 0;
  612. /**
  613. * @var current background color
  614. * @access protected
  615. */
  616. protected $bgcolor;
  617. /**
  618. * @var Store temporary font size in points.
  619. * @access protected
  620. */
  621. protected $tempfontsize = 10;
  622. /**
  623. * @var spacer for LI tags.
  624. * @access protected
  625. */
  626. protected $lispacer = '';
  627. /**
  628. * @var default encoding
  629. * @access protected
  630. * @since 1.53.0.TC010
  631. */
  632. protected $encoding = 'UTF-8';
  633. /**
  634. * @var PHP internal encoding
  635. * @access protected
  636. * @since 1.53.0.TC016
  637. */
  638. protected $internal_encoding;
  639. /**
  640. * @var indicates if the document language is Right-To-Left
  641. * @access protected
  642. * @since 2.0.000
  643. */
  644. protected $rtl = false;
  645. /**
  646. * @var used to force RTL or LTR string inversion
  647. * @access protected
  648. * @since 2.0.000
  649. */
  650. protected $tmprtl = false;
  651. // --- Variables used for document encryption:
  652. /**
  653. * Indicates whether document is protected
  654. * @access protected
  655. * @since 2.0.000 (2008-01-02)
  656. */
  657. protected $encrypted;
  658. /**
  659. * Array containing encryption settings
  660. * @access protected
  661. * @since 5.0.005 (2010-05-11)
  662. */
  663. protected $encryptdata = array();
  664. /**
  665. * last RC4 key encrypted (cached for optimisation)
  666. * @access protected
  667. * @since 2.0.000 (2008-01-02)
  668. */
  669. protected $last_enc_key;
  670. /**
  671. * last RC4 computed key
  672. * @access protected
  673. * @since 2.0.000 (2008-01-02)
  674. */
  675. protected $last_enc_key_c;
  676. /**
  677. * Encryption padding
  678. * @access protected
  679. */
  680. protected $enc_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";
  681. /**
  682. * File ID (used on trailer)
  683. * @access protected
  684. * @since 5.0.005 (2010-05-12)
  685. */
  686. protected $file_id;
  687. // --- bookmark ---
  688. /**
  689. * Outlines for bookmark
  690. * @access protected
  691. * @since 2.1.002 (2008-02-12)
  692. */
  693. protected $outlines = array();
  694. /**
  695. * Outline root for bookmark
  696. * @access protected
  697. * @since 2.1.002 (2008-02-12)
  698. */
  699. protected $OutlineRoot;
  700. // --- javascript and form ---
  701. /**
  702. * javascript code
  703. * @access protected
  704. * @since 2.1.002 (2008-02-12)
  705. */
  706. protected $javascript = '';
  707. /**
  708. * javascript counter
  709. * @access protected
  710. * @since 2.1.002 (2008-02-12)
  711. */
  712. protected $n_js;
  713. /**
  714. * line trough state
  715. * @access protected
  716. * @since 2.8.000 (2008-03-19)
  717. */
  718. protected $linethrough;
  719. /**
  720. * Array with additional document-wide usage rights for the document.
  721. * @access protected
  722. * @since 5.8.014 (2010-08-23)
  723. */
  724. protected $ur = array();
  725. /**
  726. * Dot Per Inch Document Resolution (do not change)
  727. * @access protected
  728. * @since 3.0.000 (2008-03-27)
  729. */
  730. protected $dpi = 72;
  731. /**
  732. * Array of page numbers were a new page group was started
  733. * @access protected
  734. * @since 3.0.000 (2008-03-27)
  735. */
  736. protected $newpagegroup = array();
  737. /**
  738. * Contains the number of pages of the groups
  739. * @access protected
  740. * @since 3.0.000 (2008-03-27)
  741. */
  742. protected $pagegroups;
  743. /**
  744. * Contains the alias of the current page group
  745. * @access protected
  746. * @since 3.0.000 (2008-03-27)
  747. */
  748. protected $currpagegroup;
  749. /**
  750. * Restrict the rendering of some elements to screen or printout.
  751. * @access protected
  752. * @since 3.0.000 (2008-03-27)
  753. */
  754. protected $visibility = 'all';
  755. /**
  756. * Print visibility.
  757. * @access protected
  758. * @since 3.0.000 (2008-03-27)
  759. */
  760. protected $n_ocg_print;
  761. /**
  762. * View visibility.
  763. * @access protected
  764. * @since 3.0.000 (2008-03-27)
  765. */
  766. protected $n_ocg_view;
  767. /**
  768. * Array of transparency objects and parameters.
  769. * @access protected
  770. * @since 3.0.000 (2008-03-27)
  771. */
  772. protected $extgstates;
  773. /**
  774. * Set the default JPEG compression quality (1-100)
  775. * @access protected
  776. * @since 3.0.000 (2008-03-27)
  777. */
  778. protected $jpeg_quality;
  779. /**
  780. * Default cell height ratio.
  781. * @access protected
  782. * @since 3.0.014 (2008-05-23)
  783. */
  784. protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
  785. /**
  786. * PDF viewer preferences.
  787. * @access protected
  788. * @since 3.1.000 (2008-06-09)
  789. */
  790. protected $viewer_preferences;
  791. /**
  792. * A name object specifying how the document should be displayed when opened.
  793. * @access protected
  794. * @since 3.1.000 (2008-06-09)
  795. */
  796. protected $PageMode;
  797. /**
  798. * Array for storing gradient information.
  799. * @access protected
  800. * @since 3.1.000 (2008-06-09)
  801. */
  802. protected $gradients = array();
  803. /**
  804. * Array used to store positions inside the pages buffer.
  805. * keys are the page numbers
  806. * @access protected
  807. * @since 3.2.000 (2008-06-26)
  808. */
  809. protected $intmrk = array();
  810. /**
  811. * Array used to store positions inside the pages buffer.
  812. * keys are the page numbers
  813. * @access protected
  814. * @since 5.7.000 (2010-08-03)
  815. */
  816. protected $bordermrk = array();
  817. /**
  818. * Array used to store page positions to track empty pages.
  819. * keys are the page numbers
  820. * @access protected
  821. * @since 5.8.007 (2010-08-18)
  822. */
  823. protected $emptypagemrk = array();
  824. /**
  825. * Array used to store content positions inside the pages buffer.
  826. * keys are the page numbers
  827. * @access protected
  828. * @since 4.6.021 (2009-07-20)
  829. */
  830. protected $cntmrk = array();
  831. /**
  832. * Array used to store footer positions of each page.
  833. * @access protected
  834. * @since 3.2.000 (2008-07-01)
  835. */
  836. protected $footerpos = array();
  837. /**
  838. * Array used to store footer length of each page.
  839. * @access protected
  840. * @since 4.0.014 (2008-07-29)
  841. */
  842. protected $footerlen = array();
  843. /**
  844. * True if a newline is created.
  845. * @access protected
  846. * @since 3.2.000 (2008-07-01)
  847. */
  848. protected $newline = true;
  849. /**
  850. * End position of the latest inserted line
  851. * @access protected
  852. * @since 3.2.000 (2008-07-01)
  853. */
  854. protected $endlinex = 0;
  855. /**
  856. * PDF string for last line width
  857. * @access protected
  858. * @since 4.0.006 (2008-07-16)
  859. */
  860. protected $linestyleWidth = '';
  861. /**
  862. * PDF string for last line width
  863. * @access protected
  864. * @since 4.0.006 (2008-07-16)
  865. */
  866. protected $linestyleCap = '0 J';
  867. /**
  868. * PDF string for last line width
  869. * @access protected
  870. * @since 4.0.006 (2008-07-16)
  871. */
  872. protected $linestyleJoin = '0 j';
  873. /**
  874. * PDF string for last line width
  875. * @access protected
  876. * @since 4.0.006 (2008-07-16)
  877. */
  878. protected $linestyleDash = '[] 0 d';
  879. /**
  880. * True if marked-content sequence is open
  881. * @access protected
  882. * @since 4.0.013 (2008-07-28)
  883. */
  884. protected $openMarkedContent = false;
  885. /**
  886. * Count the latest inserted vertical spaces on HTML
  887. * @access protected
  888. * @since 4.0.021 (2008-08-24)
  889. */
  890. protected $htmlvspace = 0;
  891. /**
  892. * Array of Spot colors
  893. * @access protected
  894. * @since 4.0.024 (2008-09-12)
  895. */
  896. protected $spot_colors = array();
  897. /**
  898. * Symbol used for HTML unordered list items
  899. * @access protected
  900. * @since 4.0.028 (2008-09-26)
  901. */
  902. protected $lisymbol = '';
  903. /**
  904. * String used to mark the beginning and end of EPS image blocks
  905. * @access protected
  906. * @since 4.1.000 (2008-10-18)
  907. */
  908. protected $epsmarker = 'x#!#EPS#!#x';
  909. /**
  910. * Array of transformation matrix
  911. * @access protected
  912. * @since 4.2.000 (2008-10-29)
  913. */
  914. protected $transfmatrix = array();
  915. /**
  916. * Current key for transformation matrix
  917. * @access protected
  918. * @since 4.8.005 (2009-09-17)
  919. */
  920. protected $transfmatrix_key = 0;
  921. /**
  922. * Booklet mode for double-sided pages
  923. * @access protected
  924. * @since 4.2.000 (2008-10-29)
  925. */
  926. protected $booklet = false;
  927. /**
  928. * Epsilon value used for float calculations
  929. * @access protected
  930. * @since 4.2.000 (2008-10-29)
  931. */
  932. protected $feps = 0.005;
  933. /**
  934. * Array used for custom vertical spaces for HTML tags
  935. * @access protected
  936. * @since 4.2.001 (2008-10-30)
  937. */
  938. protected $tagvspaces = array();
  939. /**
  940. * @var HTML PARSER: custom indent amount for lists.
  941. * Negative value means disabled.
  942. * @access protected
  943. * @since 4.2.007 (2008-11-12)
  944. */
  945. protected $customlistindent = -1;
  946. /**
  947. * @var if true keeps the border open for the cell sides that cross the page.
  948. * @access protected
  949. * @since 4.2.010 (2008-11-14)
  950. */
  951. protected $opencell = true;
  952. /**
  953. * @var array of files to embedd
  954. * @access protected
  955. * @since 4.4.000 (2008-12-07)
  956. */
  957. protected $embeddedfiles = array();
  958. /**
  959. * @var boolean true when inside html pre tag
  960. * @access protected
  961. * @since 4.4.001 (2008-12-08)
  962. */
  963. protected $premode = false;
  964. /**
  965. * Array used to store positions of graphics transformation blocks inside the page buffer.
  966. * keys are the page numbers
  967. * @access protected
  968. * @since 4.4.002 (2008-12-09)
  969. */
  970. protected $transfmrk = array();
  971. /**
  972. * Default color for html links
  973. * @access protected
  974. * @since 4.4.003 (2008-12-09)
  975. */
  976. protected $htmlLinkColorArray = array(0, 0, 255);
  977. /**
  978. * Default font style to add to html links
  979. * @access protected
  980. * @since 4.4.003 (2008-12-09)
  981. */
  982. protected $htmlLinkFontStyle = 'U';
  983. /**
  984. * Counts the number of pages.
  985. * @access protected
  986. * @since 4.5.000 (2008-12-31)
  987. */
  988. protected $numpages = 0;
  989. /**
  990. * Array containing page lengths in bytes.
  991. * @access protected
  992. * @since 4.5.000 (2008-12-31)
  993. */
  994. protected $pagelen = array();
  995. /**
  996. * Counts the number of pages.
  997. * @access protected
  998. * @since 4.5.000 (2008-12-31)
  999. */
  1000. protected $numimages = 0;
  1001. /**
  1002. * Store the image keys.
  1003. * @access protected
  1004. * @since 4.5.000 (2008-12-31)
  1005. */
  1006. protected $imagekeys = array();
  1007. /**
  1008. * Length of the buffer in bytes.
  1009. * @access protected
  1010. * @since 4.5.000 (2008-12-31)
  1011. */
  1012. protected $bufferlen = 0;
  1013. /**
  1014. * If true enables disk caching.
  1015. * @access protected
  1016. * @since 4.5.000 (2008-12-31)
  1017. */
  1018. protected $diskcache = false;
  1019. /**
  1020. * Counts the number of fonts.
  1021. * @access protected
  1022. * @since 4.5.000 (2009-01-02)
  1023. */
  1024. protected $numfonts = 0;
  1025. /**
  1026. * Store the font keys.
  1027. * @access protected
  1028. * @since 4.5.000 (2009-01-02)
  1029. */
  1030. protected $fontkeys = array();
  1031. /**
  1032. * Store the font object IDs.
  1033. * @access protected
  1034. * @since 4.8.001 (2009-09-09)
  1035. */
  1036. protected $font_obj_ids = array();
  1037. /**
  1038. * Store the fage status (true when opened, false when closed).
  1039. * @access protected
  1040. * @since 4.5.000 (2009-01-02)
  1041. */
  1042. protected $pageopen = array();
  1043. /**
  1044. * Default monospaced font
  1045. * @access protected
  1046. * @since 4.5.025 (2009-03-10)
  1047. */
  1048. protected $default_monospaced_font = 'courier';
  1049. /**
  1050. * Used to store a cloned copy of the current class object
  1051. * @access protected
  1052. * @since 4.5.029 (2009-03-19)
  1053. */
  1054. protected $objcopy;
  1055. /**
  1056. * Array used to store the lengths of cache files
  1057. * @access protected
  1058. * @since 4.5.029 (2009-03-19)
  1059. */
  1060. protected $cache_file_length = array();
  1061. /**
  1062. * Table header content to be repeated on each new page
  1063. * @access protected
  1064. * @since 4.5.030 (2009-03-20)
  1065. */
  1066. protected $thead = '';
  1067. /**
  1068. * Margins used for table header.
  1069. * @access protected
  1070. * @since 4.5.030 (2009-03-20)
  1071. */
  1072. protected $theadMargins = array();
  1073. /**
  1074. * Cache array for UTF8StringToArray() method.
  1075. * @access protected
  1076. * @since 4.5.037 (2009-04-07)
  1077. */
  1078. protected $cache_UTF8StringToArray = array();
  1079. /**
  1080. * Maximum size of cache array used for UTF8StringToArray() method.
  1081. * @access protected
  1082. * @since 4.5.037 (2009-04-07)
  1083. */
  1084. protected $cache_maxsize_UTF8StringToArray = 8;
  1085. /**
  1086. * Current size of cache array used for UTF8StringToArray() method.
  1087. * @access protected
  1088. * @since 4.5.037 (2009-04-07)
  1089. */
  1090. protected $cache_size_UTF8StringToArray = 0;
  1091. /**
  1092. * If true enables document signing
  1093. * @access protected
  1094. * @since 4.6.005 (2009-04-24)
  1095. */
  1096. protected $sign = false;
  1097. /**
  1098. * Signature data
  1099. * @access protected
  1100. * @since 4.6.005 (2009-04-24)
  1101. */
  1102. protected $signature_data = array();
  1103. /**
  1104. * Signature max length
  1105. * @access protected
  1106. * @since 4.6.005 (2009-04-24)
  1107. */
  1108. protected $signature_max_length = 11742;
  1109. /**
  1110. * data for signature appearance
  1111. * @access protected
  1112. * @since 5.3.011 (2010-06-16)
  1113. */
  1114. protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
  1115. /**
  1116. * Regular expression used to find blank characters used for word-wrapping.
  1117. * @access protected
  1118. * @since 4.6.006 (2009-04-28)
  1119. */
  1120. protected $re_spaces = '/[^\S\xa0]/';
  1121. /**
  1122. * Array of parts $re_spaces
  1123. * @access protected
  1124. * @since 5.5.011 (2010-07-09)
  1125. */
  1126. protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
  1127. /**
  1128. * Signature object ID
  1129. * @access protected
  1130. * @since 4.6.022 (2009-06-23)
  1131. */
  1132. protected $sig_obj_id = 0;
  1133. /**
  1134. * ByteRange placemark used during signature process.
  1135. * @access protected
  1136. * @since 4.6.028 (2009-08-25)
  1137. */
  1138. protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
  1139. /**
  1140. * Placemark used during signature process.
  1141. * @access protected
  1142. * @since 4.6.028 (2009-08-25)
  1143. */
  1144. protected $sig_annot_ref = '***SIGANNREF*** 0 R';
  1145. /**
  1146. * ID of page objects
  1147. * @access protected
  1148. * @since 4.7.000 (2009-08-29)
  1149. */
  1150. protected $page_obj_id = array();
  1151. /**
  1152. * List of form annotations IDs
  1153. * @access protected
  1154. * @since 4.8.000 (2009-09-07)
  1155. */
  1156. protected $form_obj_id = array();
  1157. /**
  1158. * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry.
  1159. * @access protected
  1160. * @since 4.8.000 (2009-09-07)
  1161. */
  1162. protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
  1163. /**
  1164. * Javascript objects array
  1165. * @access protected
  1166. * @since 4.8.000 (2009-09-07)
  1167. */
  1168. protected $js_objects = array();
  1169. /**
  1170. * Current form action (used during XHTML rendering)
  1171. * @access protected
  1172. * @since 4.8.000 (2009-09-07)
  1173. */
  1174. protected $form_action = '';
  1175. /**
  1176. * Current form encryption type (used during XHTML rendering)
  1177. * @access protected
  1178. * @since 4.8.000 (2009-09-07)
  1179. */
  1180. protected $form_enctype = 'application/x-www-form-urlencoded';
  1181. /**
  1182. * Current method to submit forms.
  1183. * @access protected
  1184. * @since 4.8.000 (2009-09-07)
  1185. */
  1186. protected $form_mode = 'post';
  1187. /**
  1188. * List of fonts used on form fields (fontname => fontkey).
  1189. * @access protected
  1190. * @since 4.8.001 (2009-09-09)
  1191. */
  1192. protected $annotation_fonts = array();
  1193. /**
  1194. * List of radio buttons parent objects.
  1195. * @access protected
  1196. * @since 4.8.001 (2009-09-09)
  1197. */
  1198. protected $radiobutton_groups = array();
  1199. /**
  1200. * List of radio group objects IDs
  1201. * @access protected
  1202. * @since 4.8.001 (2009-09-09)
  1203. */
  1204. protected $radio_groups = array();
  1205. /**
  1206. * Text indentation value (used for text-indent CSS attribute)
  1207. * @access protected
  1208. * @since 4.8.006 (2009-09-23)
  1209. */
  1210. protected $textindent = 0;
  1211. /**
  1212. * Store page number when startTransaction() is called.
  1213. * @access protected
  1214. * @since 4.8.006 (2009-09-23)
  1215. */
  1216. protected $start_transaction_page = 0;
  1217. /**
  1218. * Store Y position when startTransaction() is called.
  1219. * @access protected
  1220. * @since 4.9.001 (2010-03-28)
  1221. */
  1222. protected $start_transaction_y = 0;
  1223. /**
  1224. * True when we are printing the thead section on a new page
  1225. * @access protected
  1226. * @since 4.8.027 (2010-01-25)
  1227. */
  1228. protected $inthead = false;
  1229. /**
  1230. * Array of column measures (width, space, starting Y position)
  1231. * @access protected
  1232. * @since 4.9.001 (2010-03-28)
  1233. */
  1234. protected $columns = array();
  1235. /**
  1236. * Number of colums
  1237. * @access protected
  1238. * @since 4.9.001 (2010-03-28)
  1239. */
  1240. protected $num_columns = 1;
  1241. /**
  1242. * Current column number
  1243. * @access protected
  1244. * @since 4.9.001 (2010-03-28)
  1245. */
  1246. protected $current_column = 0;
  1247. /**
  1248. * Starting page for columns
  1249. * @access protected
  1250. * @since 4.9.001 (2010-03-28)
  1251. */
  1252. protected $column_start_page = 0;
  1253. /**
  1254. * Maximum page and column selected
  1255. * @access protected
  1256. * @since 5.8.000 (2010-08-11)
  1257. */
  1258. protected $maxselcol = array('page' => 0, 'column' => 0);
  1259. /**
  1260. * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding
  1261. * @access protected
  1262. * @since 5.8.000 (2010-08-11)
  1263. */
  1264. protected $colxshift = array('x' => 0, 's' => 0, 'p' => 0);
  1265. /**
  1266. * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping.
  1267. * @access protected
  1268. * @since 4.9.008 (2010-04-03)
  1269. */
  1270. protected $textrendermode = 0;
  1271. /**
  1272. * Text stroke width in doc units
  1273. * @access protected
  1274. * @since 4.9.008 (2010-04-03)
  1275. */
  1276. protected $textstrokewidth = 0;
  1277. /**
  1278. * @var current stroke color
  1279. * @access protected
  1280. * @since 4.9.008 (2010-04-03)
  1281. */
  1282. protected $strokecolor;
  1283. /**
  1284. * @var default unit of measure for document
  1285. * @access protected
  1286. * @since 5.0.000 (2010-04-22)
  1287. */
  1288. protected $pdfunit = 'mm';
  1289. /**
  1290. * @var true when we are on TOC (Table Of Content) page
  1291. * @access protected
  1292. */
  1293. protected $tocpage = false;
  1294. /**
  1295. * @var If true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
  1296. * @access protected
  1297. * @since 5.0.000 (2010-04-26)
  1298. */
  1299. protected $rasterize_vector_images = false;
  1300. /**
  1301. * @var If true enables font subsetting by default
  1302. * @access protected
  1303. * @since 5.3.002 (2010-06-07)
  1304. */
  1305. protected $font_subsetting = true;
  1306. /**
  1307. * @var Array of default graphic settings
  1308. * @access protected
  1309. * @since 5.5.008 (2010-07-02)
  1310. */
  1311. protected $default_graphic_vars = array();
  1312. /**
  1313. * @var Array of XObjects
  1314. * @access protected
  1315. * @since 5.8.014 (2010-08-23)
  1316. */
  1317. protected $xobjects = array();
  1318. /**
  1319. * @var boolean true when we are inside an XObject
  1320. * @access protected
  1321. * @since 5.8.017 (2010-08-24)
  1322. */
  1323. protected $inxobj = false;
  1324. /**
  1325. * @var current XObject ID
  1326. * @access protected
  1327. * @since 5.8.017 (2010-08-24)
  1328. */
  1329. protected $xobjid = '';
  1330. /**
  1331. * @var percentage of character stretching
  1332. * @access protected
  1333. * @since 5.9.000 (2010-09-29)
  1334. */
  1335. protected $font_stretching = 100;
  1336. /**
  1337. * @var increases or decreases the space between characters in a text by the specified amount (tracking/kerning).
  1338. * @access protected
  1339. * @since 5.9.000 (2010-09-29)
  1340. */
  1341. protected $font_spacing = 0;
  1342. /**
  1343. * @var array of no-write regions
  1344. * ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right)
  1345. * @access protected
  1346. * @since 5.9.003 (2010-10-14)
  1347. */
  1348. protected $page_regions = array();
  1349. /**
  1350. * @var array containing HTML color names and values
  1351. * @access protected
  1352. * @since 5.9.004 (2010-10-18)
  1353. */
  1354. protected $webcolor = array();
  1355. /**
  1356. * @var directory used for the last SVG image
  1357. * @access protected
  1358. * @since 5.0.000 (2010-05-05)
  1359. */
  1360. protected $svgdir = '';
  1361. /**
  1362. * @var Deafult unit of measure for SVG
  1363. * @access protected
  1364. * @since 5.0.000 (2010-05-02)
  1365. */
  1366. protected $svgunit = 'px';
  1367. /**
  1368. * @var array of SVG gradients
  1369. * @access protected
  1370. * @since 5.0.000 (2010-05-02)
  1371. */
  1372. protected $svggradients = array();
  1373. /**
  1374. * @var ID of last SVG gradient
  1375. * @access protected
  1376. * @since 5.0.000 (2010-05-02)
  1377. */
  1378. protected $svggradientid = 0;
  1379. /**
  1380. * @var true when in SVG defs group
  1381. * @access protected
  1382. * @since 5.0.000 (2010-05-02)
  1383. */
  1384. protected $svgdefsmode = false;
  1385. /**
  1386. * @var array of SVG defs
  1387. * @access protected
  1388. * @since 5.0.000 (2010-05-02)
  1389. */
  1390. protected $svgdefs = array();
  1391. /**
  1392. * @var true when in SVG clipPath tag
  1393. * @access protected
  1394. * @since 5.0.000 (2010-04-26)
  1395. */
  1396. protected $svgclipmode = false;
  1397. /**
  1398. * @var array of SVG clipPath commands
  1399. * @access protected
  1400. * @since 5.0.000 (2010-05-02)
  1401. */
  1402. protected $svgclippaths = array();
  1403. /**
  1404. * @var array of SVG clipPath tranformation matrix
  1405. * @access protected
  1406. * @since 5.8.022 (2010-08-31)
  1407. */
  1408. protected $svgcliptm = array();
  1409. /**
  1410. * @var ID of last SVG clipPath
  1411. * @access protected
  1412. * @since 5.0.000 (2010-05-02)
  1413. */
  1414. protected $svgclipid = 0;
  1415. /**
  1416. * @var svg text
  1417. * @access protected
  1418. * @since 5.0.000 (2010-05-02)
  1419. */
  1420. protected $svgtext = '';
  1421. /**
  1422. * @var svg text properties
  1423. * @access protected
  1424. * @since 5.8.013 (2010-08-23)
  1425. */
  1426. protected $svgtextmode = array();
  1427. /**
  1428. * @var array of hinheritable SVG properties
  1429. * @access protected
  1430. * @since 5.0.000 (2010-05-02)
  1431. */
  1432. protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode');
  1433. /**
  1434. * @var array of SVG properties
  1435. * @access protected
  1436. * @since 5.0.000 (2010-05-02)
  1437. */
  1438. protected $svgstyles = array(array(
  1439. 'alignment-baseline' => 'auto',
  1440. 'baseline-shift' => 'baseline',
  1441. 'clip' => 'auto',
  1442. 'clip-path' => 'none',
  1443. 'clip-rule' => 'nonzero',
  1444. 'color' => 'black',
  1445. 'color-interpolation' => 'sRGB',
  1446. 'color-interpolation-filters' => 'linearRGB',
  1447. 'color-profile' => 'auto',
  1448. 'color-rendering' => 'auto',
  1449. 'cursor' => 'auto',
  1450. 'direction' => 'ltr',
  1451. 'display' => 'inline',
  1452. 'dominant-baseline' => 'auto',
  1453. 'enable-background' => 'accumulate',
  1454. 'fill' => 'black',
  1455. 'fill-opacity' => 1,
  1456. 'fill-rule' => 'nonzero',
  1457. 'filter' => 'none',
  1458. 'flood-color' => 'black',
  1459. 'flood-opacity' => 1,
  1460. 'font' => '',
  1461. 'font-family' => 'helvetica',
  1462. 'font-size' => 'medium',
  1463. 'font-size-adjust' => 'none',
  1464. 'font-stretch' => 'normal',
  1465. 'font-style' => 'normal',
  1466. 'font-variant' => 'normal',
  1467. 'font-weight' => 'normal',
  1468. 'glyph-orientation-horizontal' => '0deg',
  1469. 'glyph-orientation-vertical' => 'auto',
  1470. 'image-rendering' => 'auto',
  1471. 'kerning' => 'auto',
  1472. 'letter-spacing' => 'normal',
  1473. 'lighting-color' => 'white',
  1474. 'marker' => '',
  1475. 'marker-end' => 'none',
  1476. 'marker-mid' => 'none',
  1477. 'marker-start' => 'none',
  1478. 'mask' => 'none',
  1479. 'opacity' => 1,
  1480. 'overflow' => 'auto',
  1481. 'pointer-events' => 'visiblePainted',
  1482. 'shape-rendering' => 'auto',
  1483. 'stop-color' => 'black',
  1484. 'stop-opacity' => 1,
  1485. 'stroke' => 'none',
  1486. 'stroke-dasharray' => 'none',
  1487. 'stroke-dashoffset' => 0,
  1488. 'stroke-linecap' => 'butt',
  1489. 'stroke-linejoin' => 'miter',
  1490. 'stroke-miterlimit' => 4,
  1491. 'stroke-opacity' => 1,
  1492. 'stroke-width' => 1,
  1493. 'text-anchor' => 'start',
  1494. 'text-decoration' => 'none',
  1495. 'text-rendering' => 'auto',
  1496. 'unicode-bidi' => 'normal',
  1497. 'visibility' => 'visible',
  1498. 'word-spacing' => 'normal',
  1499. 'writing-mode' => 'lr-tb',
  1500. 'text-color' => 'black',
  1501. 'transfmatrix' => array(1, 0, 0, 1, 0, 0)
  1502. ));
  1503. //------------------------------------------------------------
  1504. // METHODS
  1505. //------------------------------------------------------------
  1506. /**
  1507. * This is the class constructor.
  1508. * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
  1509. * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
  1510. * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
  1511. * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
  1512. * @param boolean $unicode TRUE means that the input text is unicode (default = true)
  1513. * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
  1514. * @param String $encoding charset encoding; default is UTF-8
  1515. * @access public
  1516. * @see getPageSizeFromFormat(), setPageFormat()
  1517. */
  1518. public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
  1519. /* Set internal character encoding to ASCII */
  1520. if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
  1521. $this->internal_encoding = mb_internal_encoding();
  1522. mb_internal_encoding('ASCII');
  1523. }
  1524. require(dirname(__FILE__).'/htmlcolors.php');
  1525. $this->webcolor = $webcolor;
  1526. require_once(dirname(__FILE__).'/unicode_data.php');
  1527. $this->unicode = new TCPDF_UNICODE_DATA();
  1528. $this->font_obj_ids = array();
  1529. $this->page_obj_id = array();
  1530. $this->form_obj_id = array();
  1531. // set disk caching
  1532. $this->diskcache = $diskcache ? true : false;
  1533. // set language direction
  1534. $this->rtl = false;
  1535. $this->tmprtl = false;
  1536. // some checks
  1537. $this->_dochecks();
  1538. // initialization of properties
  1539. $this->isunicode = $unicode;
  1540. $this->page = 0;
  1541. $this->transfmrk[0] = array();
  1542. $this->pagedim = array();
  1543. $this->n = 2;
  1544. $this->buffer = '';
  1545. $this->pages = array();
  1546. $this->state = 0;
  1547. $this->fonts = array();
  1548. $this->FontFiles = array();
  1549. $this->diffs = array();
  1550. $this->images = array();
  1551. $this->links = array();
  1552. $this->gradients = array();
  1553. $this->InFooter = false;
  1554. $this->lasth = 0;
  1555. $this->FontFamily = 'helvetica';
  1556. $this->FontStyle = '';
  1557. $this->FontSizePt = 12;
  1558. $this->underline = false;
  1559. $this->overline = false;
  1560. $this->linethrough = false;
  1561. $this->DrawColor = '0 G';
  1562. $this->FillColor = '0 g';
  1563. $this->TextColor = '0 g';
  1564. $this->ColorFlag = false;
  1565. // encryption values
  1566. $this->encrypted = false;
  1567. $this->last_enc_key = '';
  1568. // standard Unicode fonts
  1569. $this->CoreFonts = array(
  1570. 'courier'=>'Courier',
  1571. 'courierB'=>'Courier-Bold',
  1572. 'courierI'=>'Courier-Oblique',
  1573. 'courierBI'=>'Courier-BoldOblique',
  1574. 'helvetica'=>'Helvetica',
  1575. 'helveticaB'=>'Helvetica-Bold',
  1576. 'helveticaI'=>'Helvetica-Oblique',
  1577. 'helveticaBI'=>'Helvetica-BoldOblique',
  1578. 'times'=>'Times-Roman',
  1579. 'timesB'=>'Times-Bold',
  1580. 'timesI'=>'Times-Italic',
  1581. 'timesBI'=>'Times-BoldItalic',
  1582. 'symbol'=>'Symbol',
  1583. 'zapfdingbats'=>'ZapfDingbats'
  1584. );
  1585. // set scale factor
  1586. $this->setPageUnit($unit);
  1587. // set page format and orientation
  1588. $this->setPageFormat($format, $orientation);
  1589. // page margins (1 cm)
  1590. $margin = 28.35 / $this->k;
  1591. $this->SetMargins($margin, $margin);
  1592. // internal cell padding
  1593. $cpadding = $margin / 10;
  1594. $this->setCellPaddings($cpadding, 0, $cpadding, 0);
  1595. // cell margins
  1596. $this->setCellMargins(0, 0, 0, 0);
  1597. // line width (0.2 mm)
  1598. $this->LineWidth = 0.57 / $this->k;
  1599. $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
  1600. $this->linestyleCap = '0 J';
  1601. $this->linestyleJoin = '0 j';
  1602. $this->linestyleDash = '[] 0 d';
  1603. // automatic page break
  1604. $this->SetAutoPageBreak(true, (2 * $margin));
  1605. // full width display mode
  1606. $this->SetDisplayMode('fullwidth');
  1607. // compression
  1608. $this->SetCompression(true);
  1609. // set default PDF version number
  1610. $this->PDFVersion = '1.7';
  1611. $this->encoding = $encoding;
  1612. $this->HREF = array();
  1613. $this->getFontsList();
  1614. $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
  1615. $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
  1616. $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
  1617. $this->extgstates = array();
  1618. // user's rights
  1619. $this->sign = false;
  1620. $this->ur['enabled'] = false;
  1621. $this->ur['document'] = '/FullSave';
  1622. $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
  1623. $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
  1624. $this->ur['signature'] = '/Modify';
  1625. $this->ur['ef'] = '/Create/Delete/Modify/Import';
  1626. $this->ur['formex'] = '';
  1627. $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
  1628. // set default JPEG quality
  1629. $this->jpeg_quality = 75;
  1630. // initialize some settings
  1631. $this->utf8Bidi(array(''), '');
  1632. // set default font
  1633. $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
  1634. // check if PCRE Unicode support is enabled
  1635. if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
  1636. // PCRE unicode support is turned ON
  1637. // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
  1638. // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
  1639. // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
  1640. //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
  1641. $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
  1642. } else {
  1643. // PCRE unicode support is turned OFF
  1644. $this->setSpacesRE('/[^\S\xa0]/');
  1645. }
  1646. $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
  1647. // set file ID for trailer
  1648. $this->file_id = md5($this->getRandomSeed('TCPDF'.$orientation.$unit.$format.$encoding));
  1649. // get default graphic vars
  1650. $this->default_graphic_vars = $this->getGraphicVars();
  1651. }
  1652. /**
  1653. * Default destructor.
  1654. * @access public
  1655. * @since 1.53.0.TC016
  1656. */
  1657. public function __destruct() {
  1658. // restore internal encoding
  1659. if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
  1660. mb_internal_encoding($this->internal_encoding);
  1661. }
  1662. // unset all class variables
  1663. $this->_destroy(true);
  1664. }
  1665. /**
  1666. * Set the units of measure for the document.
  1667. * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
  1668. * @access public
  1669. * @since 3.0.015 (2008-06-06)
  1670. */
  1671. public function setPageUnit($unit) {
  1672. $unit = strtolower($unit);
  1673. //Set scale factor
  1674. switch ($unit) {
  1675. // points
  1676. case 'px':
  1677. case 'pt': {
  1678. $this->k = 1;
  1679. break;
  1680. }
  1681. // millimeters
  1682. case 'mm': {
  1683. $this->k = $this->dpi / 25.4;
  1684. break;
  1685. }
  1686. // centimeters
  1687. case 'cm': {
  1688. $this->k = $this->dpi / 2.54;
  1689. break;
  1690. }
  1691. // inches
  1692. case 'in': {
  1693. $this->k = $this->dpi;
  1694. break;
  1695. }
  1696. // unsupported unit
  1697. default : {
  1698. $this->Error('Incorrect unit: '.$unit);
  1699. break;
  1700. }
  1701. }
  1702. $this->pdfunit = $unit;
  1703. if (isset($this->CurOrientation)) {
  1704. $this->setPageOrientation($this->CurOrientation);
  1705. }
  1706. }
  1707. /**
  1708. * Get page dimensions from format name.
  1709. * @param mixed $format The format name. It can be: <ul>
  1710. * <li><b>ISO 216 A Series + 2 SIS 014711 extensions</b></li>
  1711. * <li>A0 (841x1189 mm ; 33.11x46.81 in)</li>
  1712. * <li>A1 (594x841 mm ; 23.39x33.11 in)</li>
  1713. * <li>A2 (420x594 mm ; 16.54x23.39 in)</li>
  1714. * <li>A3 (297x420 mm ; 11.69x16.54 in)</li>
  1715. * <li>A4 (210x297 mm ; 8.27x11.69 in)</li>
  1716. * <li>A5 (148x210 mm ; 5.83x8.27 in)</li>
  1717. * <li>A6 (105x148 mm ; 4.13x5.83 in)</li>
  1718. * <li>A7 (74x105 mm ; 2.91x4.13 in)</li>
  1719. * <li>A8 (52x74 mm ; 2.05x2.91 in)</li>
  1720. * <li>A9 (37x52 mm ; 1.46x2.05 in)</li>
  1721. * <li>A10 (26x37 mm ; 1.02x1.46 in)</li>
  1722. * <li>A11 (18x26 mm ; 0.71x1.02 in)</li>
  1723. * <li>A12 (13x18 mm ; 0.51x0.71 in)</li>
  1724. * <li><b>ISO 216 B Series + 2 SIS 014711 extensions</b></li>
  1725. * <li>B0 (1000x1414 mm ; 39.37x55.67 in)</li>
  1726. * <li>B1 (707x1000 mm ; 27.83x39.37 in)</li>
  1727. * <li>B2 (500x707 mm ; 19.69x27.83 in)</li>
  1728. * <li>B3 (353x500 mm ; 13.90x19.69 in)</li>
  1729. * <li>B4 (250x353 mm ; 9.84x13.90 in)</li>
  1730. * <li>B5 (176x250 mm ; 6.93x9.84 in)</li>
  1731. * <li>B6 (125x176 mm ; 4.92x6.93 in)</li>
  1732. * <li>B7 (88x125 mm ; 3.46x4.92 in)</li>
  1733. * <li>B8 (62x88 mm ; 2.44x3.46 in)</li>
  1734. * <li>B9 (44x62 mm ; 1.73x2.44 in)</li>
  1735. * <li>B10 (31x44 mm ; 1.22x1.73 in)</li>
  1736. * <li>B11 (22x31 mm ; 0.87x1.22 in)</li>
  1737. * <li>B12 (15x22 mm ; 0.59x0.87 in)</li>
  1738. * <li><b>ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION</b></li>
  1739. * <li>C0 (917x1297 mm ; 36.10x51.06 in)</li>
  1740. * <li>C1 (648x917 mm ; 25.51x36.10 in)</li>
  1741. * <li>C2 (458x648 mm ; 18.03x25.51 in)</li>
  1742. * <li>C3 (324x458 mm ; 12.76x18.03 in)</li>
  1743. * <li>C4 (229x324 mm ; 9.02x12.76 in)</li>
  1744. * <li>C5 (162x229 mm ; 6.38x9.02 in)</li>
  1745. * <li>C6 (114x162 mm ; 4.49x6.38 in)</li>
  1746. * <li>C7 (81x114 mm ; 3.19x4.49 in)</li>
  1747. * <li>C8 (57x81 mm ; 2.24x3.19 in)</li>
  1748. * <li>C9 (40x57 mm ; 1.57x2.24 in)</li>
  1749. * <li>C10 (28x40 mm ; 1.10x1.57 in)</li>
  1750. * <li>C11 (20x28 mm ; 0.79x1.10 in)</li>
  1751. * <li>C12 (14x20 mm ; 0.55x0.79 in)</li>
  1752. * <li>C76 (81x162 mm ; 3.19x6.38 in)</li>
  1753. * <li>DL (110x220 mm ; 4.33x8.66 in)</li>
  1754. * <li><b>SIS 014711 E Series</b></li>
  1755. * <li>E0 (879x1241 mm ; 34.61x48.86 in)</li>
  1756. * <li>E1 (620x879 mm ; 24.41x34.61 in)</li>
  1757. * <li>E2 (440x620 mm ; 17.32x24.41 in)</li>
  1758. * <li>E3 (310x440 mm ; 12.20x17.32 in)</li>
  1759. * <li>E4 (220x310 mm ; 8.66x12.20 in)</li>
  1760. * <li>E5 (155x220 mm ; 6.10x8.66 in)</li>
  1761. * <li>E6 (110x155 mm ; 4.33x6.10 in)</li>
  1762. * <li>E7 (78x110 mm ; 3.07x4.33 in)</li>
  1763. * <li>E8 (55x78 mm ; 2.17x3.07 in)</li>
  1764. * <li>E9 (39x55 mm ; 1.54x2.17 in)</li>
  1765. * <li>E10 (27x39 mm ; 1.06x1.54 in)</li>
  1766. * <li>E11 (19x27 mm ; 0.75x1.06 in)</li>
  1767. * <li>E12 (13x19 mm ; 0.51x0.75 in)</li>
  1768. * <li><b>SIS 014711 G Series</b></li>
  1769. * <li>G0 (958x1354 mm ; 37.72x53.31 in)</li>
  1770. * <li>G1 (677x958 mm ; 26.65x37.72 in)</li>
  1771. * <li>G2 (479x677 mm ; 18.86x26.65 in)</li>
  1772. * <li>G3 (338x479 mm ; 13.31x18.86 in)</li>
  1773. * <li>G4 (239x338 mm ; 9.41x13.31 in)</li>
  1774. * <li>G5 (169x239 mm ; 6.65x9.41 in)</li>
  1775. * <li>G6 (119x169 mm ; 4.69x6.65 in)</li>
  1776. * <li>G7 (84x119 mm ; 3.31x4.69 in)</li>
  1777. * <li>G8 (59x84 mm ; 2.32x3.31 in)</li>
  1778. * <li>G9 (42x59 mm ; 1.65x2.32 in)</li>
  1779. * <li>G10 (29x42 mm ; 1.14x1.65 in)</li>
  1780. * <li>G11 (21x29 mm ; 0.83x1.14 in)</li>
  1781. * <li>G12 (14x21 mm ; 0.55x0.83 in)</li>
  1782. * <li><b>ISO Press</b></li>
  1783. * <li>RA0 (860x1220 mm ; 33.86x48.03 in)</li>
  1784. * <li>RA1 (610x860 mm ; 24.02x33.86 in)</li>
  1785. * <li>RA2 (430x610 mm ; 16.93x24.02 in)</li>
  1786. * <li>RA3 (305x430 mm ; 12.01x16.93 in)</li>
  1787. * <li>RA4 (215x305 mm ; 8.46x12.01 in)</li>
  1788. * <li>SRA0 (900x1280 mm ; 35.43x50.39 in)</li>
  1789. * <li>SRA1 (640x900 mm ; 25.20x35.43 in)</li>
  1790. * <li>SRA2 (450x640 mm ; 17.72x25.20 in)</li>
  1791. * <li>SRA3 (320x450 mm ; 12.60x17.72 in)</li>
  1792. * <li>SRA4 (225x320 mm ; 8.86x12.60 in)</li>
  1793. * <li><b>German DIN 476</b></li>
  1794. * <li>4A0 (1682x2378 mm ; 66.22x93.62 in)</li>
  1795. * <li>2A0 (1189x1682 mm ; 46.81x66.22 in)</li>
  1796. * <li><b>Variations on the ISO Standard</b></li>
  1797. * <li>A2_EXTRA (445x619 mm ; 17.52x24.37 in)</li>
  1798. * <li>A3+ (329x483 mm ; 12.95x19.02 in)</li>
  1799. * <li>A3_EXTRA (322x445 mm ; 12.68x17.52 in)</li>
  1800. * <li>A3_SUPER (305x508 mm ; 12.01x20.00 in)</li>
  1801. * <li>SUPER_A3 (305x487 mm ; 12.01x19.17 in)</li>
  1802. * <li>A4_EXTRA (235x322 mm ; 9.25x12.68 in)</li>
  1803. * <li>A4_SUPER (229x322 mm ; 9.02x12.68 in)</li>
  1804. * <li>SUPER_A4 (227x356 mm ; 8.94x14.02 in)</li>
  1805. * <li>A4_LONG (210x348 mm ; 8.27x13.70 in)</li>
  1806. * <li>F4 (210x330 mm ; 8.27x12.99 in)</li>
  1807. * <li>SO_B5_EXTRA (202x276 mm ; 7.95x10.87 in)</li>
  1808. * <li>A5_EXTRA (173x235 mm ; 6.81x9.25 in)</li>
  1809. * <li><b>ANSI Series</b></li>
  1810. * <li>ANSI_E (864x1118 mm ; 34.00x44.00 in)</li>
  1811. * <li>ANSI_D (559x864 mm ; 22.00x34.00 in)</li>
  1812. * <li>ANSI_C (432x559 mm ; 17.00x22.00 in)</li>
  1813. * <li>ANSI_B (279x432 mm ; 11.00x17.00 in)</li>
  1814. * <li>ANSI_A (216x279 mm ; 8.50x11.00 in)</li>
  1815. * <li><b>Traditional 'Loose' North American Paper Sizes</b></li>
  1816. * <li>LEDGER, USLEDGER (432x279 mm ; 17.00x11.00 in)</li>
  1817. * <li>TABLOID, USTABLOID, BIBLE, ORGANIZERK (279x432 mm ; 11.00x17.00 in)</li>
  1818. * <li>LETTER, USLETTER, ORGANIZERM (216x279 mm ; 8.50x11.00 in)</li>
  1819. * <li>LEGAL, USLEGAL (216x356 mm ; 8.50x14.00 in)</li>
  1820. * <li>GLETTER, GOVERNMENTLETTER (203x267 mm ; 8.00x10.50 in)</li>
  1821. * <li>JLEGAL, JUNIORLEGAL (203x127 mm ; 8.00x5.00 in)</li>
  1822. * <li><b>Other North American Paper Sizes</b></li>
  1823. * <li>QUADDEMY (889x1143 mm ; 35.00x45.00 in)</li>
  1824. * <li>SUPER_B (330x483 mm ; 13.00x19.00 in)</li>
  1825. * <li>QUARTO (229x279 mm ; 9.00x11.00 in)</li>
  1826. * <li>FOLIO, GOVERNMENTLEGAL (216x330 mm ; 8.50x13.00 in)</li>
  1827. * <li>EXECUTIVE, MONARCH (184x267 mm ; 7.25x10.50 in)</li>
  1828. * <li>MEMO, STATEMENT, ORGANIZERL (140x216 mm ; 5.50x8.50 in)</li>
  1829. * <li>FOOLSCAP (210x330 mm ; 8.27x13.00 in)</li>
  1830. * <li>COMPACT (108x171 mm ; 4.25x6.75 in)</li>
  1831. * <li>ORGANIZERJ (70x127 mm ; 2.75x5.00 in)</li>
  1832. * <li><b>Canadian standard CAN 2-9.60M</b></li>
  1833. * <li>P1 (560x860 mm ; 22.05x33.86 in)</li>
  1834. * <li>P2 (430x560 mm ; 16.93x22.05 in)</li>
  1835. * <li>P3 (280x430 mm ; 11.02x16.93 in)</li>
  1836. * <li>P4 (215x280 mm ; 8.46x11.02 in)</li>
  1837. * <li>P5 (140x215 mm ; 5.51x8.46 in)</li>
  1838. * <li>P6 (107x140 mm ; 4.21x5.51 in)</li>
  1839. * <li><b>North American Architectural Sizes</b></li>
  1840. * <li>ARCH_E (914x1219 mm ; 36.00x48.00 in)</li>
  1841. * <li>ARCH_E1 (762x1067 mm ; 30.00x42.00 in)</li>
  1842. * <li>ARCH_D (610x914 mm ; 24.00x36.00 in)</li>
  1843. * <li>ARCH_C, BROADSHEET (457x610 mm ; 18.00x24.00 in)</li>
  1844. * <li>ARCH_B (305x457 mm ; 12.00x18.00 in)</li>
  1845. * <li>ARCH_A (229x305 mm ; 9.00x12.00 in)</li>
  1846. * <li><b>Announcement Envelopes</b></li>
  1847. * <li>ANNENV_A2 (111x146 mm ; 4.37x5.75 in)</li>
  1848. * <li>ANNENV_A6 (121x165 mm ; 4.75x6.50 in)</li>
  1849. * <li>ANNENV_A7 (133x184 mm ; 5.25x7.25 in)</li>
  1850. * <li>ANNENV_A8 (140x206 mm ; 5.50x8.12 in)</li>
  1851. * <li>ANNENV_A10 (159x244 mm ; 6.25x9.62 in)</li>
  1852. * <li>ANNENV_SLIM (98x225 mm ; 3.87x8.87 in)</li>
  1853. * <li><b>Commercial Envelopes</b></li>
  1854. * <li>COMMENV_N6_1/4 (89x152 mm ; 3.50x6.00 in)</li>
  1855. * <li>COMMENV_N6_3/4 (92x165 mm ; 3.62x6.50 in)</li>
  1856. * <li>COMMENV_N8 (98x191 mm ; 3.87x7.50 in)</li>
  1857. * <li>COMMENV_N9 (98x225 mm ; 3.87x8.87 in)</li>
  1858. * <li>COMMENV_N10 (105x241 mm ; 4.12x9.50 in)</li>
  1859. * <li>COMMENV_N11 (114x263 mm ; 4.50x10.37 in)</li>
  1860. * <li>COMMENV_N12 (121x279 mm ; 4.75x11.00 in)</li>
  1861. * <li>COMMENV_N14 (127x292 mm ; 5.00x11.50 in)</li>
  1862. * <li><b>Catalogue Envelopes</b></li>
  1863. * <li>CATENV_N1 (152x229 mm ; 6.00x9.00 in)</li>
  1864. * <li>CATENV_N1_3/4 (165x241 mm ; 6.50x9.50 in)</li>
  1865. * <li>CATENV_N2 (165x254 mm ; 6.50x10.00 in)</li>
  1866. * <li>CATENV_N3 (178x254 mm ; 7.00x10.00 in)</li>
  1867. * <li>CATENV_N6 (191x267 mm ; 7.50x10.50 in)</li>
  1868. * <li>CATENV_N7 (203x279 mm ; 8.00x11.00 in)</li>
  1869. * <li>CATENV_N8 (210x286 mm ; 8.25x11.25 in)</li>
  1870. * <li>CATENV_N9_1/2 (216x267 mm ; 8.50x10.50 in)</li>
  1871. * <li>CATENV_N9_3/4 (222x286 mm ; 8.75x11.25 in)</li>
  1872. * <li>CATENV_N10_1/2 (229x305 mm ; 9.00x12.00 in)</li>
  1873. * <li>CATENV_N12_1/2 (241x318 mm ; 9.50x12.50 in)</li>
  1874. * <li>CATENV_N13_1/2 (254x330 mm ; 10.00x13.00 in)</li>
  1875. * <li>CATENV_N14_1/4 (286x311 mm ; 11.25x12.25 in)</li>
  1876. * <li>CATENV_N14_1/2 (292x368 mm ; 11.50x14.50 in)</li>
  1877. * <li><b>Japanese (JIS P 0138-61) Standard B-Series</b></li>
  1878. * <li>JIS_B0 (1030x1456 mm ; 40.55x57.32 in)</li>
  1879. * <li>JIS_B1 (728x1030 mm ; 28.66x40.55 in)</li>
  1880. * <li>JIS_B2 (515x728 mm ; 20.28x28.66 in)</li>
  1881. * <li>JIS_B3 (364x515 mm ; 14.33x20.28 in)</li>
  1882. * <li>JIS_B4 (257x364 mm ; 10.12x14.33 in)</li>
  1883. * <li>JIS_B5 (182x257 mm ; 7.17x10.12 in)</li>
  1884. * <li>JIS_B6 (128x182 mm ; 5.04x7.17 in)</li>
  1885. * <li>JIS_B7 (91x128 mm ; 3.58x5.04 in)</li>
  1886. * <li>JIS_B8 (64x91 mm ; 2.52x3.58 in)</li>
  1887. * <li>JIS_B9 (45x64 mm ; 1.77x2.52 in)</li>
  1888. * <li>JIS_B10 (32x45 mm ; 1.26x1.77 in)</li>
  1889. * <li>JIS_B11 (22x32 mm ; 0.87x1.26 in)</li>
  1890. * <li>JIS_B12 (16x22 mm ; 0.63x0.87 in)</li>
  1891. * <li><b>PA Series</b></li>
  1892. * <li>PA0 (840x1120 mm ; 33.07x44.09 in)</li>
  1893. * <li>PA1 (560x840 mm ; 22.05x33.07 in)</li>
  1894. * <li>PA2 (420x560 mm ; 16.54x22.05 in)</li>
  1895. * <li>PA3 (280x420 mm ; 11.02x16.54 in)</li>
  1896. * <li>PA4 (210x280 mm ; 8.27x11.02 in)</li>
  1897. * <li>PA5 (140x210 mm ; 5.51x8.27 in)</li>
  1898. * <li>PA6 (105x140 mm ; 4.13x5.51 in)</li>
  1899. * <li>PA7 (70x105 mm ; 2.76x4.13 in)</li>
  1900. * <li>PA8 (52x70 mm ; 2.05x2.76 in)</li>
  1901. * <li>PA9 (35x52 mm ; 1.38x2.05 in)</li>
  1902. * <li>PA10 (26x35 mm ; 1.02x1.38 in)</li>
  1903. * <li><b>Standard Photographic Print Sizes</b></li>
  1904. * <li>PASSPORT_PHOTO (35x45 mm ; 1.38x1.77 in)</li>
  1905. * <li>E (82x120 mm ; 3.25x4.72 in)</li>
  1906. * <li>3R, L (89x127 mm ; 3.50x5.00 in)</li>
  1907. * <li>4R, KG (102x152 mm ; 4.02x5.98 in)</li>
  1908. * <li>4D (120x152 mm ; 4.72x5.98 in)</li>
  1909. * <li>5R, 2L (127x178 mm ; 5.00x7.01 in)</li>
  1910. * <li>6R, 8P (152x203 mm ; 5.98x7.99 in)</li>
  1911. * <li>8R, 6P (203x254 mm ; 7.99x10.00 in)</li>
  1912. * <li>S8R, 6PW (203x305 mm ; 7.99x12.01 in)</li>
  1913. * <li>10R, 4P (254x305 mm ; 10.00x12.01 in)</li>
  1914. * <li>S10R, 4PW (254x381 mm ; 10.00x15.00 in)</li>
  1915. * <li>11R (279x356 mm ; 10.98x14.02 in)</li>
  1916. * <li>S11R (279x432 mm ; 10.98x17.01 in)</li>
  1917. * <li>12R (305x381 mm ; 12.01x15.00 in)</li>
  1918. * <li>S12R (305x456 mm ; 12.01x17.95 in)</li>
  1919. * <li><b>Common Newspaper Sizes</b></li>
  1920. * <li>NEWSPAPER_BROADSHEET (750x600 mm ; 29.53x23.62 in)</li>
  1921. * <li>NEWSPAPER_BERLINER (470x315 mm ; 18.50x12.40 in)</li>
  1922. * <li>NEWSPAPER_COMPACT, NEWSPAPER_TABLOID (430x280 mm ; 16.93x11.02 in)</li>
  1923. * <li><b>Business Cards</b></li>
  1924. * <li>CREDIT_CARD, BUSINESS_CARD, BUSINESS_CARD_ISO7810 (54x86 mm ; 2.13x3.37 in)</li>
  1925. * <li>BUSINESS_CARD_ISO216 (52x74 mm ; 2.05x2.91 in)</li>
  1926. * <li>BUSINESS_CARD_IT, BUSINESS_CARD_UK, BUSINESS_CARD_FR, BUSINESS_CARD_DE, BUSINESS_CARD_ES (55x85 mm ; 2.17x3.35 in)</li>
  1927. * <li>BUSINESS_CARD_US, BUSINESS_CARD_CA (51x89 mm ; 2.01x3.50 in)</li>
  1928. * <li>BUSINESS_CARD_JP (55x91 mm ; 2.17x3.58 in)</li>
  1929. * <li>BUSINESS_CARD_HK (54x90 mm ; 2.13x3.54 in)</li>
  1930. * <li>BUSINESS_CARD_AU, BUSINESS_CARD_DK, BUSINESS_CARD_SE (55x90 mm ; 2.17x3.54 in)</li>
  1931. * <li>BUSINESS_CARD_RU, BUSINESS_CARD_CZ, BUSINESS_CARD_FI, BUSINESS_CARD_HU, BUSINESS_CARD_IL (50x90 mm ; 1.97x3.54 in)</li>
  1932. * <li><b>Billboards</b></li>
  1933. * <li>4SHEET (1016x1524 mm ; 40.00x60.00 in)</li>
  1934. * <li>6SHEET (1200x1800 mm ; 47.24x70.87 in)</li>
  1935. * <li>12SHEET (3048x1524 mm ; 120.00x60.00 in)</li>
  1936. * <li>16SHEET (2032x3048 mm ; 80.00x120.00 in)</li>
  1937. * <li>32SHEET (4064x3048 mm ; 160.00x120.00 in)</li>
  1938. * <li>48SHEET (6096x3048 mm ; 240.00x120.00 in)</li>
  1939. * <li>64SHEET (8128x3048 mm ; 320.00x120.00 in)</li>
  1940. * <li>96SHEET (12192x3048 mm ; 480.00x120.00 in)</li>
  1941. * <li><b>Old Imperial English (some are still used in USA)</b></li>
  1942. * <li>EN_EMPEROR (1219x1829 mm ; 48.00x72.00 in)</li>
  1943. * <li>EN_ANTIQUARIAN (787x1346 mm ; 31.00x53.00 in)</li>
  1944. * <li>EN_GRAND_EAGLE (730x1067 mm ; 28.75x42.00 in)</li>
  1945. * <li>EN_DOUBLE_ELEPHANT (679x1016 mm ; 26.75x40.00 in)</li>
  1946. * <li>EN_ATLAS (660x864 mm ; 26.00x34.00 in)</li>
  1947. * <li>EN_COLOMBIER (597x876 mm ; 23.50x34.50 in)</li>
  1948. * <li>EN_ELEPHANT (584x711 mm ; 23.00x28.00 in)</li>
  1949. * <li>EN_DOUBLE_DEMY (572x902 mm ; 22.50x35.50 in)</li>
  1950. * <li>EN_IMPERIAL (559x762 mm ; 22.00x30.00 in)</li>
  1951. * <li>EN_PRINCESS (546x711 mm ; 21.50x28.00 in)</li>
  1952. * <li>EN_CARTRIDGE (533x660 mm ; 21.00x26.00 in)</li>
  1953. * <li>EN_DOUBLE_LARGE_POST (533x838 mm ; 21.00x33.00 in)</li>
  1954. * <li>EN_ROYAL (508x635 mm ; 20.00x25.00 in)</li>
  1955. * <li>EN_SHEET, EN_HALF_POST (495x597 mm ; 19.50x23.50 in)</li>
  1956. * <li>EN_SUPER_ROYAL (483x686 mm ; 19.00x27.00 in)</li>
  1957. * <li>EN_DOUBLE_POST (483x775 mm ; 19.00x30.50 in)</li>
  1958. * <li>EN_MEDIUM (445x584 mm ; 17.50x23.00 in)</li>
  1959. * <li>EN_DEMY (445x572 mm ; 17.50x22.50 in)</li>
  1960. * <li>EN_LARGE_POST (419x533 mm ; 16.50x21.00 in)</li>
  1961. * <li>EN_COPY_DRAUGHT (406x508 mm ; 16.00x20.00 in)</li>
  1962. * <li>EN_POST (394x489 mm ; 15.50x19.25 in)</li>
  1963. * <li>EN_CROWN (381x508 mm ; 15.00x20.00 in)</li>
  1964. * <li>EN_PINCHED_POST (375x470 mm ; 14.75x18.50 in)</li>
  1965. * <li>EN_BRIEF (343x406 mm ; 13.50x16.00 in)</li>
  1966. * <li>EN_FOOLSCAP (343x432 mm ; 13.50x17.00 in)</li>
  1967. * <li>EN_SMALL_FOOLSCAP (337x419 mm ; 13.25x16.50 in)</li>
  1968. * <li>EN_POTT (318x381 mm ; 12.50x15.00 in)</li>
  1969. * <li><b>Old Imperial Belgian</b></li>
  1970. * <li>BE_GRAND_AIGLE (700x1040 mm ; 27.56x40.94 in)</li>
  1971. * <li>BE_COLOMBIER (620x850 mm ; 24.41x33.46 in)</li>
  1972. * <li>BE_DOUBLE_CARRE (620x920 mm ; 24.41x36.22 in)</li>
  1973. * <li>BE_ELEPHANT (616x770 mm ; 24.25x30.31 in)</li>
  1974. * <li>BE_PETIT_AIGLE (600x840 mm ; 23.62x33.07 in)</li>
  1975. * <li>BE_GRAND_JESUS (550x730 mm ; 21.65x28.74 in)</li>
  1976. * <li>BE_JESUS (540x730 mm ; 21.26x28.74 in)</li>
  1977. * <li>BE_RAISIN (500x650 mm ; 19.69x25.59 in)</li>
  1978. * <li>BE_GRAND_MEDIAN (460x605 mm ; 18.11x23.82 in)</li>
  1979. * <li>BE_DOUBLE_POSTE (435x565 mm ; 17.13x22.24 in)</li>
  1980. * <li>BE_COQUILLE (430x560 mm ; 16.93x22.05 in)</li>
  1981. * <li>BE_PETIT_MEDIAN (415x530 mm ; 16.34x20.87 in)</li>
  1982. * <li>BE_RUCHE (360x460 mm ; 14.17x18.11 in)</li>
  1983. * <li>BE_PROPATRIA (345x430 mm ; 13.58x16.93 in)</li>
  1984. * <li>BE_LYS (317x397 mm ; 12.48x15.63 in)</li>
  1985. * <li>BE_POT (307x384 mm ; 12.09x15.12 in)</li>
  1986. * <li>BE_ROSETTE (270x347 mm ; 10.63x13.66 in)</li>
  1987. * <li><b>Old Imperial French</b></li>
  1988. * <li>FR_UNIVERS (1000x1300 mm ; 39.37x51.18 in)</li>
  1989. * <li>FR_DOUBLE_COLOMBIER (900x1260 mm ; 35.43x49.61 in)</li>
  1990. * <li>FR_GRANDE_MONDE (900x1260 mm ; 35.43x49.61 in)</li>
  1991. * <li>FR_DOUBLE_SOLEIL (800x1200 mm ; 31.50x47.24 in)</li>
  1992. * <li>FR_DOUBLE_JESUS (760x1120 mm ; 29.92x44.09 in)</li>
  1993. * <li>FR_GRAND_AIGLE (750x1060 mm ; 29.53x41.73 in)</li>
  1994. * <li>FR_PETIT_AIGLE (700x940 mm ; 27.56x37.01 in)</li>
  1995. * <li>FR_DOUBLE_RAISIN (650x1000 mm ; 25.59x39.37 in)</li>
  1996. * <li>FR_JOURNAL (650x940 mm ; 25.59x37.01 in)</li>
  1997. * <li>FR_COLOMBIER_AFFICHE (630x900 mm ; 24.80x35.43 in)</li>
  1998. * <li>FR_DOUBLE_CAVALIER (620x920 mm ; 24.41x36.22 in)</li>
  1999. * <li>FR_CLOCHE (600x800 mm ; 23.62x31.50 in)</li>
  2000. * <li>FR_SOLEIL (600x800 mm ; 23.62x31.50 in)</li>
  2001. * <li>FR_DOUBLE_CARRE (560x900 mm ; 22.05x35.43 in)</li>
  2002. * <li>FR_DOUBLE_COQUILLE (560x880 mm ; 22.05x34.65 in)</li>
  2003. * <li>FR_JESUS (560x760 mm ; 22.05x29.92 in)</li>
  2004. * <li>FR_RAISIN (500x650 mm ; 19.69x25.59 in)</li>
  2005. * <li>FR_CAVALIER (460x620 mm ; 18.11x24.41 in)</li>
  2006. * <li>FR_DOUBLE_COURONNE (460x720 mm ; 18.11x28.35 in)</li>
  2007. * <li>FR_CARRE (450x560 mm ; 17.72x22.05 in)</li>
  2008. * <li>FR_COQUILLE (440x560 mm ; 17.32x22.05 in)</li>
  2009. * <li>FR_DOUBLE_TELLIERE (440x680 mm ; 17.32x26.77 in)</li>
  2010. * <li>FR_DOUBLE_CLOCHE (400x600 mm ; 15.75x23.62 in)</li>
  2011. * <li>FR_DOUBLE_POT (400x620 mm ; 15.75x24.41 in)</li>
  2012. * <li>FR_ECU (400x520 mm ; 15.75x20.47 in)</li>
  2013. * <li>FR_COURONNE (360x460 mm ; 14.17x18.11 in)</li>
  2014. * <li>FR_TELLIERE (340x440 mm ; 13.39x17.32 in)</li>
  2015. * <li>FR_POT (310x400 mm ; 12.20x15.75 in)</li>
  2016. * </ul>
  2017. * @return array containing page width and height in points
  2018. * @access public
  2019. * @since 5.0.010 (2010-05-17)
  2020. */
  2021. public function getPageSizeFromFormat($format) {
  2022. // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 25.4 mm)
  2023. switch (strtoupper($format)) {
  2024. // ISO 216 A Series + 2 SIS 014711 extensions
  2025. case 'A0' : {$pf = array( 2383.937, 3370.394); break;}
  2026. case 'A1' : {$pf = array( 1683.780, 2383.937); break;}
  2027. case 'A2' : {$pf = array( 1190.551, 1683.780); break;}
  2028. case 'A3' : {$pf = array( 841.890, 1190.551); break;}
  2029. case 'A4' : {$pf = array( 595.276, 841.890); break;}
  2030. case 'A5' : {$pf = array( 419.528, 595.276); break;}
  2031. case 'A6' : {$pf = array( 297.638, 419.528); break;}
  2032. case 'A7' : {$pf = array( 209.764, 297.638); break;}
  2033. case 'A8' : {$pf = array( 147.402, 209.764); break;}
  2034. case 'A9' : {$pf = array( 104.882, 147.402); break;}
  2035. case 'A10': {$pf = array( 73.701, 104.882); break;}
  2036. case 'A11': {$pf = array( 51.024, 73.701); break;}
  2037. case 'A12': {$pf = array( 36.850, 51.024); break;}
  2038. // ISO 216 B Series + 2 SIS 014711 extensions
  2039. case 'B0' : {$pf = array( 2834.646, 4008.189); break;}
  2040. case 'B1' : {$pf = array( 2004.094, 2834.646); break;}
  2041. case 'B2' : {$pf = array( 1417.323, 2004.094); break;}
  2042. case 'B3' : {$pf = array( 1000.630, 1417.323); break;}
  2043. case 'B4' : {$pf = array( 708.661, 1000.630); break;}
  2044. case 'B5' : {$pf = array( 498.898, 708.661); break;}
  2045. case 'B6' : {$pf = array( 354.331, 498.898); break;}
  2046. case 'B7' : {$pf = array( 249.449, 354.331); break;}
  2047. case 'B8' : {$pf = array( 175.748, 249.449); break;}
  2048. case 'B9' : {$pf = array( 124.724, 175.748); break;}
  2049. case 'B10': {$pf = array( 87.874, 124.724); break;}
  2050. case 'B11': {$pf = array( 62.362, 87.874); break;}
  2051. case 'B12': {$pf = array( 42.520, 62.362); break;}
  2052. // ISO 216 C Series + 2 SIS 014711 extensions + 2 EXTENSION
  2053. case 'C0' : {$pf = array( 2599.370, 3676.535); break;}
  2054. case 'C1' : {$pf = array( 1836.850, 2599.370); break;}
  2055. case 'C2' : {$pf = array( 1298.268, 1836.850); break;}
  2056. case 'C3' : {$pf = array( 918.425, 1298.268); break;}
  2057. case 'C4' : {$pf = array( 649.134, 918.425); break;}
  2058. case 'C5' : {$pf = array( 459.213, 649.134); break;}
  2059. case 'C6' : {$pf = array( 323.150, 459.213); break;}
  2060. case 'C7' : {$pf = array( 229.606, 323.150); break;}
  2061. case 'C8' : {$pf = array( 161.575, 229.606); break;}
  2062. case 'C9' : {$pf = array( 113.386, 161.575); break;}
  2063. case 'C10': {$pf = array( 79.370, 113.386); break;}
  2064. case 'C11': {$pf = array( 56.693, 79.370); break;}
  2065. case 'C12': {$pf = array( 39.685, 56.693); break;}
  2066. case 'C76': {$pf = array( 229.606, 459.213); break;}
  2067. case 'DL' : {$pf = array( 311.811, 623.622); break;}
  2068. // SIS 014711 E Series
  2069. case 'E0' : {$pf = array( 2491.654, 3517.795); break;}
  2070. case 'E1' : {$pf = array( 1757.480, 2491.654); break;}
  2071. case 'E2' : {$pf = array( 1247.244, 1757.480); break;}
  2072. case 'E3' : {$pf = array( 878.740, 1247.244); break;}
  2073. case 'E4' : {$pf = array( 623.622, 878.740); break;}
  2074. case 'E5' : {$pf = array( 439.370, 623.622); break;}
  2075. case 'E6' : {$pf = array( 311.811, 439.370); break;}
  2076. case 'E7' : {$pf = array( 221.102, 311.811); break;}
  2077. case 'E8' : {$pf = array( 155.906, 221.102); break;}
  2078. case 'E9' : {$pf = array( 110.551, 155.906); break;}
  2079. case 'E10': {$pf = array( 76.535, 110.551); break;}
  2080. case 'E11': {$pf = array( 53.858, 76.535); break;}
  2081. case 'E12': {$pf = array( 36.850, 53.858); break;}
  2082. // SIS 014711 G Series
  2083. case 'G0' : {$pf = array( 2715.591, 3838.110); break;}
  2084. case 'G1' : {$pf = array( 1919.055, 2715.591); break;}
  2085. case 'G2' : {$pf = array( 1357.795, 1919.055); break;}
  2086. case 'G3' : {$pf = array( 958.110, 1357.795); break;}
  2087. case 'G4' : {$pf = array( 677.480, 958.110); break;}
  2088. case 'G5' : {$pf = array( 479.055, 677.480); break;}
  2089. case 'G6' : {$pf = array( 337.323, 479.055); break;}
  2090. case 'G7' : {$pf = array( 238.110, 337.323); break;}
  2091. case 'G8' : {$pf = array( 167.244, 238.110); break;}
  2092. case 'G9' : {$pf = array( 119.055, 167.244); break;}
  2093. case 'G10': {$pf = array( 82.205, 119.055); break;}
  2094. case 'G11': {$pf = array( 59.528, 82.205); break;}
  2095. case 'G12': {$pf = array( 39.685, 59.528); break;}
  2096. // ISO Press
  2097. case 'RA0': {$pf = array( 2437.795, 3458.268); break;}
  2098. case 'RA1': {$pf = array( 1729.134, 2437.795); break;}
  2099. case 'RA2': {$pf = array( 1218.898, 1729.134); break;}
  2100. case 'RA3': {$pf = array( 864.567, 1218.898); break;}
  2101. case 'RA4': {$pf = array( 609.449, 864.567); break;}
  2102. case 'SRA0': {$pf = array( 2551.181, 3628.346); break;}
  2103. case 'SRA1': {$pf = array( 1814.173, 2551.181); break;}
  2104. case 'SRA2': {$pf = array( 1275.591, 1814.173); break;}
  2105. case 'SRA3': {$pf = array( 907.087, 1275.591); break;}
  2106. case 'SRA4': {$pf = array( 637.795, 907.087); break;}
  2107. // German DIN 476
  2108. case '4A0': {$pf = array( 4767.874, 6740.787); break;}
  2109. case '2A0': {$pf = array( 3370.394, 4767.874); break;}
  2110. // Variations on the ISO Standard
  2111. case 'A2_EXTRA' : {$pf = array( 1261.417, 1754.646); break;}
  2112. case 'A3+' : {$pf = array( 932.598, 1369.134); break;}
  2113. case 'A3_EXTRA' : {$pf = array( 912.756, 1261.417); break;}
  2114. case 'A3_SUPER' : {$pf = array( 864.567, 1440.000); break;}
  2115. case 'SUPER_A3' : {$pf = array( 864.567, 1380.472); break;}
  2116. case 'A4_EXTRA' : {$pf = array( 666.142, 912.756); break;}
  2117. case 'A4_SUPER' : {$pf = array( 649.134, 912.756); break;}
  2118. case 'SUPER_A4' : {$pf = array( 643.465, 1009.134); break;}
  2119. case 'A4_LONG' : {$pf = array( 595.276, 986.457); break;}
  2120. case 'F4' : {$pf = array( 595.276, 935.433); break;}
  2121. case 'SO_B5_EXTRA': {$pf = array( 572.598, 782.362); break;}
  2122. case 'A5_EXTRA' : {$pf = array( 490.394, 666.142); break;}
  2123. // ANSI Series
  2124. case 'ANSI_E': {$pf = array( 2448.000, 3168.000); break;}
  2125. case 'ANSI_D': {$pf = array( 1584.000, 2448.000); break;}
  2126. case 'ANSI_C': {$pf = array( 1224.000, 1584.000); break;}
  2127. case 'ANSI_B': {$pf = array( 792.000, 1224.000); break;}
  2128. case 'ANSI_A': {$pf = array( 612.000, 792.000); break;}
  2129. // Traditional 'Loose' North American Paper Sizes
  2130. case 'USLEDGER':
  2131. case 'LEDGER' : {$pf = array( 1224.000, 792.000); break;}
  2132. case 'ORGANIZERK':
  2133. case 'BIBLE':
  2134. case 'USTABLOID':
  2135. case 'TABLOID': {$pf = array( 792.000, 1224.000); break;}
  2136. case 'ORGANIZERM':
  2137. case 'USLETTER':
  2138. case 'LETTER' : {$pf = array( 612.000, 792.000); break;}
  2139. case 'USLEGAL':
  2140. case 'LEGAL' : {$pf = array( 612.000, 1008.000); break;}
  2141. case 'GOVERNMENTLETTER':
  2142. case 'GLETTER': {$pf = array( 576.000, 756.000); break;}
  2143. case 'JUNIORLEGAL':
  2144. case 'JLEGAL' : {$pf = array( 576.000, 360.000); break;}
  2145. // Other North American Paper Sizes
  2146. case 'QUADDEMY': {$pf = array( 2520.000, 3240.000); break;}
  2147. case 'SUPER_B': {$pf = array( 936.000, 1368.000); break;}
  2148. case 'QUARTO': {$pf = array( 648.000, 792.000); break;}
  2149. case 'GOVERNMENTLEGAL':
  2150. case 'FOLIO': {$pf = array( 612.000, 936.000); break;}
  2151. case 'MONARCH':
  2152. case 'EXECUTIVE': {$pf = array( 522.000, 756.000); break;}
  2153. case 'ORGANIZERL':
  2154. case 'STATEMENT':
  2155. case 'MEMO': {$pf = array( 396.000, 612.000); break;}
  2156. case 'FOOLSCAP': {$pf = array( 595.440, 936.000); break;}
  2157. case 'COMPACT': {$pf = array( 306.000, 486.000); break;}
  2158. case 'ORGANIZERJ': {$pf = array( 198.000, 360.000); break;}
  2159. // Canadian standard CAN 2-9.60M
  2160. case 'P1': {$pf = array( 1587.402, 2437.795); break;}
  2161. case 'P2': {$pf = array( 1218.898, 1587.402); break;}
  2162. case 'P3': {$pf = array( 793.701, 1218.898); break;}
  2163. case 'P4': {$pf = array( 609.449, 793.701); break;}
  2164. case 'P5': {$pf = array( 396.850, 609.449); break;}
  2165. case 'P6': {$pf = array( 303.307, 396.850); break;}
  2166. // North American Architectural Sizes
  2167. case 'ARCH_E' : {$pf = array( 2592.000, 3456.000); break;}
  2168. case 'ARCH_E1': {$pf = array( 2160.000, 3024.000); break;}
  2169. case 'ARCH_D' : {$pf = array( 1728.000, 2592.000); break;}
  2170. case 'BROADSHEET':
  2171. case 'ARCH_C' : {$pf = array( 1296.000, 1728.000); break;}
  2172. case 'ARCH_B' : {$pf = array( 864.000, 1296.000); break;}
  2173. case 'ARCH_A' : {$pf = array( 648.000, 864.000); break;}
  2174. // --- North American Envelope Sizes ---
  2175. // - Announcement Envelopes
  2176. case 'ANNENV_A2' : {$pf = array( 314.640, 414.000); break;}
  2177. case 'ANNENV_A6' : {$pf = array( 342.000, 468.000); break;}
  2178. case 'ANNENV_A7' : {$pf = array( 378.000, 522.000); break;}
  2179. case 'ANNENV_A8' : {$pf = array( 396.000, 584.640); break;}
  2180. case 'ANNENV_A10' : {$pf = array( 450.000, 692.640); break;}
  2181. case 'ANNENV_SLIM': {$pf = array( 278.640, 638.640); break;}
  2182. // - Commercial Envelopes
  2183. case 'COMMENV_N6_1/4': {$pf = array( 252.000, 432.000); break;}
  2184. case 'COMMENV_N6_3/4': {$pf = array( 260.640, 468.000); break;}
  2185. case 'COMMENV_N8' : {$pf = array( 278.640, 540.000); break;}
  2186. case 'COMMENV_N9' : {$pf = array( 278.640, 638.640); break;}
  2187. case 'COMMENV_N10' : {$pf = array( 296.640, 684.000); break;}
  2188. case 'COMMENV_N11' : {$pf = array( 324.000, 746.640); break;}
  2189. case 'COMMENV_N12' : {$pf = array( 342.000, 792.000); break;}
  2190. case 'COMMENV_N14' : {$pf = array( 360.000, 828.000); break;}
  2191. // - Catalogue Envelopes
  2192. case 'CATENV_N1' : {$pf = array( 432.000, 648.000); break;}
  2193. case 'CATENV_N1_3/4' : {$pf = array( 468.000, 684.000); break;}
  2194. case 'CATENV_N2' : {$pf = array( 468.000, 720.000); break;}
  2195. case 'CATENV_N3' : {$pf = array( 504.000, 720.000); break;}
  2196. case 'CATENV_N6' : {$pf = array( 540.000, 756.000); break;}
  2197. case 'CATENV_N7' : {$pf = array( 576.000, 792.000); break;}
  2198. case 'CATENV_N8' : {$pf = array( 594.000, 810.000); break;}
  2199. case 'CATENV_N9_1/2' : {$pf = array( 612.000, 756.000); break;}
  2200. case 'CATENV_N9_3/4' : {$pf = array( 630.000, 810.000); break;}
  2201. case 'CATENV_N10_1/2': {$pf = array( 648.000, 864.000); break;}
  2202. case 'CATENV_N12_1/2': {$pf = array( 684.000, 900.000); break;}
  2203. case 'CATENV_N13_1/2': {$pf = array( 720.000, 936.000); break;}
  2204. case 'CATENV_N14_1/4': {$pf = array( 810.000, 882.000); break;}
  2205. case 'CATENV_N14_1/2': {$pf = array( 828.000, 1044.000); break;}
  2206. // Japanese (JIS P 0138-61) Standard B-Series
  2207. case 'JIS_B0' : {$pf = array( 2919.685, 4127.244); break;}
  2208. case 'JIS_B1' : {$pf = array( 2063.622, 2919.685); break;}
  2209. case 'JIS_B2' : {$pf = array( 1459.843, 2063.622); break;}
  2210. case 'JIS_B3' : {$pf = array( 1031.811, 1459.843); break;}
  2211. case 'JIS_B4' : {$pf = array( 728.504, 1031.811); break;}
  2212. case 'JIS_B5' : {$pf = array( 515.906, 728.504); break;}
  2213. case 'JIS_B6' : {$pf = array( 362.835, 515.906); break;}
  2214. case 'JIS_B7' : {$pf = array( 257.953, 362.835); break;}
  2215. case 'JIS_B8' : {$pf = array( 181.417, 257.953); break;}
  2216. case 'JIS_B9' : {$pf = array( 127.559, 181.417); break;}
  2217. case 'JIS_B10': {$pf = array( 90.709, 127.559); break;}
  2218. case 'JIS_B11': {$pf = array( 62.362, 90.709); break;}
  2219. case 'JIS_B12': {$pf = array( 45.354, 62.362); break;}
  2220. // PA Series
  2221. case 'PA0' : {$pf = array( 2381.102, 3174.803,); break;}
  2222. case 'PA1' : {$pf = array( 1587.402, 2381.102); break;}
  2223. case 'PA2' : {$pf = array( 1190.551, 1587.402); break;}
  2224. case 'PA3' : {$pf = array( 793.701, 1190.551); break;}
  2225. case 'PA4' : {$pf = array( 595.276, 793.701); break;}
  2226. case 'PA5' : {$pf = array( 396.850, 595.276); break;}
  2227. case 'PA6' : {$pf = array( 297.638, 396.850); break;}
  2228. case 'PA7' : {$pf = array( 198.425, 297.638); break;}
  2229. case 'PA8' : {$pf = array( 147.402, 198.425); break;}
  2230. case 'PA9' : {$pf = array( 99.213, 147.402); break;}
  2231. case 'PA10': {$pf = array( 73.701, 99.213); break;}
  2232. // Standard Photographic Print Sizes
  2233. case 'PASSPORT_PHOTO': {$pf = array( 99.213, 127.559); break;}
  2234. case 'E' : {$pf = array( 233.858, 340.157); break;}
  2235. case 'L':
  2236. case '3R' : {$pf = array( 252.283, 360.000); break;}
  2237. case 'KG':
  2238. case '4R' : {$pf = array( 289.134, 430.866); break;}
  2239. case '4D' : {$pf = array( 340.157, 430.866); break;}
  2240. case '2L':
  2241. case '5R' : {$pf = array( 360.000, 504.567); break;}
  2242. case '8P':
  2243. case '6R' : {$pf = array( 430.866, 575.433); break;}
  2244. case '6P':
  2245. case '8R' : {$pf = array( 575.433, 720.000); break;}
  2246. case '6PW':
  2247. case 'S8R' : {$pf = array( 575.433, 864.567); break;}
  2248. case '4P':
  2249. case '10R' : {$pf = array( 720.000, 864.567); break;}
  2250. case '4PW':
  2251. case 'S10R': {$pf = array( 720.000, 1080.000); break;}
  2252. case '11R' : {$pf = array( 790.866, 1009.134); break;}
  2253. case 'S11R': {$pf = array( 790.866, 1224.567); break;}
  2254. case '12R' : {$pf = array( 864.567, 1080.000); break;}
  2255. case 'S12R': {$pf = array( 864.567, 1292.598); break;}
  2256. // Common Newspaper Sizes
  2257. case 'NEWSPAPER_BROADSHEET': {$pf = array( 2125.984, 1700.787); break;}
  2258. case 'NEWSPAPER_BERLINER' : {$pf = array( 1332.283, 892.913); break;}
  2259. case 'NEWSPAPER_TABLOID':
  2260. case 'NEWSPAPER_COMPACT' : {$pf = array( 1218.898, 793.701); break;}
  2261. // Business Cards
  2262. case 'CREDIT_CARD':
  2263. case 'BUSINESS_CARD':
  2264. case 'BUSINESS_CARD_ISO7810': {$pf = array( 153.014, 242.646); break;}
  2265. case 'BUSINESS_CARD_ISO216' : {$pf = array( 147.402, 209.764); break;}
  2266. case 'BUSINESS_CARD_IT':
  2267. case 'BUSINESS_CARD_UK':
  2268. case 'BUSINESS_CARD_FR':
  2269. case 'BUSINESS_CARD_DE':
  2270. case 'BUSINESS_CARD_ES' : {$pf = array( 155.906, 240.945); break;}
  2271. case 'BUSINESS_CARD_CA':
  2272. case 'BUSINESS_CARD_US' : {$pf = array( 144.567, 252.283); break;}
  2273. case 'BUSINESS_CARD_JP' : {$pf = array( 155.906, 257.953); break;}
  2274. case 'BUSINESS_CARD_HK' : {$pf = array( 153.071, 255.118); break;}
  2275. case 'BUSINESS_CARD_AU':
  2276. case 'BUSINESS_CARD_DK':
  2277. case 'BUSINESS_CARD_SE' : {$pf = array( 155.906, 255.118); break;}
  2278. case 'BUSINESS_CARD_RU':
  2279. case 'BUSINESS_CARD_CZ':
  2280. case 'BUSINESS_CARD_FI':
  2281. case 'BUSINESS_CARD_HU':
  2282. case 'BUSINESS_CARD_IL' : {$pf = array( 141.732, 255.118); break;}
  2283. // Billboards
  2284. case '4SHEET' : {$pf = array( 2880.000, 4320.000); break;}
  2285. case '6SHEET' : {$pf = array( 3401.575, 5102.362); break;}
  2286. case '12SHEET': {$pf = array( 8640.000, 4320.000); break;}
  2287. case '16SHEET': {$pf = array( 5760.000, 8640.000); break;}
  2288. case '32SHEET': {$pf = array(11520.000, 8640.000); break;}
  2289. case '48SHEET': {$pf = array(17280.000, 8640.000); break;}
  2290. case '64SHEET': {$pf = array(23040.000, 8640.000); break;}
  2291. case '96SHEET': {$pf = array(34560.000, 8640.000); break;}
  2292. // Old European Sizes
  2293. // - Old Imperial English Sizes
  2294. case 'EN_EMPEROR' : {$pf = array( 3456.000, 5184.000); break;}
  2295. case 'EN_ANTIQUARIAN' : {$pf = array( 2232.000, 3816.000); break;}
  2296. case 'EN_GRAND_EAGLE' : {$pf = array( 2070.000, 3024.000); break;}
  2297. case 'EN_DOUBLE_ELEPHANT' : {$pf = array( 1926.000, 2880.000); break;}
  2298. case 'EN_ATLAS' : {$pf = array( 1872.000, 2448.000); break;}
  2299. case 'EN_COLOMBIER' : {$pf = array( 1692.000, 2484.000); break;}
  2300. case 'EN_ELEPHANT' : {$pf = array( 1656.000, 2016.000); break;}
  2301. case 'EN_DOUBLE_DEMY' : {$pf = array( 1620.000, 2556.000); break;}
  2302. case 'EN_IMPERIAL' : {$pf = array( 1584.000, 2160.000); break;}
  2303. case 'EN_PRINCESS' : {$pf = array( 1548.000, 2016.000); break;}
  2304. case 'EN_CARTRIDGE' : {$pf = array( 1512.000, 1872.000); break;}
  2305. case 'EN_DOUBLE_LARGE_POST': {$pf = array( 1512.000, 2376.000); break;}
  2306. case 'EN_ROYAL' : {$pf = array( 1440.000, 1800.000); break;}
  2307. case 'EN_SHEET':
  2308. case 'EN_HALF_POST' : {$pf = array( 1404.000, 1692.000); break;}
  2309. case 'EN_SUPER_ROYAL' : {$pf = array( 1368.000, 1944.000); break;}
  2310. case 'EN_DOUBLE_POST' : {$pf = array( 1368.000, 2196.000); break;}
  2311. case 'EN_MEDIUM' : {$pf = array( 1260.000, 1656.000); break;}
  2312. case 'EN_DEMY' : {$pf = array( 1260.000, 1620.000); break;}
  2313. case 'EN_LARGE_POST' : {$pf = array( 1188.000, 1512.000); break;}
  2314. case 'EN_COPY_DRAUGHT' : {$pf = array( 1152.000, 1440.000); break;}
  2315. case 'EN_POST' : {$pf = array( 1116.000, 1386.000); break;}
  2316. case 'EN_CROWN' : {$pf = array( 1080.000, 1440.000); break;}
  2317. case 'EN_PINCHED_POST' : {$pf = array( 1062.000, 1332.000); break;}
  2318. case 'EN_BRIEF' : {$pf = array( 972.000, 1152.000); break;}
  2319. case 'EN_FOOLSCAP' : {$pf = array( 972.000, 1224.000); break;}
  2320. case 'EN_SMALL_FOOLSCAP' : {$pf = array( 954.000, 1188.000); break;}
  2321. case 'EN_POTT' : {$pf = array( 900.000, 1080.000); break;}
  2322. // - Old Imperial Belgian Sizes
  2323. case 'BE_GRAND_AIGLE' : {$pf = array( 1984.252, 2948.031); break;}
  2324. case 'BE_COLOMBIER' : {$pf = array( 1757.480, 2409.449); break;}
  2325. case 'BE_DOUBLE_CARRE': {$pf = array( 1757.480, 2607.874); break;}
  2326. case 'BE_ELEPHANT' : {$pf = array( 1746.142, 2182.677); break;}
  2327. case 'BE_PETIT_AIGLE' : {$pf = array( 1700.787, 2381.102); break;}
  2328. case 'BE_GRAND_JESUS' : {$pf = array( 1559.055, 2069.291); break;}
  2329. case 'BE_JESUS' : {$pf = array( 1530.709, 2069.291); break;}
  2330. case 'BE_RAISIN' : {$pf = array( 1417.323, 1842.520); break;}
  2331. case 'BE_GRAND_MEDIAN': {$pf = array( 1303.937, 1714.961); break;}
  2332. case 'BE_DOUBLE_POSTE': {$pf = array( 1233.071, 1601.575); break;}
  2333. case 'BE_COQUILLE' : {$pf = array( 1218.898, 1587.402); break;}
  2334. case 'BE_PETIT_MEDIAN': {$pf = array( 1176.378, 1502.362); break;}
  2335. case 'BE_RUCHE' : {$pf = array( 1020.472, 1303.937); break;}
  2336. case 'BE_PROPATRIA' : {$pf = array( 977.953, 1218.898); break;}
  2337. case 'BE_LYS' : {$pf = array( 898.583, 1125.354); break;}
  2338. case 'BE_POT' : {$pf = array( 870.236, 1088.504); break;}
  2339. case 'BE_ROSETTE' : {$pf = array( 765.354, 983.622); break;}
  2340. // - Old Imperial French Sizes
  2341. case 'FR_UNIVERS' : {$pf = array( 2834.646, 3685.039); break;}
  2342. case 'FR_DOUBLE_COLOMBIER' : {$pf = array( 2551.181, 3571.654); break;}
  2343. case 'FR_GRANDE_MONDE' : {$pf = array( 2551.181, 3571.654); break;}
  2344. case 'FR_DOUBLE_SOLEIL' : {$pf = array( 2267.717, 3401.575); break;}
  2345. case 'FR_DOUBLE_JESUS' : {$pf = array( 2154.331, 3174.803); break;}
  2346. case 'FR_GRAND_AIGLE' : {$pf = array( 2125.984, 3004.724); break;}
  2347. case 'FR_PETIT_AIGLE' : {$pf = array( 1984.252, 2664.567); break;}
  2348. case 'FR_DOUBLE_RAISIN' : {$pf = array( 1842.520, 2834.646); break;}
  2349. case 'FR_JOURNAL' : {$pf = array( 1842.520, 2664.567); break;}
  2350. case 'FR_COLOMBIER_AFFICHE': {$pf = array( 1785.827, 2551.181); break;}
  2351. case 'FR_DOUBLE_CAVALIER' : {$pf = array( 1757.480, 2607.874); break;}
  2352. case 'FR_CLOCHE' : {$pf = array( 1700.787, 2267.717); break;}
  2353. case 'FR_SOLEIL' : {$pf = array( 1700.787, 2267.717); break;}
  2354. case 'FR_DOUBLE_CARRE' : {$pf = array( 1587.402, 2551.181); break;}
  2355. case 'FR_DOUBLE_COQUILLE' : {$pf = array( 1587.402, 2494.488); break;}
  2356. case 'FR_JESUS' : {$pf = array( 1587.402, 2154.331); break;}
  2357. case 'FR_RAISIN' : {$pf = array( 1417.323, 1842.520); break;}
  2358. case 'FR_CAVALIER' : {$pf = array( 1303.937, 1757.480); break;}
  2359. case 'FR_DOUBLE_COURONNE' : {$pf = array( 1303.937, 2040.945); break;}
  2360. case 'FR_CARRE' : {$pf = array( 1275.591, 1587.402); break;}
  2361. case 'FR_COQUILLE' : {$pf = array( 1247.244, 1587.402); break;}
  2362. case 'FR_DOUBLE_TELLIERE' : {$pf = array( 1247.244, 1927.559); break;}
  2363. case 'FR_DOUBLE_CLOCHE' : {$pf = array( 1133.858, 1700.787); break;}
  2364. case 'FR_DOUBLE_POT' : {$pf = array( 1133.858, 1757.480); break;}
  2365. case 'FR_ECU' : {$pf = array( 1133.858, 1474.016); break;}
  2366. case 'FR_COURONNE' : {$pf = array( 1020.472, 1303.937); break;}
  2367. case 'FR_TELLIERE' : {$pf = array( 963.780, 1247.244); break;}
  2368. case 'FR_POT' : {$pf = array( 878.740, 1133.858); break;}
  2369. // DEFAULT ISO A4
  2370. default: {$pf = array( 595.276, 841.890); break;}
  2371. }
  2372. return $pf;
  2373. }
  2374. /**
  2375. * Change the format of the current page
  2376. * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() documentation or an array of two numners (width, height) or an array containing the following measures and options:<ul>
  2377. * <li>['format'] = page format name (one of the above);</li>
  2378. * <li>['Rotate'] : The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li>
  2379. * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
  2380. * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
  2381. * <li>['MediaBox']['llx'] : lower-left x coordinate in points</li>
  2382. * <li>['MediaBox']['lly'] : lower-left y coordinate in points</li>
  2383. * <li>['MediaBox']['urx'] : upper-right x coordinate in points</li>
  2384. * <li>['MediaBox']['ury'] : upper-right y coordinate in points</li>
  2385. * <li>['CropBox'] : the visible region of default user space:</li>
  2386. * <li>['CropBox']['llx'] : lower-left x coordinate in points</li>
  2387. * <li>['CropBox']['lly'] : lower-left y coordinate in points</li>
  2388. * <li>['CropBox']['urx'] : upper-right x coordinate in points</li>
  2389. * <li>['CropBox']['ury'] : upper-right y coordinate in points</li>
  2390. * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
  2391. * <li>['BleedBox']['llx'] : lower-left x coordinate in points</li>
  2392. * <li>['BleedBox']['lly'] : lower-left y coordinate in points</li>
  2393. * <li>['BleedBox']['urx'] : upper-right x coordinate in points</li>
  2394. * <li>['BleedBox']['ury'] : upper-right y coordinate in points</li>
  2395. * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
  2396. * <li>['TrimBox']['llx'] : lower-left x coordinate in points</li>
  2397. * <li>['TrimBox']['lly'] : lower-left y coordinate in points</li>
  2398. * <li>['TrimBox']['urx'] : upper-right x coordinate in points</li>
  2399. * <li>['TrimBox']['ury'] : upper-right y coordinate in points</li>
  2400. * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
  2401. * <li>['ArtBox']['llx'] : lower-left x coordinate in points</li>
  2402. * <li>['ArtBox']['lly'] : lower-left y coordinate in points</li>
  2403. * <li>['ArtBox']['urx'] : upper-right x coordinate in points</li>
  2404. * <li>['ArtBox']['ury'] : upper-right y coordinate in points</li>
  2405. * <li>['BoxColorInfo'] :specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for each of the possible page boundaries other than the MediaBox:</li>
  2406. * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
  2407. * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
  2408. * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
  2409. * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
  2410. * <li>['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation</li>
  2411. * <li>['trans']['Dur'] : The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li>
  2412. * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
  2413. * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
  2414. * <li>['trans']['Dm'] : (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li>
  2415. * <li>['trans']['M'] : (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li>
  2416. * <li>['trans']['Di'] : (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li>
  2417. * <li>['trans']['SS'] : (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0.</li>
  2418. * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
  2419. * </ul>
  2420. * @param string $orientation page orientation. Possible values are (case insensitive):<ul>
  2421. * <li>P or Portrait (default)</li>
  2422. * <li>L or Landscape</li>
  2423. * <li>'' (empty string) for automatic orientation</li>
  2424. * </ul>
  2425. * @access protected
  2426. * @since 3.0.015 (2008-06-06)
  2427. * @see getPageSizeFromFormat()
  2428. */
  2429. protected function setPageFormat($format, $orientation='P') {
  2430. if (!empty($format) AND isset($this->pagedim[$this->page])) {
  2431. // remove inherited values
  2432. unset($this->pagedim[$this->page]);
  2433. }
  2434. if (is_string($format)) {
  2435. // get page measures from format name
  2436. $pf = $this->getPageSizeFromFormat($format);
  2437. $this->fwPt = $pf[0];
  2438. $this->fhPt = $pf[1];
  2439. } else {
  2440. // the boundaries of the physical medium on which the page shall be displayed or printed
  2441. if (isset($format['MediaBox'])) {
  2442. $this->setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false);
  2443. $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
  2444. $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
  2445. } else {
  2446. if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
  2447. $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
  2448. } else {
  2449. if (!isset($format['format'])) {
  2450. // default value
  2451. $format['format'] = 'A4';
  2452. }
  2453. $pf = $this->getPageSizeFromFormat($format['format']);
  2454. }
  2455. $this->fwPt = $pf[0];
  2456. $this->fhPt = $pf[1];
  2457. $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
  2458. }
  2459. // the visible region of default user space
  2460. if (isset($format['CropBox'])) {
  2461. $this->setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false);
  2462. }
  2463. // the region to which the contents of the page shall be clipped when output in a production environment
  2464. if (isset($format['BleedBox'])) {
  2465. $this->setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false);
  2466. }
  2467. // the intended dimensions of the finished page after trimming
  2468. if (isset($format['TrimBox'])) {
  2469. $this->setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false);
  2470. }
  2471. // the page's meaningful content (including potential white space)
  2472. if (isset($format['ArtBox'])) {
  2473. $this->setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false);
  2474. }
  2475. // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
  2476. if (isset($format['BoxColorInfo'])) {
  2477. $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
  2478. }
  2479. if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
  2480. // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
  2481. $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
  2482. }
  2483. if (isset($format['PZ'])) {
  2484. // The page's preferred zoom (magnification) factor
  2485. $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
  2486. }
  2487. if (isset($format['trans'])) {
  2488. // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
  2489. if (isset($format['trans']['Dur'])) {
  2490. // The page's display duration
  2491. $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
  2492. }
  2493. $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
  2494. if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
  2495. // The transition style that shall be used when moving to this page from another during a presentation
  2496. $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
  2497. $valid_effect = array('Split', 'Blinds');
  2498. $valid_vals = array('H', 'V');
  2499. if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
  2500. $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
  2501. }
  2502. $valid_effect = array('Split', 'Box', 'Fly');
  2503. $valid_vals = array('I', 'O');
  2504. if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
  2505. $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
  2506. }
  2507. $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
  2508. if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
  2509. if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
  2510. OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
  2511. OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
  2512. $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
  2513. }
  2514. }
  2515. if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
  2516. $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
  2517. }
  2518. if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
  2519. $this->pagedim[$this->page]['trans']['B'] = 'true';
  2520. }
  2521. } else {
  2522. $this->pagedim[$this->page]['trans']['S'] = 'R';
  2523. }
  2524. if (isset($format['trans']['D'])) {
  2525. // The duration of the transition effect, in seconds
  2526. $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
  2527. } else {
  2528. $this->pagedim[$this->page]['trans']['D'] = 1;
  2529. }
  2530. }
  2531. }
  2532. $this->setPageOrientation($orientation);
  2533. }
  2534. /**
  2535. * Set page boundaries.
  2536. * @param int $page page number
  2537. * @param string $type valid values are: <ul><li>'MediaBox' : the boundaries of the physical medium on which the page shall be displayed or printed;</li><li>'CropBox' : the visible region of default user space;</li><li>'BleedBox' : the region to which the contents of the page shall be clipped when output in a production environment;</li><li>'TrimBox' : the intended dimensions of the finished page after trimming;</li><li>'ArtBox' : the page's meaningful content (including potential white space).</li></ul>
  2538. * @param float $llx lower-left x coordinate in user units
  2539. * @param float $lly lower-left y coordinate in user units
  2540. * @param float $urx upper-right x coordinate in user units
  2541. * @param float $ury upper-right y coordinate in user units
  2542. * @param boolean $points if true uses user units as unit of measure, otherwise uses PDF points
  2543. * @access public
  2544. * @since 5.0.010 (2010-05-17)
  2545. */
  2546. public function setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false) {
  2547. if (!isset($this->pagedim[$page])) {
  2548. // initialize array
  2549. $this->pagedim[$page] = array();
  2550. }
  2551. $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
  2552. if (!in_array($type, $pageboxes)) {
  2553. return;
  2554. }
  2555. if ($points) {
  2556. $k = 1;
  2557. } else {
  2558. $k = $this->k;
  2559. }
  2560. $this->pagedim[$page][$type]['llx'] = ($llx * $k);
  2561. $this->pagedim[$page][$type]['lly'] = ($lly * $k);
  2562. $this->pagedim[$page][$type]['urx'] = ($urx * $k);
  2563. $this->pagedim[$page][$type]['ury'] = ($ury * $k);
  2564. }
  2565. /**
  2566. * Swap X and Y coordinates of page boxes (change page boxes orientation).
  2567. * @param int $page page number
  2568. * @access protected
  2569. * @since 5.0.010 (2010-05-17)
  2570. */
  2571. protected function swapPageBoxCoordinates($page) {
  2572. $pageboxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
  2573. foreach ($pageboxes as $type) {
  2574. // swap X and Y coordinates
  2575. if (isset($this->pagedim[$page][$type])) {
  2576. $tmp = $this->pagedim[$page][$type]['llx'];
  2577. $this->pagedim[$page][$type]['llx'] = $this->pagedim[$page][$type]['lly'];
  2578. $this->pagedim[$page][$type]['lly'] = $tmp;
  2579. $tmp = $this->pagedim[$page][$type]['urx'];
  2580. $this->pagedim[$page][$type]['urx'] = $this->pagedim[$page][$type]['ury'];
  2581. $this->pagedim[$page][$type]['ury'] = $tmp;
  2582. }
  2583. }
  2584. }
  2585. /**
  2586. * Set page orientation.
  2587. * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
  2588. * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
  2589. * @param float $bottommargin bottom margin of the page.
  2590. * @access public
  2591. * @since 3.0.015 (2008-06-06)
  2592. */
  2593. public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
  2594. if (!isset($this->pagedim[$this->page]['MediaBox'])) {
  2595. // the boundaries of the physical medium on which the page shall be displayed or printed
  2596. $this->setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true);
  2597. }
  2598. if (!isset($this->pagedim[$this->page]['CropBox'])) {
  2599. // the visible region of default user space
  2600. $this->setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true);
  2601. }
  2602. if (!isset($this->pagedim[$this->page]['BleedBox'])) {
  2603. // the region to which the contents of the page shall be clipped when output in a production environment
  2604. $this->setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
  2605. }
  2606. if (!isset($this->pagedim[$this->page]['TrimBox'])) {
  2607. // the intended dimensions of the finished page after trimming
  2608. $this->setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
  2609. }
  2610. if (!isset($this->pagedim[$this->page]['ArtBox'])) {
  2611. // the page's meaningful content (including potential white space)
  2612. $this->setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true);
  2613. }
  2614. if (!isset($this->pagedim[$this->page]['Rotate'])) {
  2615. // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
  2616. $this->pagedim[$this->page]['Rotate'] = 0;
  2617. }
  2618. if (!isset($this->pagedim[$this->page]['PZ'])) {
  2619. // The page's preferred zoom (magnification) factor
  2620. $this->pagedim[$this->page]['PZ'] = 1;
  2621. }
  2622. if ($this->fwPt > $this->fhPt) {
  2623. // landscape
  2624. $default_orientation = 'L';
  2625. } else {
  2626. // portrait
  2627. $default_orientation = 'P';
  2628. }
  2629. $valid_orientations = array('P', 'L');
  2630. if (empty($orientation)) {
  2631. $orientation = $default_orientation;
  2632. } else {
  2633. $orientation = strtoupper($orientation{0});
  2634. }
  2635. if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
  2636. $this->CurOrientation = $orientation;
  2637. $this->wPt = $this->fhPt;
  2638. $this->hPt = $this->fwPt;
  2639. } else {
  2640. $this->CurOrientation = $default_orientation;
  2641. $this->wPt = $this->fwPt;
  2642. $this->hPt = $this->fhPt;
  2643. }
  2644. if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
  2645. // swap X and Y coordinates (change page orientation)
  2646. $this->swapPageBoxCoordinates($this->page);
  2647. }
  2648. $this->w = $this->wPt / $this->k;
  2649. $this->h = $this->hPt / $this->k;
  2650. if ($this->empty_string($autopagebreak)) {
  2651. if (isset($this->AutoPageBreak)) {
  2652. $autopagebreak = $this->AutoPageBreak;
  2653. } else {
  2654. $autopagebreak = true;
  2655. }
  2656. }
  2657. if ($this->empty_string($bottommargin)) {
  2658. if (isset($this->bMargin)) {
  2659. $bottommargin = $this->bMargin;
  2660. } else {
  2661. // default value = 2 cm
  2662. $bottommargin = 2 * 28.35 / $this->k;
  2663. }
  2664. }
  2665. $this->SetAutoPageBreak($autopagebreak, $bottommargin);
  2666. // store page dimensions
  2667. $this->pagedim[$this->page]['w'] = $this->wPt;
  2668. $this->pagedim[$this->page]['h'] = $this->hPt;
  2669. $this->pagedim[$this->page]['wk'] = $this->w;
  2670. $this->pagedim[$this->page]['hk'] = $this->h;
  2671. $this->pagedim[$this->page]['tm'] = $this->tMargin;
  2672. $this->pagedim[$this->page]['bm'] = $bottommargin;
  2673. $this->pagedim[$this->page]['lm'] = $this->lMargin;
  2674. $this->pagedim[$this->page]['rm'] = $this->rMargin;
  2675. $this->pagedim[$this->page]['pb'] = $autopagebreak;
  2676. $this->pagedim[$this->page]['or'] = $this->CurOrientation;
  2677. $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
  2678. $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
  2679. }
  2680. /**
  2681. * Set regular expression to detect withespaces or word separators.
  2682. * The pattern delimiter must be the forward-slash character '/'.
  2683. * Some example patterns are:
  2684. * <pre>
  2685. * Non-Unicode or missing PCRE unicode support: '/[^\S\xa0]/'
  2686. * Unicode and PCRE unicode support: '/[^\S\P{Z}\xa0]/u'
  2687. * Unicode and PCRE unicode support in Chinese mode: '/[^\S\P{Z}\P{Lo}\xa0]/u'
  2688. * if PCRE unicode support is turned ON (\P is the negate class of \p):
  2689. * \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
  2690. * \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
  2691. * \p{Lo} is needed for Chinese characters because are packed next to each other without spaces in between.
  2692. * </pre>
  2693. * @param string $re regular expression (leave empty for default).
  2694. * @access public
  2695. * @since 4.6.016 (2009-06-15)
  2696. */
  2697. public function setSpacesRE($re='/[^\S\xa0]/') {
  2698. $this->re_spaces = $re;
  2699. $re_parts = explode('/', $re);
  2700. // get pattern parts
  2701. $this->re_space = array();
  2702. if (isset($re_parts[1]) AND !empty($re_parts[1])) {
  2703. $this->re_space['p'] = $re_parts[1];
  2704. } else {
  2705. $this->re_space['p'] = '[\s]';
  2706. }
  2707. // set pattern modifiers
  2708. if (isset($re_parts[2]) AND !empty($re_parts[2])) {
  2709. $this->re_space['m'] = $re_parts[2];
  2710. } else {
  2711. $this->re_space['m'] = '';
  2712. }
  2713. }
  2714. /**
  2715. * Enable or disable Right-To-Left language mode
  2716. * @param Boolean $enable if true enable Right-To-Left language mode.
  2717. * @param Boolean $resetx if true reset the X position on direction change.
  2718. * @access public
  2719. * @since 2.0.000 (2008-01-03)
  2720. */
  2721. public function setRTL($enable, $resetx=true) {
  2722. $enable = $enable ? true : false;
  2723. $resetx = ($resetx AND ($enable != $this->rtl));
  2724. $this->rtl = $enable;
  2725. $this->tmprtl = false;
  2726. if ($resetx) {
  2727. $this->Ln(0);
  2728. }
  2729. }
  2730. /**
  2731. * Return the RTL status
  2732. * @return boolean
  2733. * @access public
  2734. * @since 4.0.012 (2008-07-24)
  2735. */
  2736. public function getRTL() {
  2737. return $this->rtl;
  2738. }
  2739. /**
  2740. * Force temporary RTL language direction
  2741. * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
  2742. * @access public
  2743. * @since 2.1.000 (2008-01-09)
  2744. */
  2745. public function setTempRTL($mode) {
  2746. $newmode = false;
  2747. switch (strtoupper($mode)) {
  2748. case 'LTR':
  2749. case 'L': {
  2750. if ($this->rtl) {
  2751. $newmode = 'L';
  2752. }
  2753. break;
  2754. }
  2755. case 'RTL':
  2756. case 'R': {
  2757. if (!$this->rtl) {
  2758. $newmode = 'R';
  2759. }
  2760. break;
  2761. }
  2762. case false:
  2763. default: {
  2764. $newmode = false;
  2765. break;
  2766. }
  2767. }
  2768. $this->tmprtl = $newmode;
  2769. }
  2770. /**
  2771. * Return the current temporary RTL status
  2772. * @return boolean
  2773. * @access public
  2774. * @since 4.8.014 (2009-11-04)
  2775. */
  2776. public function isRTLTextDir() {
  2777. return ($this->rtl OR ($this->tmprtl == 'R'));
  2778. }
  2779. /**
  2780. * Set the last cell height.
  2781. * @param float $h cell height.
  2782. * @author Nicola Asuni
  2783. * @access public
  2784. * @since 1.53.0.TC034
  2785. */
  2786. public function setLastH($h) {
  2787. $this->lasth = $h;
  2788. }
  2789. /**
  2790. * Reset the last cell height.
  2791. * @access public
  2792. * @since 5.9.000 (2010-10-03)
  2793. */
  2794. public function resetLastH() {
  2795. $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
  2796. }
  2797. /**
  2798. * Get the last cell height.
  2799. * @return last cell height
  2800. * @access public
  2801. * @since 4.0.017 (2008-08-05)
  2802. */
  2803. public function getLastH() {
  2804. return $this->lasth;
  2805. }
  2806. /**
  2807. * Set the adjusting factor to convert pixels to user units.
  2808. * @param float $scale adjusting factor to convert pixels to user units.
  2809. * @author Nicola Asuni
  2810. * @access public
  2811. * @since 1.5.2
  2812. */
  2813. public function setImageScale($scale) {
  2814. $this->imgscale = $scale;
  2815. }
  2816. /**
  2817. * Returns the adjusting factor to convert pixels to user units.
  2818. * @return float adjusting factor to convert pixels to user units.
  2819. * @author Nicola Asuni
  2820. * @access public
  2821. * @since 1.5.2
  2822. */
  2823. public function getImageScale() {
  2824. return $this->imgscale;
  2825. }
  2826. /**
  2827. * Returns an array of page dimensions:
  2828. * <ul><li>$this->pagedim[$this->page]['w'] = page width in points</li><li>$this->pagedim[$this->page]['h'] = height in points</li><li>$this->pagedim[$this->page]['wk'] = page width in user units</li><li>$this->pagedim[$this->page]['hk'] = page height in user units</li><li>$this->pagedim[$this->page]['tm'] = top margin</li><li>$this->pagedim[$this->page]['bm'] = bottom margin</li><li>$this->pagedim[$this->page]['lm'] = left margin</li><li>$this->pagedim[$this->page]['rm'] = right margin</li><li>$this->pagedim[$this->page]['pb'] = auto page break</li><li>$this->pagedim[$this->page]['or'] = page orientation</li><li>$this->pagedim[$this->page]['olm'] = original left margin</li><li>$this->pagedim[$this->page]['orm'] = original right margin</li><li>$this->pagedim[$this->page]['Rotate'] = The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li><li>$this->pagedim[$this->page]['PZ'] = The page's preferred zoom (magnification) factor.</li><li>$this->pagedim[$this->page]['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation<ul><li>$this->pagedim[$this->page]['trans']['Dur'] = The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li><li>$this->pagedim[$this->page]['trans']['S'] = transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li><li>$this->pagedim[$this->page]['trans']['D'] = The duration of the transition effect, in seconds.</li><li>$this->pagedim[$this->page]['trans']['Dm'] = (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li><li>$this->pagedim[$this->page]['trans']['M'] = (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li><li>$this->pagedim[$this->page]['trans']['Di'] = (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li><li>$this->pagedim[$this->page]['trans']['SS'] = (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0. </li><li>$this->pagedim[$this->page]['trans']['B'] = (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li></ul></li><li>$this->pagedim[$this->page]['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed<ul><li>$this->pagedim[$this->page]['MediaBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['CropBox'] : the visible region of default user space<ul><li>$this->pagedim[$this->page]['CropBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment<ul><li>$this->pagedim[$this->page]['BleedBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['TrimBox'] : the intended dimensions of the finished page after trimming<ul><li>$this->pagedim[$this->page]['TrimBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['ArtBox'] : the extent of the page's meaningful content<ul><li>$this->pagedim[$this->page]['ArtBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['ury'] = upper-right y coordinate in points</li></ul></li></ul>
  2829. * @param int $pagenum page number (empty = current page)
  2830. * @return array of page dimensions.
  2831. * @author Nicola Asuni
  2832. * @access public
  2833. * @since 4.5.027 (2009-03-16)
  2834. */
  2835. public function getPageDimensions($pagenum='') {
  2836. if (empty($pagenum)) {
  2837. $pagenum = $this->page;
  2838. }
  2839. return $this->pagedim[$pagenum];
  2840. }
  2841. /**
  2842. * Returns the page width in units.
  2843. * @param int $pagenum page number (empty = current page)
  2844. * @return int page width.
  2845. * @author Nicola Asuni
  2846. * @access public
  2847. * @since 1.5.2
  2848. * @see getPageDimensions()
  2849. */
  2850. public function getPageWidth($pagenum='') {
  2851. if (empty($pagenum)) {
  2852. return $this->w;
  2853. }
  2854. return $this->pagedim[$pagenum]['w'];
  2855. }
  2856. /**
  2857. * Returns the page height in units.
  2858. * @param int $pagenum page number (empty = current page)
  2859. * @return int page height.
  2860. * @author Nicola Asuni
  2861. * @access public
  2862. * @since 1.5.2
  2863. * @see getPageDimensions()
  2864. */
  2865. public function getPageHeight($pagenum='') {
  2866. if (empty($pagenum)) {
  2867. return $this->h;
  2868. }
  2869. return $this->pagedim[$pagenum]['h'];
  2870. }
  2871. /**
  2872. * Returns the page break margin.
  2873. * @param int $pagenum page number (empty = current page)
  2874. * @return int page break margin.
  2875. * @author Nicola Asuni
  2876. * @access public
  2877. * @since 1.5.2
  2878. * @see getPageDimensions()
  2879. */
  2880. public function getBreakMargin($pagenum='') {
  2881. if (empty($pagenum)) {
  2882. return $this->bMargin;
  2883. }
  2884. return $this->pagedim[$pagenum]['bm'];
  2885. }
  2886. /**
  2887. * Returns the scale factor (number of points in user unit).
  2888. * @return int scale factor.
  2889. * @author Nicola Asuni
  2890. * @access public
  2891. * @since 1.5.2
  2892. */
  2893. public function getScaleFactor() {
  2894. return $this->k;
  2895. }
  2896. /**
  2897. * Defines the left, top and right margins.
  2898. * @param float $left Left margin.
  2899. * @param float $top Top margin.
  2900. * @param float $right Right margin. Default value is the left one.
  2901. * @param boolean $keepmargins if true overwrites the default page margins
  2902. * @access public
  2903. * @since 1.0
  2904. * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
  2905. */
  2906. public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
  2907. //Set left, top and right margins
  2908. $this->lMargin = $left;
  2909. $this->tMargin = $top;
  2910. if ($right == -1) {
  2911. $right = $left;
  2912. }
  2913. $this->rMargin = $right;
  2914. if ($keepmargins) {
  2915. // overwrite original values
  2916. $this->original_lMargin = $this->lMargin;
  2917. $this->original_rMargin = $this->rMargin;
  2918. }
  2919. }
  2920. /**
  2921. * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
  2922. * @param float $margin The margin.
  2923. * @access public
  2924. * @since 1.4
  2925. * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
  2926. */
  2927. public function SetLeftMargin($margin) {
  2928. //Set left margin
  2929. $this->lMargin = $margin;
  2930. if (($this->page > 0) AND ($this->x < $margin)) {
  2931. $this->x = $margin;
  2932. }
  2933. }
  2934. /**
  2935. * Defines the top margin. The method can be called before creating the first page.
  2936. * @param float $margin The margin.
  2937. * @access public
  2938. * @since 1.5
  2939. * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
  2940. */
  2941. public function SetTopMargin($margin) {
  2942. //Set top margin
  2943. $this->tMargin = $margin;
  2944. if (($this->page > 0) AND ($this->y < $margin)) {
  2945. $this->y = $margin;
  2946. }
  2947. }
  2948. /**
  2949. * Defines the right margin. The method can be called before creating the first page.
  2950. * @param float $margin The margin.
  2951. * @access public
  2952. * @since 1.5
  2953. * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
  2954. */
  2955. public function SetRightMargin($margin) {
  2956. $this->rMargin = $margin;
  2957. if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
  2958. $this->x = $this->w - $margin;
  2959. }
  2960. }
  2961. /**
  2962. * Set the same internal Cell padding for top, right, bottom, left-
  2963. * @param float $pad internal padding.
  2964. * @access public
  2965. * @since 2.1.000 (2008-01-09)
  2966. * @see getCellPaddings(), setCellPaddings()
  2967. */
  2968. public function SetCellPadding($pad) {
  2969. if ($pad >= 0) {
  2970. $this->cell_padding['L'] = $pad;
  2971. $this->cell_padding['T'] = $pad;
  2972. $this->cell_padding['R'] = $pad;
  2973. $this->cell_padding['B'] = $pad;
  2974. }
  2975. }
  2976. /**
  2977. * Set the internal Cell paddings.
  2978. * @param float $left left padding
  2979. * @param float $top top padding
  2980. * @param float $right right padding
  2981. * @param float $bottom bottom padding
  2982. * @access public
  2983. * @since 5.9.000 (2010-10-03)
  2984. * @see getCellPaddings(), SetCellPadding()
  2985. */
  2986. public function setCellPaddings($left='', $top='', $right='', $bottom='') {
  2987. if (($left !== '') AND ($left >= 0)) {
  2988. $this->cell_padding['L'] = $left;
  2989. }
  2990. if (($top !== '') AND ($top >= 0)) {
  2991. $this->cell_padding['T'] = $top;
  2992. }
  2993. if (($right !== '') AND ($right >= 0)) {
  2994. $this->cell_padding['R'] = $right;
  2995. }
  2996. if (($bottom !== '') AND ($bottom >= 0)) {
  2997. $this->cell_padding['B'] = $bottom;
  2998. }
  2999. }
  3000. /**
  3001. * Get the internal Cell padding array.
  3002. * @return array of padding values
  3003. * @access public
  3004. * @since 5.9.000 (2010-10-03)
  3005. * @see setCellPaddings(), SetCellPadding()
  3006. */
  3007. public function getCellPaddings() {
  3008. return $this->cell_padding;
  3009. }
  3010. /**
  3011. * Set the internal Cell margins.
  3012. * @param float $left left margin
  3013. * @param float $top top margin
  3014. * @param float $right right margin
  3015. * @param float $bottom bottom margin
  3016. * @access public
  3017. * @since 5.9.000 (2010-10-03)
  3018. * @see getCellMargins()
  3019. */
  3020. public function setCellMargins($left='', $top='', $right='', $bottom='') {
  3021. if (($left !== '') AND ($left >= 0)) {
  3022. $this->cell_margin['L'] = $left;
  3023. }
  3024. if (($top !== '') AND ($top >= 0)) {
  3025. $this->cell_margin['T'] = $top;
  3026. }
  3027. if (($right !== '') AND ($right >= 0)) {
  3028. $this->cell_margin['R'] = $right;
  3029. }
  3030. if (($bottom !== '') AND ($bottom >= 0)) {
  3031. $this->cell_margin['B'] = $bottom;
  3032. }
  3033. }
  3034. /**
  3035. * Get the internal Cell margin array.
  3036. * @return array of margin values
  3037. * @access public
  3038. * @since 5.9.000 (2010-10-03)
  3039. * @see setCellMargins()
  3040. */
  3041. public function getCellMargins() {
  3042. return $this->cell_margin;
  3043. }
  3044. /**
  3045. * Adjust the internal Cell padding array to take account of the line width.
  3046. * @param mixed $brd Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  3047. * @return array of adjustments
  3048. * @access public
  3049. * @since 5.9.000 (2010-10-03)
  3050. */
  3051. protected function adjustCellPadding($brd=0) {
  3052. if (empty($brd)) {
  3053. return;
  3054. }
  3055. if (is_string($brd)) {
  3056. // convert string to array
  3057. $slen = strlen($brd);
  3058. $newbrd = array();
  3059. for ($i = 0; $i < $slen; ++$i) {
  3060. $newbrd[$brd{$i}] = true;
  3061. }
  3062. $brd = $newbrd;
  3063. } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
  3064. $brd = array('LRTB' => true);
  3065. }
  3066. if (!is_array($brd)) {
  3067. return;
  3068. }
  3069. // store current cell padding
  3070. $cp = $this->cell_padding;
  3071. // select border mode
  3072. if (isset($brd['mode'])) {
  3073. $mode = $brd['mode'];
  3074. unset($brd['mode']);
  3075. } else {
  3076. $mode = 'normal';
  3077. }
  3078. // process borders
  3079. foreach ($brd as $border => $style) {
  3080. $line_width = $this->LineWidth;
  3081. if (is_array($style) AND isset($style['width'])) {
  3082. // get border width
  3083. $line_width = $style['width'];
  3084. }
  3085. $adj = 0; // line width inside the cell
  3086. switch ($mode) {
  3087. case 'ext': {
  3088. $adj = 0;
  3089. break;
  3090. }
  3091. case 'int': {
  3092. $adj = $line_width;
  3093. break;
  3094. }
  3095. case 'normal':
  3096. default: {
  3097. $adj = ($line_width / 2);
  3098. break;
  3099. }
  3100. }
  3101. // correct internal cell padding if required to avoid overlap between text and lines
  3102. if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
  3103. $this->cell_padding['T'] = $adj;
  3104. }
  3105. if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
  3106. $this->cell_padding['R'] = $adj;
  3107. }
  3108. if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
  3109. $this->cell_padding['B'] = $adj;
  3110. }
  3111. if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
  3112. $this->cell_padding['L'] = $adj;
  3113. }
  3114. }
  3115. return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L']));
  3116. }
  3117. /**
  3118. * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
  3119. * @param boolean $auto Boolean indicating if mode should be on or off.
  3120. * @param float $margin Distance from the bottom of the page.
  3121. * @access public
  3122. * @since 1.0
  3123. * @see Cell(), MultiCell(), AcceptPageBreak()
  3124. */
  3125. public function SetAutoPageBreak($auto, $margin=0) {
  3126. //Set auto page break mode and triggering margin
  3127. $this->AutoPageBreak = $auto;
  3128. $this->bMargin = $margin;
  3129. $this->PageBreakTrigger = $this->h - $margin;
  3130. }
  3131. /**
  3132. * Defines the way the document is to be displayed by the viewer.
  3133. * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
  3134. * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
  3135. * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
  3136. * @access public
  3137. * @since 1.2
  3138. */
  3139. public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
  3140. //Set display mode in viewer
  3141. if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
  3142. $this->ZoomMode = $zoom;
  3143. } else {
  3144. $this->Error('Incorrect zoom display mode: '.$zoom);
  3145. }
  3146. switch ($layout) {
  3147. case 'default':
  3148. case 'single':
  3149. case 'SinglePage': {
  3150. $this->LayoutMode = 'SinglePage';
  3151. break;
  3152. }
  3153. case 'continuous':
  3154. case 'OneColumn': {
  3155. $this->LayoutMode = 'OneColumn';
  3156. break;
  3157. }
  3158. case 'two':
  3159. case 'TwoColumnLeft': {
  3160. $this->LayoutMode = 'TwoColumnLeft';
  3161. break;
  3162. }
  3163. case 'TwoColumnRight': {
  3164. $this->LayoutMode = 'TwoColumnRight';
  3165. break;
  3166. }
  3167. case 'TwoPageLeft': {
  3168. $this->LayoutMode = 'TwoPageLeft';
  3169. break;
  3170. }
  3171. case 'TwoPageRight': {
  3172. $this->LayoutMode = 'TwoPageRight';
  3173. break;
  3174. }
  3175. default: {
  3176. $this->LayoutMode = 'SinglePage';
  3177. }
  3178. }
  3179. switch ($mode) {
  3180. case 'UseNone': {
  3181. $this->PageMode = 'UseNone';
  3182. break;
  3183. }
  3184. case 'UseOutlines': {
  3185. $this->PageMode = 'UseOutlines';
  3186. break;
  3187. }
  3188. case 'UseThumbs': {
  3189. $this->PageMode = 'UseThumbs';
  3190. break;
  3191. }
  3192. case 'FullScreen': {
  3193. $this->PageMode = 'FullScreen';
  3194. break;
  3195. }
  3196. case 'UseOC': {
  3197. $this->PageMode = 'UseOC';
  3198. break;
  3199. }
  3200. case '': {
  3201. $this->PageMode = 'UseAttachments';
  3202. break;
  3203. }
  3204. default: {
  3205. $this->PageMode = 'UseNone';
  3206. }
  3207. }
  3208. }
  3209. /**
  3210. * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
  3211. * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
  3212. * @param boolean $compress Boolean indicating if compression must be enabled.
  3213. * @access public
  3214. * @since 1.4
  3215. */
  3216. public function SetCompression($compress) {
  3217. //Set page compression
  3218. if (function_exists('gzcompress')) {
  3219. $this->compress = $compress ? true : false;
  3220. } else {
  3221. $this->compress = false;
  3222. }
  3223. }
  3224. /**
  3225. * Defines the title of the document.
  3226. * @param string $title The title.
  3227. * @access public
  3228. * @since 1.2
  3229. * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
  3230. */
  3231. public function SetTitle($title) {
  3232. //Title of document
  3233. $this->title = $title;
  3234. }
  3235. /**
  3236. * Defines the subject of the document.
  3237. * @param string $subject The subject.
  3238. * @access public
  3239. * @since 1.2
  3240. * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
  3241. */
  3242. public function SetSubject($subject) {
  3243. //Subject of document
  3244. $this->subject = $subject;
  3245. }
  3246. /**
  3247. * Defines the author of the document.
  3248. * @param string $author The name of the author.
  3249. * @access public
  3250. * @since 1.2
  3251. * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
  3252. */
  3253. public function SetAuthor($author) {
  3254. //Author of document
  3255. $this->author = $author;
  3256. }
  3257. /**
  3258. * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
  3259. * @param string $keywords The list of keywords.
  3260. * @access public
  3261. * @since 1.2
  3262. * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
  3263. */
  3264. public function SetKeywords($keywords) {
  3265. //Keywords of document
  3266. $this->keywords = $keywords;
  3267. }
  3268. /**
  3269. * Defines the creator of the document. This is typically the name of the application that generates the PDF.
  3270. * @param string $creator The name of the creator.
  3271. * @access public
  3272. * @since 1.2
  3273. * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
  3274. */
  3275. public function SetCreator($creator) {
  3276. //Creator of document
  3277. $this->creator = $creator;
  3278. }
  3279. /**
  3280. * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.
  3281. * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
  3282. * @param string $msg The error message
  3283. * @access public
  3284. * @since 1.0
  3285. */
  3286. public function Error($msg) {
  3287. // unset all class variables
  3288. $this->_destroy(true);
  3289. // exit program and print error
  3290. die('<strong>TCPDF ERROR: </strong>'.$msg);
  3291. }
  3292. /**
  3293. * This method begins the generation of the PDF document.
  3294. * It is not necessary to call it explicitly because AddPage() does it automatically.
  3295. * Note: no page is created by this method
  3296. * @access public
  3297. * @since 1.0
  3298. * @see AddPage(), Close()
  3299. */
  3300. public function Open() {
  3301. //Begin document
  3302. $this->state = 1;
  3303. }
  3304. /**
  3305. * Terminates the PDF document.
  3306. * It is not necessary to call this method explicitly because Output() does it automatically.
  3307. * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
  3308. * @access public
  3309. * @since 1.0
  3310. * @see Open(), Output()
  3311. */
  3312. public function Close() {
  3313. if ($this->state == 3) {
  3314. return;
  3315. }
  3316. if ($this->page == 0) {
  3317. $this->AddPage();
  3318. }
  3319. // save current graphic settings
  3320. $gvars = $this->getGraphicVars();
  3321. $this->lastpage(true);
  3322. $this->SetAutoPageBreak(false);
  3323. $this->x = 0;
  3324. $this->y = $this->h - (1 / $this->k);
  3325. $this->lMargin = 0;
  3326. $this->_out('q');
  3327. $this->setVisibility('screen');
  3328. $this->SetFont('helvetica', '', 1);
  3329. $this->SetTextColor(255, 255, 255);
  3330. $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
  3331. $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
  3332. $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
  3333. $this->setVisibility('all');
  3334. $this->_out('Q');
  3335. // restore graphic settings
  3336. $this->setGraphicVars($gvars);
  3337. // close page
  3338. $this->endPage();
  3339. // close document
  3340. $this->_enddoc();
  3341. // unset all class variables (except critical ones)
  3342. $this->_destroy(false);
  3343. }
  3344. /**
  3345. * Move pointer at the specified document page and update page dimensions.
  3346. * @param int $pnum page number (1 ... numpages)
  3347. * @param boolean $resetmargins if true reset left, right, top margins and Y position.
  3348. * @access public
  3349. * @since 2.1.000 (2008-01-07)
  3350. * @see getPage(), lastpage(), getNumPages()
  3351. */
  3352. public function setPage($pnum, $resetmargins=false) {
  3353. if (($pnum == $this->page) AND ($this->state == 2)) {
  3354. return;
  3355. }
  3356. if (($pnum > 0) AND ($pnum <= $this->numpages)) {
  3357. $this->state = 2;
  3358. // save current graphic settings
  3359. //$gvars = $this->getGraphicVars();
  3360. $oldpage = $this->page;
  3361. $this->page = $pnum;
  3362. $this->wPt = $this->pagedim[$this->page]['w'];
  3363. $this->hPt = $this->pagedim[$this->page]['h'];
  3364. $this->w = $this->pagedim[$this->page]['wk'];
  3365. $this->h = $this->pagedim[$this->page]['hk'];
  3366. $this->tMargin = $this->pagedim[$this->page]['tm'];
  3367. $this->bMargin = $this->pagedim[$this->page]['bm'];
  3368. $this->original_lMargin = $this->pagedim[$this->page]['olm'];
  3369. $this->original_rMargin = $this->pagedim[$this->page]['orm'];
  3370. $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
  3371. $this->CurOrientation = $this->pagedim[$this->page]['or'];
  3372. $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
  3373. // restore graphic settings
  3374. //$this->setGraphicVars($gvars);
  3375. if ($resetmargins) {
  3376. $this->lMargin = $this->pagedim[$this->page]['olm'];
  3377. $this->rMargin = $this->pagedim[$this->page]['orm'];
  3378. $this->SetY($this->tMargin);
  3379. } else {
  3380. // account for booklet mode
  3381. if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
  3382. $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
  3383. $this->lMargin += $deltam;
  3384. $this->rMargin -= $deltam;
  3385. }
  3386. }
  3387. } else {
  3388. $this->Error('Wrong page number on setPage() function: '.$pnum);
  3389. }
  3390. }
  3391. /**
  3392. * Reset pointer to the last document page.
  3393. * @param boolean $resetmargins if true reset left, right, top margins and Y position.
  3394. * @access public
  3395. * @since 2.0.000 (2008-01-04)
  3396. * @see setPage(), getPage(), getNumPages()
  3397. */
  3398. public function lastPage($resetmargins=false) {
  3399. $this->setPage($this->getNumPages(), $resetmargins);
  3400. }
  3401. /**
  3402. * Get current document page number.
  3403. * @return int page number
  3404. * @access public
  3405. * @since 2.1.000 (2008-01-07)
  3406. * @see setPage(), lastpage(), getNumPages()
  3407. */
  3408. public function getPage() {
  3409. return $this->page;
  3410. }
  3411. /**
  3412. * Get the total number of insered pages.
  3413. * @return int number of pages
  3414. * @access public
  3415. * @since 2.1.000 (2008-01-07)
  3416. * @see setPage(), getPage(), lastpage()
  3417. */
  3418. public function getNumPages() {
  3419. return $this->numpages;
  3420. }
  3421. /**
  3422. * Adds a new TOC (Table Of Content) page to the document.
  3423. * @param string $orientation page orientation.
  3424. * @param boolean $keepmargins if true overwrites the default page margins with the current margins
  3425. * @access public
  3426. * @since 5.0.001 (2010-05-06)
  3427. * @see AddPage(), startPage(), endPage(), endTOCPage()
  3428. */
  3429. public function addTOCPage($orientation='', $format='', $keepmargins=false) {
  3430. $this->AddPage($orientation, $format, $keepmargins, true);
  3431. }
  3432. /**
  3433. * Terminate the current TOC (Table Of Content) page
  3434. * @access public
  3435. * @since 5.0.001 (2010-05-06)
  3436. * @see AddPage(), startPage(), endPage(), addTOCPage()
  3437. */
  3438. public function endTOCPage() {
  3439. $this->endPage(true);
  3440. }
  3441. /**
  3442. * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
  3443. * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
  3444. * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
  3445. * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
  3446. * @param boolean $keepmargins if true overwrites the default page margins with the current margins
  3447. * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table Of Content).
  3448. * @access public
  3449. * @since 1.0
  3450. * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
  3451. */
  3452. public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
  3453. if ($this->inxobj) {
  3454. // we are inside an XObject template
  3455. return;
  3456. }
  3457. if (!isset($this->original_lMargin) OR $keepmargins) {
  3458. $this->original_lMargin = $this->lMargin;
  3459. }
  3460. if (!isset($this->original_rMargin) OR $keepmargins) {
  3461. $this->original_rMargin = $this->rMargin;
  3462. }
  3463. // terminate previous page
  3464. $this->endPage();
  3465. // start new page
  3466. $this->startPage($orientation, $format, $tocpage);
  3467. }
  3468. /**
  3469. * Terminate the current page
  3470. * @param boolean $tocpage if true set the tocpage state to false (end the page used to display Table Of Content).
  3471. * @access public
  3472. * @since 4.2.010 (2008-11-14)
  3473. * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
  3474. */
  3475. public function endPage($tocpage=false) {
  3476. // check if page is already closed
  3477. if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
  3478. return;
  3479. }
  3480. $this->InFooter = true;
  3481. // print page footer
  3482. $this->setFooter();
  3483. // close page
  3484. $this->_endpage();
  3485. // mark page as closed
  3486. $this->pageopen[$this->page] = false;
  3487. $this->InFooter = false;
  3488. if ($tocpage) {
  3489. $this->tocpage = false;
  3490. }
  3491. }
  3492. /**
  3493. * Starts a new page to the document. The page must be closed using the endPage() function.
  3494. * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
  3495. * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
  3496. * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
  3497. * @access public
  3498. * @since 4.2.010 (2008-11-14)
  3499. * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
  3500. */
  3501. public function startPage($orientation='', $format='', $tocpage=false) {
  3502. if ($tocpage) {
  3503. $this->tocpage = true;
  3504. }
  3505. if ($this->numpages > $this->page) {
  3506. // this page has been already added
  3507. $this->setPage($this->page + 1);
  3508. $this->SetY($this->tMargin);
  3509. return;
  3510. }
  3511. // start a new page
  3512. if ($this->state == 0) {
  3513. $this->Open();
  3514. }
  3515. ++$this->numpages;
  3516. $this->swapMargins($this->booklet);
  3517. // save current graphic settings
  3518. $gvars = $this->getGraphicVars();
  3519. // start new page
  3520. $this->_beginpage($orientation, $format);
  3521. // mark page as open
  3522. $this->pageopen[$this->page] = true;
  3523. // restore graphic settings
  3524. $this->setGraphicVars($gvars);
  3525. // mark this point
  3526. $this->setPageMark();
  3527. // print page header
  3528. $this->setHeader();
  3529. // restore graphic settings
  3530. $this->setGraphicVars($gvars);
  3531. // mark this point
  3532. $this->setPageMark();
  3533. // print table header (if any)
  3534. $this->setTableHeader();
  3535. // set mark for empty page check
  3536. $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
  3537. }
  3538. /**
  3539. * Set start-writing mark on current page stream used to put borders and fills.
  3540. * Borders and fills are always created after content and inserted on the position marked by this method.
  3541. * This function must be called after calling Image() function for a background image.
  3542. * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
  3543. * @access public
  3544. * @since 4.0.016 (2008-07-30)
  3545. */
  3546. public function setPageMark() {
  3547. $this->intmrk[$this->page] = $this->pagelen[$this->page];
  3548. $this->bordermrk[$this->page] = $this->intmrk[$this->page];
  3549. $this->setContentMark();
  3550. }
  3551. /**
  3552. * Set start-writing mark on selected page.
  3553. * Borders and fills are always created after content and inserted on the position marked by this method.
  3554. * @param int $page page number (default is the current page)
  3555. * @access protected
  3556. * @since 4.6.021 (2009-07-20)
  3557. */
  3558. protected function setContentMark($page=0) {
  3559. if ($page <= 0) {
  3560. $page = $this->page;
  3561. }
  3562. if (isset($this->footerlen[$page])) {
  3563. $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
  3564. } else {
  3565. $this->cntmrk[$page] = $this->pagelen[$page];
  3566. }
  3567. }
  3568. /**
  3569. * Set header data.
  3570. * @param string $ln header image logo
  3571. * @param string $lw header image logo width in mm
  3572. * @param string $ht string to print as title on document header
  3573. * @param string $hs string to print on document header
  3574. * @access public
  3575. */
  3576. public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
  3577. $this->header_logo = $ln;
  3578. $this->header_logo_width = $lw;
  3579. $this->header_title = $ht;
  3580. $this->header_string = $hs;
  3581. }
  3582. /**
  3583. * Returns header data:
  3584. * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
  3585. * @return array()
  3586. * @access public
  3587. * @since 4.0.012 (2008-07-24)
  3588. */
  3589. public function getHeaderData() {
  3590. $ret = array();
  3591. $ret['logo'] = $this->header_logo;
  3592. $ret['logo_width'] = $this->header_logo_width;
  3593. $ret['title'] = $this->header_title;
  3594. $ret['string'] = $this->header_string;
  3595. return $ret;
  3596. }
  3597. /**
  3598. * Set header margin.
  3599. * (minimum distance between header and top page margin)
  3600. * @param int $hm distance in user units
  3601. * @access public
  3602. */
  3603. public function setHeaderMargin($hm=10) {
  3604. $this->header_margin = $hm;
  3605. }
  3606. /**
  3607. * Returns header margin in user units.
  3608. * @return float
  3609. * @since 4.0.012 (2008-07-24)
  3610. * @access public
  3611. */
  3612. public function getHeaderMargin() {
  3613. return $this->header_margin;
  3614. }
  3615. /**
  3616. * Set footer margin.
  3617. * (minimum distance between footer and bottom page margin)
  3618. * @param int $fm distance in user units
  3619. * @access public
  3620. */
  3621. public function setFooterMargin($fm=10) {
  3622. $this->footer_margin = $fm;
  3623. }
  3624. /**
  3625. * Returns footer margin in user units.
  3626. * @return float
  3627. * @since 4.0.012 (2008-07-24)
  3628. * @access public
  3629. */
  3630. public function getFooterMargin() {
  3631. return $this->footer_margin;
  3632. }
  3633. /**
  3634. * Set a flag to print page header.
  3635. * @param boolean $val set to true to print the page header (default), false otherwise.
  3636. * @access public
  3637. */
  3638. public function setPrintHeader($val=true) {
  3639. $this->print_header = $val;
  3640. }
  3641. /**
  3642. * Set a flag to print page footer.
  3643. * @param boolean $value set to true to print the page footer (default), false otherwise.
  3644. * @access public
  3645. */
  3646. public function setPrintFooter($val=true) {
  3647. $this->print_footer = $val;
  3648. }
  3649. /**
  3650. * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
  3651. * @return float
  3652. * @access public
  3653. */
  3654. public function getImageRBX() {
  3655. return $this->img_rb_x;
  3656. }
  3657. /**
  3658. * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
  3659. * @return float
  3660. * @access public
  3661. */
  3662. public function getImageRBY() {
  3663. return $this->img_rb_y;
  3664. }
  3665. /**
  3666. * This method is used to render the page header.
  3667. * It is automatically called by AddPage() and could be overwritten in your own inherited class.
  3668. * @access public
  3669. */
  3670. public function Header() {
  3671. $ormargins = $this->getOriginalMargins();
  3672. $headerfont = $this->getHeaderFont();
  3673. $headerdata = $this->getHeaderData();
  3674. if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
  3675. $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
  3676. $imgy = $this->getImageRBY();
  3677. } else {
  3678. $imgy = $this->GetY();
  3679. }
  3680. $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
  3681. // set starting margin for text data cell
  3682. if ($this->getRTL()) {
  3683. $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
  3684. } else {
  3685. $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
  3686. }
  3687. $this->SetTextColor(0, 0, 0);
  3688. // header title
  3689. $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
  3690. $this->SetX($header_x);
  3691. $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
  3692. // header string
  3693. $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
  3694. $this->SetX($header_x);
  3695. $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
  3696. // print an ending header line
  3697. $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
  3698. $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
  3699. if ($this->getRTL()) {
  3700. $this->SetX($ormargins['right']);
  3701. } else {
  3702. $this->SetX($ormargins['left']);
  3703. }
  3704. $this->Cell(0, 0, '', 'T', 0, 'C');
  3705. }
  3706. /**
  3707. * This method is used to render the page footer.
  3708. * It is automatically called by AddPage() and could be overwritten in your own inherited class.
  3709. * @access public
  3710. */
  3711. public function Footer() {
  3712. $cur_y = $this->GetY();
  3713. $ormargins = $this->getOriginalMargins();
  3714. $this->SetTextColor(0, 0, 0);
  3715. //set style for cell border
  3716. $line_width = 0.85 / $this->getScaleFactor();
  3717. $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
  3718. //print document barcode
  3719. $barcode = $this->getBarcode();
  3720. if (!empty($barcode)) {
  3721. $this->Ln($line_width);
  3722. $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right']) / 3);
  3723. $style = array(
  3724. 'position' => $this->rtl?'R':'L',
  3725. 'align' => $this->rtl?'R':'L',
  3726. 'stretch' => false,
  3727. 'fitwidth' => true,
  3728. 'cellfitalign' => '',
  3729. 'border' => false,
  3730. 'padding' => 0,
  3731. 'fgcolor' => array(0,0,0),
  3732. 'bgcolor' => false,
  3733. 'text' => false
  3734. );
  3735. $this->write1DBarcode($barcode, 'C128B', '', $cur_y + $line_width, '', (($this->getFooterMargin() / 3) - $line_width), 0.3, $style, '');
  3736. }
  3737. if (empty($this->pagegroups)) {
  3738. $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
  3739. } else {
  3740. $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
  3741. }
  3742. $this->SetY($cur_y);
  3743. //Print page number
  3744. if ($this->getRTL()) {
  3745. $this->SetX($ormargins['right']);
  3746. $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
  3747. } else {
  3748. $this->SetX($ormargins['left']);
  3749. $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
  3750. }
  3751. }
  3752. /**
  3753. * This method is used to render the page header.
  3754. * @access protected
  3755. * @since 4.0.012 (2008-07-24)
  3756. */
  3757. protected function setHeader() {
  3758. if ($this->print_header) {
  3759. $this->setGraphicVars($this->default_graphic_vars);
  3760. $temp_thead = $this->thead;
  3761. $temp_theadMargins = $this->theadMargins;
  3762. $lasth = $this->lasth;
  3763. $this->_out('q');
  3764. $this->rMargin = $this->original_rMargin;
  3765. $this->lMargin = $this->original_lMargin;
  3766. $this->SetCellPadding(0);
  3767. //set current position
  3768. if ($this->rtl) {
  3769. $this->SetXY($this->original_rMargin, $this->header_margin);
  3770. } else {
  3771. $this->SetXY($this->original_lMargin, $this->header_margin);
  3772. }
  3773. $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
  3774. $this->Header();
  3775. //restore position
  3776. if ($this->rtl) {
  3777. $this->SetXY($this->original_rMargin, $this->tMargin);
  3778. } else {
  3779. $this->SetXY($this->original_lMargin, $this->tMargin);
  3780. }
  3781. $this->_out('Q');
  3782. $this->lasth = $lasth;
  3783. $this->thead = $temp_thead;
  3784. $this->theadMargins = $temp_theadMargins;
  3785. $this->newline = false;
  3786. }
  3787. }
  3788. /**
  3789. * This method is used to render the page footer.
  3790. * @access protected
  3791. * @since 4.0.012 (2008-07-24)
  3792. */
  3793. protected function setFooter() {
  3794. //Page footer
  3795. // save current graphic settings
  3796. $gvars = $this->getGraphicVars();
  3797. // mark this point
  3798. $this->footerpos[$this->page] = $this->pagelen[$this->page];
  3799. $this->_out("\n");
  3800. if ($this->print_footer) {
  3801. $this->setGraphicVars($this->default_graphic_vars);
  3802. $this->current_column = 0;
  3803. $this->num_columns = 1;
  3804. $temp_thead = $this->thead;
  3805. $temp_theadMargins = $this->theadMargins;
  3806. $lasth = $this->lasth;
  3807. $this->_out('q');
  3808. $this->rMargin = $this->original_rMargin;
  3809. $this->lMargin = $this->original_lMargin;
  3810. $this->SetCellPadding(0);
  3811. //set current position
  3812. $footer_y = $this->h - $this->footer_margin;
  3813. if ($this->rtl) {
  3814. $this->SetXY($this->original_rMargin, $footer_y);
  3815. } else {
  3816. $this->SetXY($this->original_lMargin, $footer_y);
  3817. }
  3818. $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
  3819. $this->Footer();
  3820. //restore position
  3821. if ($this->rtl) {
  3822. $this->SetXY($this->original_rMargin, $this->tMargin);
  3823. } else {
  3824. $this->SetXY($this->original_lMargin, $this->tMargin);
  3825. }
  3826. $this->_out('Q');
  3827. $this->lasth = $lasth;
  3828. $this->thead = $temp_thead;
  3829. $this->theadMargins = $temp_theadMargins;
  3830. }
  3831. // restore graphic settings
  3832. $this->setGraphicVars($gvars);
  3833. $this->current_column = $gvars['current_column'];
  3834. $this->num_columns = $gvars['num_columns'];
  3835. // calculate footer length
  3836. $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
  3837. }
  3838. /**
  3839. * This method is used to render the table header on new page (if any).
  3840. * @access protected
  3841. * @since 4.5.030 (2009-03-25)
  3842. */
  3843. protected function setTableHeader() {
  3844. if ($this->num_columns > 1) {
  3845. // multi column mode
  3846. return;
  3847. }
  3848. if (isset($this->theadMargins['top'])) {
  3849. // restore the original top-margin
  3850. $this->tMargin = $this->theadMargins['top'];
  3851. $this->pagedim[$this->page]['tm'] = $this->tMargin;
  3852. $this->y = $this->tMargin;
  3853. }
  3854. if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
  3855. // set margins
  3856. $prev_lMargin = $this->lMargin;
  3857. $prev_rMargin = $this->rMargin;
  3858. $prev_cell_padding = $this->cell_padding;
  3859. $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
  3860. $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
  3861. $this->cell_padding = $this->theadMargins['cell_padding'];
  3862. if ($this->rtl) {
  3863. $this->x = $this->w - $this->rMargin;
  3864. } else {
  3865. $this->x = $this->lMargin;
  3866. }
  3867. // print table header
  3868. $this->writeHTML($this->thead, false, false, false, false, '');
  3869. // set new top margin to skip the table headers
  3870. if (!isset($this->theadMargins['top'])) {
  3871. $this->theadMargins['top'] = $this->tMargin;
  3872. }
  3873. $this->tMargin = $this->y;
  3874. $this->pagedim[$this->page]['tm'] = $this->tMargin;
  3875. $this->lasth = 0;
  3876. $this->lMargin = $prev_lMargin;
  3877. $this->rMargin = $prev_rMargin;
  3878. $this->cell_padding = $prev_cell_padding;
  3879. }
  3880. }
  3881. /**
  3882. * Returns the current page number.
  3883. * @return int page number
  3884. * @access public
  3885. * @since 1.0
  3886. * @see AliasNbPages(), getAliasNbPages()
  3887. */
  3888. public function PageNo() {
  3889. return $this->page;
  3890. }
  3891. /**
  3892. * Defines a new spot color.
  3893. * It can be expressed in RGB components or gray scale.
  3894. * The method can be called before the first page is created and the value is retained from page to page.
  3895. * @param int $c Cyan color for CMYK. Value between 0 and 255
  3896. * @param int $m Magenta color for CMYK. Value between 0 and 255
  3897. * @param int $y Yellow color for CMYK. Value between 0 and 255
  3898. * @param int $k Key (Black) color for CMYK. Value between 0 and 255
  3899. * @access public
  3900. * @since 4.0.024 (2008-09-12)
  3901. * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
  3902. */
  3903. public function AddSpotColor($name, $c, $m, $y, $k) {
  3904. if (!isset($this->spot_colors[$name])) {
  3905. $i = 1 + count($this->spot_colors);
  3906. $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
  3907. }
  3908. }
  3909. /**
  3910. * Defines the color used for all drawing operations (lines, rectangles and cell borders).
  3911. * It can be expressed in RGB components or gray scale.
  3912. * The method can be called before the first page is created and the value is retained from page to page.
  3913. * @param array $color array of colors
  3914. * @param boolean $ret if true do not send the command.
  3915. * @return string the PDF command
  3916. * @access public
  3917. * @since 3.1.000 (2008-06-11)
  3918. * @see SetDrawColor()
  3919. */
  3920. public function SetDrawColorArray($color, $ret=false) {
  3921. if (is_array($color)) {
  3922. $color = array_values($color);
  3923. $r = isset($color[0]) ? $color[0] : -1;
  3924. $g = isset($color[1]) ? $color[1] : -1;
  3925. $b = isset($color[2]) ? $color[2] : -1;
  3926. $k = isset($color[3]) ? $color[3] : -1;
  3927. if ($r >= 0) {
  3928. return $this->SetDrawColor($r, $g, $b, $k, $ret);
  3929. }
  3930. }
  3931. return '';
  3932. }
  3933. /**
  3934. * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
  3935. * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
  3936. * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
  3937. * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
  3938. * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
  3939. * @param boolean $ret if true do not send the command.
  3940. * @return string the PDF command
  3941. * @access public
  3942. * @since 1.3
  3943. * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
  3944. */
  3945. public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false) {
  3946. // set default values
  3947. if (!is_numeric($col1)) {
  3948. $col1 = 0;
  3949. }
  3950. if (!is_numeric($col2)) {
  3951. $col2 = -1;
  3952. }
  3953. if (!is_numeric($col3)) {
  3954. $col3 = -1;
  3955. }
  3956. if (!is_numeric($col4)) {
  3957. $col4 = -1;
  3958. }
  3959. //Set color for all stroking operations
  3960. if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
  3961. // Grey scale
  3962. $this->DrawColor = sprintf('%.3F G', $col1/255);
  3963. $this->strokecolor = array('G' => $col1);
  3964. } elseif ($col4 == -1) {
  3965. // RGB
  3966. $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
  3967. $this->strokecolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
  3968. } else {
  3969. // CMYK
  3970. $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
  3971. $this->strokecolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
  3972. }
  3973. if ($this->page > 0) {
  3974. if (!$ret) {
  3975. $this->_out($this->DrawColor);
  3976. }
  3977. return $this->DrawColor;
  3978. }
  3979. return '';
  3980. }
  3981. /**
  3982. * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
  3983. * @param string $name name of the spot color
  3984. * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
  3985. * @access public
  3986. * @since 4.0.024 (2008-09-12)
  3987. * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
  3988. */
  3989. public function SetDrawSpotColor($name, $tint=100) {
  3990. if (!isset($this->spot_colors[$name])) {
  3991. $this->Error('Undefined spot color: '.$name);
  3992. }
  3993. $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
  3994. if ($this->page > 0) {
  3995. $this->_out($this->DrawColor);
  3996. }
  3997. }
  3998. /**
  3999. * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
  4000. * It can be expressed in RGB components or gray scale.
  4001. * The method can be called before the first page is created and the value is retained from page to page.
  4002. * @param array $color array of colors
  4003. * @access public
  4004. * @since 3.1.000 (2008-6-11)
  4005. * @see SetFillColor()
  4006. */
  4007. public function SetFillColorArray($color) {
  4008. if (is_array($color)) {
  4009. $color = array_values($color);
  4010. $r = isset($color[0]) ? $color[0] : -1;
  4011. $g = isset($color[1]) ? $color[1] : -1;
  4012. $b = isset($color[2]) ? $color[2] : -1;
  4013. $k = isset($color[3]) ? $color[3] : -1;
  4014. if ($r >= 0) {
  4015. $this->SetFillColor($r, $g, $b, $k);
  4016. }
  4017. }
  4018. }
  4019. /**
  4020. * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
  4021. * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
  4022. * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
  4023. * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
  4024. * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
  4025. * @access public
  4026. * @since 1.3
  4027. * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
  4028. */
  4029. public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
  4030. // set default values
  4031. if (!is_numeric($col1)) {
  4032. $col1 = 0;
  4033. }
  4034. if (!is_numeric($col2)) {
  4035. $col2 = -1;
  4036. }
  4037. if (!is_numeric($col3)) {
  4038. $col3 = -1;
  4039. }
  4040. if (!is_numeric($col4)) {
  4041. $col4 = -1;
  4042. }
  4043. //Set color for all filling operations
  4044. if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
  4045. // Grey scale
  4046. $this->FillColor = sprintf('%.3F g', $col1/255);
  4047. $this->bgcolor = array('G' => $col1);
  4048. } elseif ($col4 == -1) {
  4049. // RGB
  4050. $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
  4051. $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
  4052. } else {
  4053. // CMYK
  4054. $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
  4055. $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
  4056. }
  4057. $this->ColorFlag = ($this->FillColor != $this->TextColor);
  4058. if ($this->page > 0) {
  4059. $this->_out($this->FillColor);
  4060. }
  4061. }
  4062. /**
  4063. * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
  4064. * @param string $name name of the spot color
  4065. * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
  4066. * @access public
  4067. * @since 4.0.024 (2008-09-12)
  4068. * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
  4069. */
  4070. public function SetFillSpotColor($name, $tint=100) {
  4071. if (!isset($this->spot_colors[$name])) {
  4072. $this->Error('Undefined spot color: '.$name);
  4073. }
  4074. $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
  4075. $this->ColorFlag = ($this->FillColor != $this->TextColor);
  4076. if ($this->page > 0) {
  4077. $this->_out($this->FillColor);
  4078. }
  4079. }
  4080. /**
  4081. * Defines the color used for text. It can be expressed in RGB components or gray scale.
  4082. * The method can be called before the first page is created and the value is retained from page to page.
  4083. * @param array $color array of colors
  4084. * @access public
  4085. * @since 3.1.000 (2008-6-11)
  4086. * @see SetFillColor()
  4087. */
  4088. public function SetTextColorArray($color) {
  4089. if (is_array($color)) {
  4090. $color = array_values($color);
  4091. $r = isset($color[0]) ? $color[0] : -1;
  4092. $g = isset($color[1]) ? $color[1] : -1;
  4093. $b = isset($color[2]) ? $color[2] : -1;
  4094. $k = isset($color[3]) ? $color[3] : -1;
  4095. if ($r >= 0) {
  4096. $this->SetTextColor($r, $g, $b, $k);
  4097. }
  4098. }
  4099. }
  4100. /**
  4101. * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
  4102. * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
  4103. * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
  4104. * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
  4105. * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
  4106. * @access public
  4107. * @since 1.3
  4108. * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
  4109. */
  4110. public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
  4111. // set default values
  4112. if (!is_numeric($col1)) {
  4113. $col1 = 0;
  4114. }
  4115. if (!is_numeric($col2)) {
  4116. $col2 = -1;
  4117. }
  4118. if (!is_numeric($col3)) {
  4119. $col3 = -1;
  4120. }
  4121. if (!is_numeric($col4)) {
  4122. $col4 = -1;
  4123. }
  4124. //Set color for text
  4125. if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
  4126. // Grey scale
  4127. $this->TextColor = sprintf('%.3F g', $col1/255);
  4128. $this->fgcolor = array('G' => $col1);
  4129. } elseif ($col4 == -1) {
  4130. // RGB
  4131. $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
  4132. $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
  4133. } else {
  4134. // CMYK
  4135. $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
  4136. $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
  4137. }
  4138. $this->ColorFlag = ($this->FillColor != $this->TextColor);
  4139. }
  4140. /**
  4141. * Defines the spot color used for text.
  4142. * @param string $name name of the spot color
  4143. * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
  4144. * @access public
  4145. * @since 4.0.024 (2008-09-12)
  4146. * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
  4147. */
  4148. public function SetTextSpotColor($name, $tint=100) {
  4149. if (!isset($this->spot_colors[$name])) {
  4150. $this->Error('Undefined spot color: '.$name);
  4151. }
  4152. $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
  4153. $this->ColorFlag = ($this->FillColor != $this->TextColor);
  4154. if ($this->page > 0) {
  4155. $this->_out($this->TextColor);
  4156. }
  4157. }
  4158. /**
  4159. * Returns the length of a string in user unit. A font must be selected.<br>
  4160. * @param string $s The string whose length is to be computed
  4161. * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
  4162. * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-trough</li><li>O: overline</li></ul> or any combination. The default value is regular.
  4163. * @param float $fontsize Font size in points. The default value is the current size.
  4164. * @param boolean $getarray if true returns an array of characters widths, if false returns the total length.
  4165. * @return mixed int total string length or array of characted widths
  4166. * @author Nicola Asuni
  4167. * @access public
  4168. * @since 1.2
  4169. */
  4170. public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
  4171. return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray);
  4172. }
  4173. /**
  4174. * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
  4175. * @param string $sa The array of chars whose total length is to be computed
  4176. * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
  4177. * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li><li>O: overline</li></ul> or any combination. The default value is regular.
  4178. * @param float $fontsize Font size in points. The default value is the current size.
  4179. * @param boolean $getarray if true returns an array of characters widths, if false returns the total length.
  4180. * @return mixed int total string length or array of characted widths
  4181. * @author Nicola Asuni
  4182. * @access public
  4183. * @since 2.4.000 (2008-03-06)
  4184. */
  4185. public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
  4186. // store current values
  4187. if (!$this->empty_string($fontname)) {
  4188. $prev_FontFamily = $this->FontFamily;
  4189. $prev_FontStyle = $this->FontStyle;
  4190. $prev_FontSizePt = $this->FontSizePt;
  4191. $this->SetFont($fontname, $fontstyle, $fontsize);
  4192. }
  4193. // convert UTF-8 array to Latin1 if required
  4194. $sa = $this->UTF8ArrToLatin1($sa);
  4195. $w = 0; // total width
  4196. $wa = array(); // array of characters widths
  4197. foreach ($sa as $ck => $char) {
  4198. // character width
  4199. $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
  4200. $wa[] = $cw;
  4201. $w += $cw;
  4202. }
  4203. // restore previous values
  4204. if (!$this->empty_string($fontname)) {
  4205. $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
  4206. }
  4207. if ($getarray) {
  4208. return $wa;
  4209. }
  4210. return $w;
  4211. }
  4212. /**
  4213. * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking/kerning).
  4214. * @param int $char The char code whose length is to be returned
  4215. * @param boolean $notlast set to false for the latest character on string, true otherwise (default)
  4216. * @return float char width
  4217. * @author Nicola Asuni
  4218. * @access public
  4219. * @since 2.4.000 (2008-03-06)
  4220. */
  4221. public function GetCharWidth($char, $notlast=true) {
  4222. // get raw width
  4223. $chw = $this->getRawCharWidth($char);
  4224. if (($this->font_spacing != 0) AND $notlast) {
  4225. // increase/decrease font spacing
  4226. $chw += $this->font_spacing;
  4227. }
  4228. if ($this->font_stretching != 100) {
  4229. // fixed stretching mode
  4230. $chw *= ($this->font_stretching / 100);
  4231. }
  4232. return $chw;
  4233. }
  4234. /**
  4235. * Returns the length of the char in user unit for the current font.
  4236. * @param int $char The char code whose length is to be returned
  4237. * @return float char width
  4238. * @author Nicola Asuni
  4239. * @access public
  4240. * @since 5.9.000 (2010-09-28)
  4241. */
  4242. public function getRawCharWidth($char) {
  4243. if ($char == 173) {
  4244. // SHY character will not be printed
  4245. return (0);
  4246. }
  4247. $cw = &$this->CurrentFont['cw'];
  4248. if (isset($cw[$char])) {
  4249. $w = $cw[$char];
  4250. } elseif (isset($this->CurrentFont['dw'])) {
  4251. // default width
  4252. $w = $this->CurrentFont['dw'];
  4253. } elseif (isset($cw[32])) {
  4254. // default width
  4255. $w = $cw[32];
  4256. } else {
  4257. $w = 600;
  4258. }
  4259. return ($w * $this->FontSize / 1000);
  4260. }
  4261. /**
  4262. * Returns the numbero of characters in a string.
  4263. * @param string $s The input string.
  4264. * @return int number of characters
  4265. * @access public
  4266. * @since 2.0.0001 (2008-01-07)
  4267. */
  4268. public function GetNumChars($s) {
  4269. if ($this->isUnicodeFont()) {
  4270. return count($this->UTF8StringToArray($s));
  4271. }
  4272. return strlen($s);
  4273. }
  4274. /**
  4275. * Fill the list of available fonts ($this->fontlist).
  4276. * @access protected
  4277. * @since 4.0.013 (2008-07-28)
  4278. */
  4279. protected function getFontsList() {
  4280. $fontsdir = opendir($this->_getfontpath());
  4281. while (($file = readdir($fontsdir)) !== false) {
  4282. if (substr($file, -4) == '.php') {
  4283. array_push($this->fontlist, strtolower(basename($file, '.php')));
  4284. }
  4285. }
  4286. closedir($fontsdir);
  4287. }
  4288. /**
  4289. * Imports a TrueType, Type1, core, or CID0 font and makes it available.
  4290. * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
  4291. * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
  4292. * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
  4293. * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
  4294. * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
  4295. * @return array containing the font data, or false in case of error.
  4296. * @param mixed $subset if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
  4297. * @access public
  4298. * @since 1.5
  4299. * @see SetFont(), setFontSubsetting()
  4300. */
  4301. public function AddFont($family, $style='', $fontfile='', $subset='default') {
  4302. if ($subset === 'default') {
  4303. $subset = $this->font_subsetting;
  4304. }
  4305. if ($this->empty_string($family)) {
  4306. if (!$this->empty_string($this->FontFamily)) {
  4307. $family = $this->FontFamily;
  4308. } else {
  4309. $this->Error('Empty font family');
  4310. }
  4311. }
  4312. // move embedded styles on $style
  4313. if (substr($family, -1) == 'I') {
  4314. $style .= 'I';
  4315. $family = substr($family, 0, -1);
  4316. }
  4317. if (substr($family, -1) == 'B') {
  4318. $style .= 'B';
  4319. $family = substr($family, 0, -1);
  4320. }
  4321. // normalize family name
  4322. $family = strtolower($family);
  4323. if ((!$this->isunicode) AND ($family == 'arial')) {
  4324. $family = 'helvetica';
  4325. }
  4326. if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
  4327. $style = '';
  4328. }
  4329. $tempstyle = strtoupper($style);
  4330. $style = '';
  4331. // underline
  4332. if (strpos($tempstyle, 'U') !== false) {
  4333. $this->underline = true;
  4334. } else {
  4335. $this->underline = false;
  4336. }
  4337. // line-through (deleted)
  4338. if (strpos($tempstyle, 'D') !== false) {
  4339. $this->linethrough = true;
  4340. } else {
  4341. $this->linethrough = false;
  4342. }
  4343. // overline
  4344. if (strpos($tempstyle, 'O') !== false) {
  4345. $this->overline = true;
  4346. } else {
  4347. $this->overline = false;
  4348. }
  4349. // bold
  4350. if (strpos($tempstyle, 'B') !== false) {
  4351. $style .= 'B';
  4352. }
  4353. // oblique
  4354. if (strpos($tempstyle, 'I') !== false) {
  4355. $style .= 'I';
  4356. }
  4357. $bistyle = $style;
  4358. $fontkey = $family.$style;
  4359. $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
  4360. $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
  4361. // check if the font has been already added
  4362. $fb = $this->getFontBuffer($fontkey);
  4363. if ($fb !== false) {
  4364. if ($this->inxobj) {
  4365. // we are inside an XObject template
  4366. $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
  4367. }
  4368. return $fontdata;
  4369. }
  4370. if (isset($type)) {
  4371. unset($type);
  4372. }
  4373. if (isset($cw)) {
  4374. unset($cw);
  4375. }
  4376. // get specified font directory (if any)
  4377. $fontdir = false;
  4378. if (!$this->empty_string($fontfile)) {
  4379. $fontdir = dirname($fontfile);
  4380. if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
  4381. $fontdir = '';
  4382. } else {
  4383. $fontdir .= '/';
  4384. }
  4385. }
  4386. // search and include font file
  4387. if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
  4388. // build a standard filenames for specified font
  4389. $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
  4390. $fontfile2 = str_replace(' ', '', $family).'.php';
  4391. // search files on various directories
  4392. if (($fontdir !== false) AND file_exists($fontdir.$fontfile1)) {
  4393. $fontfile = $fontdir.$fontfile1;
  4394. } elseif (file_exists($this->_getfontpath().$fontfile1)) {
  4395. $fontfile = $this->_getfontpath().$fontfile1;
  4396. } elseif (file_exists($fontfile1)) {
  4397. $fontfile = $fontfile1;
  4398. } elseif (($fontdir !== false) AND file_exists($fontdir.$fontfile2)) {
  4399. $fontfile = $fontdir.$fontfile2;
  4400. } elseif (file_exists($this->_getfontpath().$fontfile2)) {
  4401. $fontfile = $this->_getfontpath().$fontfile2;
  4402. } else {
  4403. $fontfile = $fontfile2;
  4404. }
  4405. }
  4406. // include font file
  4407. if (file_exists($fontfile)) {
  4408. include($fontfile);
  4409. } else {
  4410. $this->Error('Could not include font definition file: '.$family.'');
  4411. }
  4412. // check font parameters
  4413. if ((!isset($type)) OR (!isset($cw))) {
  4414. $this->Error('The font definition file has a bad format: '.$fontfile.'');
  4415. }
  4416. // SET default parameters
  4417. if (!isset($file) OR $this->empty_string($file)) {
  4418. $file = '';
  4419. }
  4420. if (!isset($enc) OR $this->empty_string($enc)) {
  4421. $enc = '';
  4422. }
  4423. if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
  4424. $cidinfo = array('Registry'=>'Adobe','Ordering'=>'Identity','Supplement'=>0);
  4425. $cidinfo['uni2cid'] = array();
  4426. }
  4427. if (!isset($ctg) OR $this->empty_string($ctg)) {
  4428. $ctg = '';
  4429. }
  4430. if (!isset($desc) OR $this->empty_string($desc)) {
  4431. $desc = array();
  4432. }
  4433. if (!isset($up) OR $this->empty_string($up)) {
  4434. $up = -100;
  4435. }
  4436. if (!isset($ut) OR $this->empty_string($ut)) {
  4437. $ut = 50;
  4438. }
  4439. if (!isset($cw) OR $this->empty_string($cw)) {
  4440. $cw = array();
  4441. }
  4442. if (!isset($dw) OR $this->empty_string($dw)) {
  4443. // set default width
  4444. if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
  4445. $dw = $desc['MissingWidth'];
  4446. } elseif (isset($cw[32])) {
  4447. $dw = $cw[32];
  4448. } else {
  4449. $dw = 600;
  4450. }
  4451. }
  4452. ++$this->numfonts;
  4453. if ($type == 'cidfont0') {
  4454. // register CID font (all styles at once)
  4455. $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
  4456. $sname = $name.$styles[$bistyle];
  4457. // artificial bold
  4458. if (strpos($bistyle, 'B') !== false) {
  4459. if (isset($desc['StemV'])) {
  4460. $desc['StemV'] *= 2;
  4461. } else {
  4462. $desc['StemV'] = 120;
  4463. }
  4464. }
  4465. // artificial italic
  4466. if (strpos($bistyle, 'I') !== false) {
  4467. if (isset($desc['ItalicAngle'])) {
  4468. $desc['ItalicAngle'] -= 11;
  4469. } else {
  4470. $desc['ItalicAngle'] = -11;
  4471. }
  4472. }
  4473. } elseif ($type == 'core') {
  4474. $name = $this->CoreFonts[$fontkey];
  4475. $subset = false;
  4476. } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
  4477. $subset = false;
  4478. } elseif ($type == 'TrueTypeUnicode') {
  4479. $enc = 'Identity-H';
  4480. } else {
  4481. $this->Error('Unknow font type: '.$type.'');
  4482. }
  4483. // initialize subsetchars to contain default ASCII values (0-255)
  4484. $subsetchars = array_fill(0, 256, true);
  4485. $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
  4486. if ($this->inxobj) {
  4487. // we are inside an XObject template
  4488. $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
  4489. }
  4490. if (isset($diff) AND (!empty($diff))) {
  4491. //Search existing encodings
  4492. $d = 0;
  4493. $nb = count($this->diffs);
  4494. for ($i=1; $i <= $nb; ++$i) {
  4495. if ($this->diffs[$i] == $diff) {
  4496. $d = $i;
  4497. break;
  4498. }
  4499. }
  4500. if ($d == 0) {
  4501. $d = $nb + 1;
  4502. $this->diffs[$d] = $diff;
  4503. }
  4504. $this->setFontSubBuffer($fontkey, 'diff', $d);
  4505. }
  4506. if (!$this->empty_string($file)) {
  4507. if (!isset($this->FontFiles[$file])) {
  4508. if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
  4509. $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
  4510. } elseif ($type != 'core') {
  4511. $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
  4512. }
  4513. } else {
  4514. // update fontkeys that are sharing this font file
  4515. $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
  4516. if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
  4517. $this->FontFiles[$file]['fontkeys'][] = $fontkey;
  4518. }
  4519. }
  4520. }
  4521. return $fontdata;
  4522. }
  4523. /**
  4524. * Sets the font used to print character strings.
  4525. * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
  4526. * The method can be called before the first page is created and the font is retained from page to page.
  4527. * If you just wish to change the current font size, it is simpler to call SetFontSize().
  4528. * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
  4529. * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
  4530. * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
  4531. * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
  4532. * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
  4533. * @param mixed $subset if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
  4534. * @author Nicola Asuni
  4535. * @access public
  4536. * @since 1.0
  4537. * @see AddFont(), SetFontSize()
  4538. */
  4539. public function SetFont($family, $style='', $size=0, $fontfile='', $subset='default') {
  4540. //Select a font; size given in points
  4541. if ($size == 0) {
  4542. $size = $this->FontSizePt;
  4543. }
  4544. // try to add font (if not already added)
  4545. $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
  4546. $this->FontFamily = $fontdata['family'];
  4547. $this->FontStyle = $fontdata['style'];
  4548. $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
  4549. $this->SetFontSize($size);
  4550. }
  4551. /**
  4552. * Defines the size of the current font.
  4553. * @param float $size The size (in points)
  4554. * @param boolean $out if true output the font size command, otherwise only set the font properties.
  4555. * @access public
  4556. * @since 1.0
  4557. * @see SetFont()
  4558. */
  4559. public function SetFontSize($size, $out=true) {
  4560. // font size in points
  4561. $this->FontSizePt = $size;
  4562. // font size in user units
  4563. $this->FontSize = $size / $this->k;
  4564. // calculate some font metrics
  4565. if (isset($this->CurrentFont['desc']['FontBBox'])) {
  4566. $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
  4567. $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
  4568. } else {
  4569. $font_height = $size * 1.219;
  4570. }
  4571. if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
  4572. $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
  4573. }
  4574. if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
  4575. $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
  4576. }
  4577. if (!isset($font_ascent) AND !isset($font_descent)) {
  4578. // core font
  4579. $font_ascent = 0.76 * $font_height;
  4580. $font_descent = $font_height - $font_ascent;
  4581. } elseif (!isset($font_descent)) {
  4582. $font_descent = $font_height - $font_ascent;
  4583. } elseif (!isset($font_ascent)) {
  4584. $font_ascent = $font_height - $font_descent;
  4585. }
  4586. $this->FontAscent = $font_ascent / $this->k;
  4587. $this->FontDescent = $font_descent / $this->k;
  4588. if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i']))) {
  4589. $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  4590. }
  4591. }
  4592. /**
  4593. * Return the font descent value
  4594. * @param string $font font name
  4595. * @param string $style font style
  4596. * @param float $size The size (in points)
  4597. * @return int font descent
  4598. * @access public
  4599. * @author Nicola Asuni
  4600. * @since 4.9.003 (2010-03-30)
  4601. */
  4602. public function getFontDescent($font, $style='', $size=0) {
  4603. $fontdata = $this->AddFont($font, $style);
  4604. $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
  4605. if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
  4606. $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
  4607. } else {
  4608. $descent = 1.219 * 0.24 * $size;
  4609. }
  4610. return ($descent / $this->k);
  4611. }
  4612. /**
  4613. * Return the font ascent value
  4614. * @param string $font font name
  4615. * @param string $style font style
  4616. * @param float $size The size (in points)
  4617. * @return int font ascent
  4618. * @access public
  4619. * @author Nicola Asuni
  4620. * @since 4.9.003 (2010-03-30)
  4621. */
  4622. public function getFontAscent($font, $style='', $size=0) {
  4623. $fontdata = $this->AddFont($font, $style);
  4624. $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
  4625. if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
  4626. $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
  4627. } else {
  4628. $ascent = 1.219 * 0.76 * $size;
  4629. }
  4630. return ($ascent / $this->k);
  4631. }
  4632. /**
  4633. * Defines the default monospaced font.
  4634. * @param string $font Font name.
  4635. * @access public
  4636. * @since 4.5.025
  4637. */
  4638. public function SetDefaultMonospacedFont($font) {
  4639. $this->default_monospaced_font = $font;
  4640. }
  4641. /**
  4642. * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
  4643. * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
  4644. * @access public
  4645. * @since 1.5
  4646. * @see Cell(), Write(), Image(), Link(), SetLink()
  4647. */
  4648. public function AddLink() {
  4649. //Create a new internal link
  4650. $n = count($this->links) + 1;
  4651. $this->links[$n] = array(0, 0);
  4652. return $n;
  4653. }
  4654. /**
  4655. * Defines the page and position a link points to.
  4656. * @param int $link The link identifier returned by AddLink()
  4657. * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
  4658. * @param int $page Number of target page; -1 indicates the current page. This is the default value
  4659. * @access public
  4660. * @since 1.5
  4661. * @see AddLink()
  4662. */
  4663. public function SetLink($link, $y=0, $page=-1) {
  4664. if ($y == -1) {
  4665. $y = $this->y;
  4666. }
  4667. if ($page == -1) {
  4668. $page = $this->page;
  4669. }
  4670. $this->links[$link] = array($page, $y);
  4671. }
  4672. /**
  4673. * Puts a link on a rectangular area of the page.
  4674. * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
  4675. * @param float $x Abscissa of the upper-left corner of the rectangle
  4676. * @param float $y Ordinate of the upper-left corner of the rectangle
  4677. * @param float $w Width of the rectangle
  4678. * @param float $h Height of the rectangle
  4679. * @param mixed $link URL or identifier returned by AddLink()
  4680. * @param int $spaces number of spaces on the text to link
  4681. * @access public
  4682. * @since 1.5
  4683. * @see AddLink(), Annotation(), Cell(), Write(), Image()
  4684. */
  4685. public function Link($x, $y, $w, $h, $link, $spaces=0) {
  4686. $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
  4687. }
  4688. /**
  4689. * Puts a markup annotation on a rectangular area of the page.
  4690. * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
  4691. * @param float $x Abscissa of the upper-left corner of the rectangle
  4692. * @param float $y Ordinate of the upper-left corner of the rectangle
  4693. * @param float $w Width of the rectangle
  4694. * @param float $h Height of the rectangle
  4695. * @param string $text annotation text or alternate content
  4696. * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
  4697. * @param int $spaces number of spaces on the text to link
  4698. * @access public
  4699. * @since 4.0.018 (2008-08-06)
  4700. */
  4701. public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
  4702. if ($this->inxobj) {
  4703. // store parameters for later use on template
  4704. $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
  4705. return;
  4706. }
  4707. if ($x === '') {
  4708. $x = $this->x;
  4709. }
  4710. if ($y === '') {
  4711. $y = $this->y;
  4712. }
  4713. // check page for no-write regions and adapt page margins if necessary
  4714. $this->checkPageRegions($h, $x, $y);
  4715. // recalculate coordinates to account for graphic transformations
  4716. if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
  4717. for ($i=$this->transfmatrix_key; $i > 0; --$i) {
  4718. $maxid = count($this->transfmatrix[$i]) - 1;
  4719. for ($j=$maxid; $j >= 0; --$j) {
  4720. $ctm = $this->transfmatrix[$i][$j];
  4721. if (isset($ctm['a'])) {
  4722. $x = $x * $this->k;
  4723. $y = ($this->h - $y) * $this->k;
  4724. $w = $w * $this->k;
  4725. $h = $h * $this->k;
  4726. // top left
  4727. $xt = $x;
  4728. $yt = $y;
  4729. $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
  4730. $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
  4731. // top right
  4732. $xt = $x + $w;
  4733. $yt = $y;
  4734. $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
  4735. $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
  4736. // bottom left
  4737. $xt = $x;
  4738. $yt = $y - $h;
  4739. $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
  4740. $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
  4741. // bottom right
  4742. $xt = $x + $w;
  4743. $yt = $y - $h;
  4744. $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
  4745. $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
  4746. // new coordinates (rectangle area)
  4747. $x = min($x1, $x2, $x3, $x4);
  4748. $y = max($y1, $y2, $y3, $y4);
  4749. $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
  4750. $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
  4751. $x = $x / $this->k;
  4752. $y = $this->h - ($y / $this->k);
  4753. }
  4754. }
  4755. }
  4756. }
  4757. if ($this->page <= 0) {
  4758. $page = 1;
  4759. } else {
  4760. $page = $this->page;
  4761. }
  4762. if (!isset($this->PageAnnots[$page])) {
  4763. $this->PageAnnots[$page] = array();
  4764. }
  4765. ++$this->n;
  4766. $this->PageAnnots[$page][] = array('n' => $this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
  4767. if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
  4768. ++$this->n;
  4769. $this->embeddedfiles[basename($opt['FS'])] = array('n' => $this->n, 'file' => $opt['FS']);
  4770. }
  4771. // Add widgets annotation's icons
  4772. if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
  4773. $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
  4774. }
  4775. if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
  4776. $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
  4777. }
  4778. if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
  4779. $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
  4780. }
  4781. }
  4782. /**
  4783. * Embedd the attached files.
  4784. * @since 4.4.000 (2008-12-07)
  4785. * @access protected
  4786. * @see Annotation()
  4787. */
  4788. protected function _putEmbeddedFiles() {
  4789. reset($this->embeddedfiles);
  4790. foreach ($this->embeddedfiles as $filename => $filedata) {
  4791. $data = file_get_contents($filedata['file']);
  4792. $filter = '';
  4793. if ($this->compress) {
  4794. $data = gzcompress($data);
  4795. $filter = ' /Filter /FlateDecode';
  4796. }
  4797. $stream = $this->_getrawstream($data, $filedata['n']);
  4798. $out = $this->_getobj($filedata['n'])."\n";
  4799. $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' >>';
  4800. $out .= ' stream'."\n".$stream."\n".'endstream';
  4801. $out .= "\n".'endobj';
  4802. $this->_out($out);
  4803. }
  4804. }
  4805. /**
  4806. * Prints a text cell at the specified position.
  4807. * The origin is on the left of the first charcter, on the baseline.
  4808. * This method allows to place a string precisely on the page.
  4809. * @param float $x Abscissa of the cell origin
  4810. * @param float $y Ordinate of the cell origin
  4811. * @param string $txt String to print
  4812. * @param int $fstroke outline size in user units (false = disable)
  4813. * @param boolean $fclip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
  4814. * @param boolean $ffill if true fills the text
  4815. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  4816. * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
  4817. * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
  4818. * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
  4819. * @param mixed $link URL or identifier returned by AddLink().
  4820. * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
  4821. * @param boolean $ignore_min_height if true ignore automatic minimum height value.
  4822. * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul>
  4823. * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
  4824. * @param boolean $rtloff if true uses the page top-left corner as origin of axis for $x and $y initial position.
  4825. * @access public
  4826. * @since 1.0
  4827. * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
  4828. */
  4829. public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
  4830. $textrendermode = $this->textrendermode;
  4831. $textstrokewidth = $this->textstrokewidth;
  4832. $this->setTextRenderingMode($fstroke, $ffill, $fclip);
  4833. $this->SetXY($x, $y, $rtloff);
  4834. $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
  4835. // restore previous rendering mode
  4836. $this->textrendermode = $textrendermode;
  4837. $this->textstrokewidth = $textstrokewidth;
  4838. }
  4839. /**
  4840. * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
  4841. * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
  4842. * This method is called automatically and should not be called directly by the application.
  4843. * @return boolean
  4844. * @access public
  4845. * @since 1.4
  4846. * @see SetAutoPageBreak()
  4847. */
  4848. public function AcceptPageBreak() {
  4849. if ($this->num_columns > 1) {
  4850. // multi column mode
  4851. if($this->current_column < ($this->num_columns - 1)) {
  4852. // go to next column
  4853. $this->selectColumn($this->current_column + 1);
  4854. } else {
  4855. // add a new page
  4856. $this->AddPage();
  4857. // set first column
  4858. $this->selectColumn(0);
  4859. }
  4860. // avoid page breaking from checkPageBreak()
  4861. return false;
  4862. }
  4863. return $this->AutoPageBreak;
  4864. }
  4865. /**
  4866. * Add page if needed.
  4867. * @param float $h Cell height. Default value: 0.
  4868. * @param mixed $y starting y position, leave empty for current position.
  4869. * @param boolean $addpage if true add a page, otherwise only return the true/false state
  4870. * @return boolean true in case of page break, false otherwise.
  4871. * @since 3.2.000 (2008-07-01)
  4872. * @access protected
  4873. */
  4874. protected function checkPageBreak($h=0, $y='', $addpage=true) {
  4875. if ($this->empty_string($y)) {
  4876. $y = $this->y;
  4877. }
  4878. $current_page = $this->page;
  4879. if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
  4880. if ($addpage) {
  4881. //Automatic page break
  4882. $x = $this->x;
  4883. $this->AddPage($this->CurOrientation);
  4884. $this->y = $this->tMargin;
  4885. $oldpage = $this->page - 1;
  4886. if ($this->rtl) {
  4887. if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
  4888. $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
  4889. } else {
  4890. $this->x = $x;
  4891. }
  4892. } else {
  4893. if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
  4894. $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
  4895. } else {
  4896. $this->x = $x;
  4897. }
  4898. }
  4899. }
  4900. $this->newline = true;
  4901. return true;
  4902. }
  4903. if ($current_page != $this->page) {
  4904. // account for columns mode
  4905. $this->newline = true;
  4906. return true;
  4907. }
  4908. return false;
  4909. }
  4910. /**
  4911. * Removes SHY characters from text.
  4912. * Unicode Data:<ul>
  4913. * <li>Name : SOFT HYPHEN, commonly abbreviated as SHY</li>
  4914. * <li>HTML Entity (decimal): &amp;#173;</li>
  4915. * <li>HTML Entity (hex): &amp;#xad;</li>
  4916. * <li>HTML Entity (named): &amp;shy;</li>
  4917. * <li>How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]</li>
  4918. * <li>UTF-8 (hex): 0xC2 0xAD (c2ad)</li>
  4919. * <li>UTF-8 character: chr(194).chr(173)</li>
  4920. * </ul>
  4921. * @param string $txt input string
  4922. * @return string without SHY characters.
  4923. * @access public
  4924. * @since (4.5.019) 2009-02-28
  4925. */
  4926. public function removeSHY($txt='') {
  4927. $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
  4928. if (!$this->isunicode) {
  4929. $txt = preg_replace('/([\\xad]{1})/', '', $txt);
  4930. }
  4931. return $txt;
  4932. }
  4933. /**
  4934. * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
  4935. * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
  4936. * @param float $w Cell width. If 0, the cell extends up to the right margin.
  4937. * @param float $h Cell height. Default value: 0.
  4938. * @param string $txt String to print. Default value: empty string.
  4939. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  4940. * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
  4941. * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
  4942. * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
  4943. * @param mixed $link URL or identifier returned by AddLink().
  4944. * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
  4945. * @param boolean $ignore_min_height if true ignore automatic minimum height value.
  4946. * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
  4947. * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
  4948. * @access public
  4949. * @since 1.0
  4950. * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
  4951. */
  4952. public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
  4953. $prev_cell_margin = $this->cell_margin;
  4954. $prev_cell_padding = $this->cell_padding;
  4955. $this->adjustCellPadding($border);
  4956. if (!$ignore_min_height) {
  4957. $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
  4958. if ($h < $min_cell_height) {
  4959. $h = $min_cell_height;
  4960. }
  4961. }
  4962. $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
  4963. $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
  4964. $this->cell_padding = $prev_cell_padding;
  4965. $this->cell_margin = $prev_cell_margin;
  4966. }
  4967. /**
  4968. * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
  4969. * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
  4970. * @param float $w Cell width. If 0, the cell extends up to the right margin.
  4971. * @param float $h Cell height. Default value: 0.
  4972. * @param string $txt String to print. Default value: empty string.
  4973. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  4974. * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
  4975. * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
  4976. * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
  4977. * @param mixed $link URL or identifier returned by AddLink().
  4978. * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
  4979. * @param boolean $ignore_min_height if true ignore automatic minimum height value.
  4980. * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
  4981. * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul>
  4982. * @return string containing cell code
  4983. * @access protected
  4984. * @since 1.0
  4985. * @see Cell()
  4986. */
  4987. protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
  4988. $prev_cell_margin = $this->cell_margin;
  4989. $prev_cell_padding = $this->cell_padding;
  4990. $txt = $this->removeSHY($txt);
  4991. $rs = ''; //string to be returned
  4992. $this->adjustCellPadding($border);
  4993. if (!$ignore_min_height) {
  4994. $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
  4995. if ($h < $min_cell_height) {
  4996. $h = $min_cell_height;
  4997. }
  4998. }
  4999. // check page for no-write regions and adapt page margins if necessary
  5000. $this->checkPageRegions($h);
  5001. $k = $this->k;
  5002. if ($this->rtl) {
  5003. $x = $this->x - $this->cell_margin['R'];
  5004. } else {
  5005. $x = $this->x + $this->cell_margin['L'];
  5006. }
  5007. $y = $this->y + $this->cell_margin['T'];
  5008. $prev_font_stretching = $this->font_stretching;
  5009. $prev_font_spacing = $this->font_spacing;
  5010. // cell vertical alignment
  5011. switch ($calign) {
  5012. case 'A': {
  5013. // font top
  5014. switch ($valign) {
  5015. case 'T': {
  5016. // top
  5017. $y -= $this->cell_padding['T'];
  5018. break;
  5019. }
  5020. case 'B': {
  5021. // bottom
  5022. $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
  5023. break;
  5024. }
  5025. default:
  5026. case 'C':
  5027. case 'M': {
  5028. // center
  5029. $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
  5030. break;
  5031. }
  5032. }
  5033. break;
  5034. }
  5035. case 'L': {
  5036. // font baseline
  5037. switch ($valign) {
  5038. case 'T': {
  5039. // top
  5040. $y -= ($this->cell_padding['T'] + $this->FontAscent);
  5041. break;
  5042. }
  5043. case 'B': {
  5044. // bottom
  5045. $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
  5046. break;
  5047. }
  5048. default:
  5049. case 'C':
  5050. case 'M': {
  5051. // center
  5052. $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
  5053. break;
  5054. }
  5055. }
  5056. break;
  5057. }
  5058. case 'D': {
  5059. // font bottom
  5060. switch ($valign) {
  5061. case 'T': {
  5062. // top
  5063. $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
  5064. break;
  5065. }
  5066. case 'B': {
  5067. // bottom
  5068. $y -= ($h - $this->cell_padding['B']);
  5069. break;
  5070. }
  5071. default:
  5072. case 'C':
  5073. case 'M': {
  5074. // center
  5075. $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
  5076. break;
  5077. }
  5078. }
  5079. break;
  5080. }
  5081. case 'B': {
  5082. // cell bottom
  5083. $y -= $h;
  5084. break;
  5085. }
  5086. case 'C':
  5087. case 'M': {
  5088. // cell center
  5089. $y -= ($h / 2);
  5090. break;
  5091. }
  5092. default:
  5093. case 'T': {
  5094. // cell top
  5095. break;
  5096. }
  5097. }
  5098. // text vertical alignment
  5099. switch ($valign) {
  5100. case 'T': {
  5101. // top
  5102. $yt = $y + $this->cell_padding['T'];
  5103. break;
  5104. }
  5105. case 'B': {
  5106. // bottom
  5107. $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
  5108. break;
  5109. }
  5110. default:
  5111. case 'C':
  5112. case 'M': {
  5113. // center
  5114. $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
  5115. break;
  5116. }
  5117. }
  5118. $basefonty = $yt + $this->FontAscent;
  5119. if ($this->empty_string($w) OR ($w <= 0)) {
  5120. if ($this->rtl) {
  5121. $w = $x - $this->lMargin;
  5122. } else {
  5123. $w = $this->w - $this->rMargin - $x;
  5124. }
  5125. }
  5126. $s = '';
  5127. // fill and borders
  5128. if (is_string($border) AND (strlen($border) == 4)) {
  5129. // full border
  5130. $border = 1;
  5131. }
  5132. if ($fill OR ($border == 1)) {
  5133. if ($fill) {
  5134. $op = ($border == 1) ? 'B' : 'f';
  5135. } else {
  5136. $op = 'S';
  5137. }
  5138. if ($this->rtl) {
  5139. $xk = (($x - $w) * $k);
  5140. } else {
  5141. $xk = ($x * $k);
  5142. }
  5143. $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
  5144. }
  5145. // draw borders
  5146. $s .= $this->getCellBorder($x, $y, $w, $h, $border);
  5147. if ($txt != '') {
  5148. $txt2 = $txt;
  5149. if ($this->isunicode) {
  5150. if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
  5151. $txt2 = $this->UTF8ToLatin1($txt2);
  5152. } else {
  5153. $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values
  5154. $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl);
  5155. if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
  5156. // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ----
  5157. // NOTE: this doesn't work with HTML justification
  5158. // Symbols that could overlap on the font top (only works in LTR)
  5159. $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top
  5160. $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position
  5161. $numchars = count($unicode); // number of chars
  5162. $unik = 0;
  5163. $uniblock = array();
  5164. $uniblock[$unik] = array();
  5165. $uniblock[$unik][] = $unicode[0];
  5166. // resolve overlapping conflicts by splitting the string in several parts
  5167. for ($i = 1; $i < $numchars; ++$i) {
  5168. // check if symbols overlaps at top
  5169. if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) {
  5170. // move symbols to another array
  5171. ++$unik;
  5172. $uniblock[$unik] = array();
  5173. $uniblock[$unik][] = $unicode[$i];
  5174. ++$unik;
  5175. $uniblock[$unik] = array();
  5176. $unicode[$i] = 0x200b; // Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
  5177. } else {
  5178. $uniblock[$unik][] = $unicode[$i];
  5179. }
  5180. }
  5181. // ---- END OF Fix for bug #2977340
  5182. }
  5183. $txt2 = $this->arrUTF8ToUTF16BE($unicode, false);
  5184. }
  5185. }
  5186. $txt2 = $this->_escape($txt2);
  5187. // get current text width (considering general font stretching and spacing)
  5188. $txwidth = $this->GetStringWidth($txt);
  5189. $width = $txwidth;
  5190. // check for stretch mode
  5191. if ($stretch > 0) {
  5192. // calculate ratio between cell width and text width
  5193. if ($width <= 0) {
  5194. $ratio = 1;
  5195. } else {
  5196. $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
  5197. }
  5198. // check if stretching is required
  5199. if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
  5200. // the text will be stretched to fit cell width
  5201. if ($stretch > 2) {
  5202. // set new character spacing
  5203. $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
  5204. } else {
  5205. // set new horizontal stretching
  5206. $this->font_stretching *= $ratio;
  5207. }
  5208. // recalculate text width (the text fills the entire cell)
  5209. $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
  5210. // reset alignment
  5211. $align = '';
  5212. }
  5213. }
  5214. if ($this->font_stretching != 100) {
  5215. // apply font stretching
  5216. $rs .= sprintf('BT %.2F Tz ET ', $this->font_stretching);
  5217. }
  5218. if ($this->font_spacing != 0) {
  5219. // increase/decrease font spacing
  5220. $rs .= sprintf('BT %.2F Tc ET ', ($this->font_spacing * $this->k));
  5221. }
  5222. if ($this->ColorFlag) {
  5223. $s .= 'q '.$this->TextColor.' ';
  5224. }
  5225. // rendering mode
  5226. $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth);
  5227. // count number of spaces
  5228. $ns = substr_count($txt, chr(32));
  5229. // Justification
  5230. $spacewidth = 0;
  5231. if (($align == 'J') AND ($ns > 0)) {
  5232. if ($this->isUnicodeFont()) {
  5233. // get string width without spaces
  5234. $width = $this->GetStringWidth(str_replace(' ', '', $txt));
  5235. // calculate average space width
  5236. $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
  5237. if ($this->font_stretching != 100) {
  5238. // word spacing is affected by stretching
  5239. $spacewidth /= ($this->font_stretching / 100);
  5240. }
  5241. // set word position to be used with TJ operator
  5242. $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2);
  5243. $unicode_justification = true;
  5244. } else {
  5245. // get string width
  5246. $width = $txwidth;
  5247. // new space width
  5248. $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
  5249. if ($this->font_stretching != 100) {
  5250. // word spacing (Tw) is affected by stretching
  5251. $spacewidth /= ($this->font_stretching / 100);
  5252. }
  5253. // set word spacing
  5254. $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
  5255. }
  5256. $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
  5257. }
  5258. // replace carriage return characters
  5259. $txt2 = str_replace("\r", ' ', $txt2);
  5260. switch ($align) {
  5261. case 'C': {
  5262. $dx = ($w - $width) / 2;
  5263. break;
  5264. }
  5265. case 'R': {
  5266. if ($this->rtl) {
  5267. $dx = $this->cell_padding['R'];
  5268. } else {
  5269. $dx = $w - $width - $this->cell_padding['R'];
  5270. }
  5271. break;
  5272. }
  5273. case 'L': {
  5274. if ($this->rtl) {
  5275. $dx = $w - $width - $this->cell_padding['L'];
  5276. } else {
  5277. $dx = $this->cell_padding['L'];
  5278. }
  5279. break;
  5280. }
  5281. case 'J':
  5282. default: {
  5283. if ($this->rtl) {
  5284. $dx = $this->cell_padding['R'];
  5285. } else {
  5286. $dx = $this->cell_padding['L'];
  5287. }
  5288. break;
  5289. }
  5290. }
  5291. if ($this->rtl) {
  5292. $xdx = $x - $dx - $width;
  5293. } else {
  5294. $xdx = $x + $dx;
  5295. }
  5296. $xdk = $xdx * $k;
  5297. // print text
  5298. $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
  5299. if (isset($uniblock)) {
  5300. // print overlapping characters as separate string
  5301. $xshift = 0; // horizontal shift
  5302. $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
  5303. $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
  5304. foreach ($uniblock as $uk => $uniarr) {
  5305. if (($uk % 2) == 0) {
  5306. // x space to skip
  5307. if ($spacewidth != 0) {
  5308. // justification shift
  5309. $xshift += (count(array_keys($uniarr, 32)) * $spw);
  5310. }
  5311. $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
  5312. } else {
  5313. // character to print
  5314. $topchr = $this->arrUTF8ToUTF16BE($uniarr, false);
  5315. $topchr = $this->_escape($topchr);
  5316. $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
  5317. }
  5318. }
  5319. }
  5320. if ($this->underline) {
  5321. $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
  5322. }
  5323. if ($this->linethrough) {
  5324. $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
  5325. }
  5326. if ($this->overline) {
  5327. $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
  5328. }
  5329. if ($this->ColorFlag) {
  5330. $s .= ' Q';
  5331. }
  5332. if ($link) {
  5333. $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
  5334. }
  5335. }
  5336. // output cell
  5337. if ($s) {
  5338. // output cell
  5339. $rs .= $s;
  5340. if ($this->font_spacing != 0) {
  5341. // reset font spacing mode
  5342. $rs .= ' BT 0 Tc ET';
  5343. }
  5344. if ($this->font_stretching != 100) {
  5345. // reset font stretching mode
  5346. $rs .= ' BT 100 Tz ET';
  5347. }
  5348. }
  5349. // reset word spacing
  5350. if (!$this->isUnicodeFont() AND ($align == 'J')) {
  5351. $rs .= ' BT 0 Tw ET';
  5352. }
  5353. // reset stretching and spacing
  5354. $this->font_stretching = $prev_font_stretching;
  5355. $this->font_spacing = $prev_font_spacing;
  5356. $this->lasth = $h;
  5357. if ($ln > 0) {
  5358. //Go to the beginning of the next line
  5359. $this->y = $y + $h + $this->cell_margin['B'];
  5360. if ($ln == 1) {
  5361. if ($this->rtl) {
  5362. $this->x = $this->w - $this->rMargin;
  5363. } else {
  5364. $this->x = $this->lMargin;
  5365. }
  5366. }
  5367. } else {
  5368. // go left or right by case
  5369. if ($this->rtl) {
  5370. $this->x = $x - $w - $this->cell_margin['L'];
  5371. } else {
  5372. $this->x = $x + $w + $this->cell_margin['R'];
  5373. }
  5374. }
  5375. $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
  5376. $rs = $gstyles.$rs;
  5377. $this->cell_padding = $prev_cell_padding;
  5378. $this->cell_margin = $prev_cell_margin;
  5379. return $rs;
  5380. }
  5381. /**
  5382. * Returns the code to draw the cell border
  5383. * @param float $x X coordinate.
  5384. * @param float $y Y coordinate.
  5385. * @param float $w Cell width.
  5386. * @param float $h Cell height.
  5387. * @param mixed $brd Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  5388. * @param string $mode border position respect the square edge: normal: centered; ext: external; int: internal;
  5389. * @return string containing cell border code
  5390. * @access protected
  5391. * @see SetLineStyle()
  5392. * @since 5.7.000 (2010-08-02)
  5393. */
  5394. protected function getCellBorder($x, $y, $w, $h, $brd) {
  5395. $s = ''; // string to be returned
  5396. if (empty($brd)) {
  5397. return $s;
  5398. }
  5399. if ($brd == 1) {
  5400. $brd = array('LRTB' => true);
  5401. }
  5402. // calculate coordinates for border
  5403. $k = $this->k;
  5404. if ($this->rtl) {
  5405. $xeL = ($x - $w) * $k;
  5406. $xeR = $x * $k;
  5407. } else {
  5408. $xeL = $x * $k;
  5409. $xeR = ($x + $w) * $k;
  5410. }
  5411. $yeL = (($this->h - ($y + $h)) * $k);
  5412. $yeT = (($this->h - $y) * $k);
  5413. $xeT = $xeL;
  5414. $xeB = $xeR;
  5415. $yeR = $yeT;
  5416. $yeB = $yeL;
  5417. if (is_string($brd)) {
  5418. // convert string to array
  5419. $slen = strlen($brd);
  5420. $newbrd = array();
  5421. for ($i = 0; $i < $slen; ++$i) {
  5422. $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter');
  5423. }
  5424. $brd = $newbrd;
  5425. }
  5426. if (isset($brd['mode'])) {
  5427. $mode = $brd['mode'];
  5428. unset($brd['mode']);
  5429. } else {
  5430. $mode = 'normal';
  5431. }
  5432. foreach ($brd as $border => $style) {
  5433. if (is_array($style) AND !empty($style)) {
  5434. // apply border style
  5435. $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
  5436. $s .= $this->SetLineStyle($style, true)."\n";
  5437. }
  5438. switch ($mode) {
  5439. case 'ext': {
  5440. $off = (($this->LineWidth / 2) * $k);
  5441. $xL = $xeL - $off;
  5442. $xR = $xeR + $off;
  5443. $yT = $yeT + $off;
  5444. $yL = $yeL - $off;
  5445. $xT = $xL;
  5446. $xB = $xR;
  5447. $yR = $yT;
  5448. $yB = $yL;
  5449. $w += $this->LineWidth;
  5450. $h += $this->LineWidth;
  5451. break;
  5452. }
  5453. case 'int': {
  5454. $off = ($this->LineWidth / 2) * $k;
  5455. $xL = $xeL + $off;
  5456. $xR = $xeR - $off;
  5457. $yT = $yeT - $off;
  5458. $yL = $yeL + $off;
  5459. $xT = $xL;
  5460. $xB = $xR;
  5461. $yR = $yT;
  5462. $yB = $yL;
  5463. $w -= $this->LineWidth;
  5464. $h -= $this->LineWidth;
  5465. break;
  5466. }
  5467. case 'normal':
  5468. default: {
  5469. $xL = $xeL;
  5470. $xT = $xeT;
  5471. $xB = $xeB;
  5472. $xR = $xeR;
  5473. $yL = $yeL;
  5474. $yT = $yeT;
  5475. $yB = $yeB;
  5476. $yR = $yeR;
  5477. break;
  5478. }
  5479. }
  5480. // draw borders by case
  5481. if (strlen($border) == 4) {
  5482. $s .= sprintf('%.2F %.2F %.2F %.2F re S ', $xT, $yT, ($w * $k), (-$h * $k));
  5483. } elseif (strlen($border) == 3) {
  5484. if (strpos($border,'B') === false) { // LTR
  5485. $s .= sprintf('%.2F %.2F m ', $xL, $yL);
  5486. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5487. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5488. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5489. $s .= 'S ';
  5490. } elseif (strpos($border,'L') === false) { // TRB
  5491. $s .= sprintf('%.2F %.2F m ', $xT, $yT);
  5492. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5493. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5494. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5495. $s .= 'S ';
  5496. } elseif (strpos($border,'T') === false) { // RBL
  5497. $s .= sprintf('%.2F %.2F m ', $xR, $yR);
  5498. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5499. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5500. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5501. $s .= 'S ';
  5502. } elseif (strpos($border,'R') === false) { // BLT
  5503. $s .= sprintf('%.2F %.2F m ', $xB, $yB);
  5504. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5505. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5506. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5507. $s .= 'S ';
  5508. }
  5509. } elseif (strlen($border) == 2) {
  5510. if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
  5511. $s .= sprintf('%.2F %.2F m ', $xL, $yL);
  5512. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5513. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5514. $s .= 'S ';
  5515. } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
  5516. $s .= sprintf('%.2F %.2F m ', $xT, $yT);
  5517. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5518. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5519. $s .= 'S ';
  5520. } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
  5521. $s .= sprintf('%.2F %.2F m ', $xR, $yR);
  5522. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5523. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5524. $s .= 'S ';
  5525. } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
  5526. $s .= sprintf('%.2F %.2F m ', $xB, $yB);
  5527. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5528. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5529. $s .= 'S ';
  5530. } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
  5531. $s .= sprintf('%.2F %.2F m ', $xL, $yL);
  5532. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5533. $s .= 'S ';
  5534. $s .= sprintf('%.2F %.2F m ', $xR, $yR);
  5535. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5536. $s .= 'S ';
  5537. } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
  5538. $s .= sprintf('%.2F %.2F m ', $xT, $yT);
  5539. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5540. $s .= 'S ';
  5541. $s .= sprintf('%.2F %.2F m ', $xB, $yB);
  5542. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5543. $s .= 'S ';
  5544. }
  5545. } else { // strlen($border) == 1
  5546. if (strpos($border,'L') !== false) { // L
  5547. $s .= sprintf('%.2F %.2F m ', $xL, $yL);
  5548. $s .= sprintf('%.2F %.2F l ', $xT, $yT);
  5549. $s .= 'S ';
  5550. } elseif (strpos($border,'T') !== false) { // T
  5551. $s .= sprintf('%.2F %.2F m ', $xT, $yT);
  5552. $s .= sprintf('%.2F %.2F l ', $xR, $yR);
  5553. $s .= 'S ';
  5554. } elseif (strpos($border,'R') !== false) { // R
  5555. $s .= sprintf('%.2F %.2F m ', $xR, $yR);
  5556. $s .= sprintf('%.2F %.2F l ', $xB, $yB);
  5557. $s .= 'S ';
  5558. } elseif (strpos($border,'B') !== false) { // B
  5559. $s .= sprintf('%.2F %.2F m ', $xB, $yB);
  5560. $s .= sprintf('%.2F %.2F l ', $xL, $yL);
  5561. $s .= 'S ';
  5562. }
  5563. }
  5564. if (is_array($style) AND !empty($style)) {
  5565. // reset border style to previous value
  5566. $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
  5567. }
  5568. }
  5569. return $s;
  5570. }
  5571. /**
  5572. * This method allows printing text with line breaks.
  5573. * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
  5574. * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
  5575. * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
  5576. * @param float $h Cell minimum height. The cell extends automatically if needed.
  5577. * @param string $txt String to print
  5578. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  5579. * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
  5580. * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
  5581. * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
  5582. * @param float $x x position in user units
  5583. * @param float $y y position in user units
  5584. * @param boolean $reseth if true reset the last cell height (default true).
  5585. * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
  5586. * @param boolean $ishtml set to true if $txt is HTML content (default = false).
  5587. * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
  5588. * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
  5589. * @param string $valign Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false.
  5590. * @param boolean $fitcell if true attempt to fit all the text within the cell by reducing the font size.
  5591. * @return int Return the number of cells or 1 for html mode.
  5592. * @access public
  5593. * @since 1.3
  5594. * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
  5595. */
  5596. public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
  5597. $prev_cell_margin = $this->cell_margin;
  5598. $prev_cell_padding = $this->cell_padding;
  5599. // adjust internal padding
  5600. $this->adjustCellPadding($border);
  5601. $mc_padding = $this->cell_padding;
  5602. $mc_margin = $this->cell_margin;
  5603. $this->cell_padding['T'] = 0;
  5604. $this->cell_padding['B'] = 0;
  5605. $this->setCellMargins(0, 0, 0, 0);
  5606. if ($this->empty_string($this->lasth) OR $reseth) {
  5607. // reset row height
  5608. $this->resetLastH();
  5609. }
  5610. if (!$this->empty_string($y)) {
  5611. $this->SetY($y);
  5612. } else {
  5613. $y = $this->GetY();
  5614. }
  5615. $resth = 0;
  5616. if ((!$this->InFooter) AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
  5617. // spit cell in more pages/columns
  5618. $newh = $this->PageBreakTrigger - $y;
  5619. $resth = $h - $newh; // cell to be printed on the next page/column
  5620. $h = $newh;
  5621. }
  5622. // get current page number
  5623. $startpage = $this->page;
  5624. // get current column
  5625. $startcolumn = $this->current_column;
  5626. if (!$this->empty_string($x)) {
  5627. $this->SetX($x);
  5628. } else {
  5629. $x = $this->GetX();
  5630. }
  5631. // check page for no-write regions and adapt page margins if necessary
  5632. $this->checkPageRegions(0, $x, $y);
  5633. // apply margins
  5634. $oy = $y + $mc_margin['T'];
  5635. if ($this->rtl) {
  5636. $ox = $this->w - $x - $mc_margin['R'];
  5637. } else {
  5638. $ox = $x + $mc_margin['L'];
  5639. }
  5640. $this->x = $ox;
  5641. $this->y = $oy;
  5642. // set width
  5643. if ($this->empty_string($w) OR ($w <= 0)) {
  5644. if ($this->rtl) {
  5645. $w = $this->x - $this->lMargin - $mc_margin['L'];
  5646. } else {
  5647. $w = $this->w - $this->x - $this->rMargin - $mc_margin['R'];
  5648. }
  5649. }
  5650. // store original margin values
  5651. $lMargin = $this->lMargin;
  5652. $rMargin = $this->rMargin;
  5653. if ($this->rtl) {
  5654. $this->rMargin = $this->w - $this->x;
  5655. $this->lMargin = $this->x - $w;
  5656. } else {
  5657. $this->lMargin = $this->x;
  5658. $this->rMargin = $this->w - $this->x - $w;
  5659. }
  5660. if ($autopadding) {
  5661. // add top padding
  5662. $this->y += $mc_padding['T'];
  5663. }
  5664. if ($ishtml) { // ******* Write HTML text
  5665. $this->writeHTML($txt, true, 0, $reseth, true, $align);
  5666. $nl = 1;
  5667. } else { // ******* Write simple text
  5668. // vertical alignment
  5669. if ($maxh > 0) {
  5670. // get text height
  5671. $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
  5672. if ($fitcell) {
  5673. $prev_FontSizePt = $this->FontSizePt;
  5674. // try to reduce font size to fit text on cell (use a quick search algorithm)
  5675. $fmin = 1;
  5676. $fmax = $this->FontSizePt;
  5677. $prev_text_height = $text_height;
  5678. $maxit = 100; // max number of iterations
  5679. while ($maxit > 0) {
  5680. $fmid = (($fmax + $fmin) / 2);
  5681. $this->SetFontSize($fmid, false);
  5682. $this->resetLastH();
  5683. $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
  5684. if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
  5685. break;
  5686. } elseif ($text_height < $maxh) {
  5687. $fmin = $fmid;
  5688. } else {
  5689. $fmax = $fmid;
  5690. }
  5691. --$maxit;
  5692. }
  5693. $this->SetFontSize($this->FontSizePt);
  5694. }
  5695. if ($text_height < $maxh) {
  5696. if ($valign == 'M') {
  5697. // text vertically centered
  5698. $this->y += (($maxh - $text_height) / 2);
  5699. } elseif ($valign == 'B') {
  5700. // text vertically aligned on bottom
  5701. $this->y += ($maxh - $text_height);
  5702. }
  5703. }
  5704. }
  5705. $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
  5706. if ($fitcell) {
  5707. // restore font size
  5708. $this->SetFontSize($prev_FontSizePt);
  5709. }
  5710. }
  5711. if ($autopadding) {
  5712. // add bottom padding
  5713. $this->y += $mc_padding['B'];
  5714. }
  5715. // Get end-of-text Y position
  5716. $currentY = $this->y;
  5717. // get latest page number
  5718. $endpage = $this->page;
  5719. if ($resth > 0) {
  5720. $skip = ($endpage - $startpage);
  5721. $tmpresth = $resth;
  5722. while ($tmpresth > 0) {
  5723. if ($skip <= 0) {
  5724. // add a page (or trig AcceptPageBreak() for multicolumn mode)
  5725. $this->checkPageBreak($this->PageBreakTrigger + 1);
  5726. }
  5727. if ($this->num_columns > 1) {
  5728. $tmpresth -= ($this->h - $this->y - $this->bMargin);
  5729. } else {
  5730. $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
  5731. }
  5732. --$skip;
  5733. }
  5734. $currentY = $this->y;
  5735. $endpage = $this->page;
  5736. }
  5737. // get latest column
  5738. $endcolumn = $this->current_column;
  5739. if ($this->num_columns == 0) {
  5740. $this->num_columns = 1;
  5741. }
  5742. // get border modes
  5743. $border_start = $this->getBorderMode($border, $position='start');
  5744. $border_end = $this->getBorderMode($border, $position='end');
  5745. $border_middle = $this->getBorderMode($border, $position='middle');
  5746. // design borders around HTML cells.
  5747. for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
  5748. $ccode = '';
  5749. $this->setPage($page);
  5750. if ($this->num_columns < 2) {
  5751. // single-column mode
  5752. $this->SetX($x);
  5753. $this->y = $this->tMargin;
  5754. }
  5755. // account for margin changes
  5756. if ($page > $startpage) {
  5757. if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
  5758. $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
  5759. } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
  5760. $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
  5761. }
  5762. }
  5763. if ($startpage == $endpage) {
  5764. // single page
  5765. for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
  5766. $this->selectColumn($column);
  5767. if ($this->rtl) {
  5768. $this->x -= $mc_margin['R'];
  5769. } else {
  5770. $this->x += $mc_margin['L'];
  5771. }
  5772. if ($startcolumn == $endcolumn) { // single column
  5773. $cborder = $border;
  5774. $h = max($h, ($currentY - $oy));
  5775. $this->y = $oy;
  5776. } elseif ($column == $startcolumn) { // first column
  5777. $cborder = $border_start;
  5778. $this->y = $oy;
  5779. $h = $this->h - $this->y - $this->bMargin;
  5780. } elseif ($column == $endcolumn) { // end column
  5781. $cborder = $border_end;
  5782. $h = $currentY - $this->y;
  5783. if ($resth > $h) {
  5784. $h = $resth;
  5785. }
  5786. } else { // middle column
  5787. $cborder = $border_middle;
  5788. $h = $this->h - $this->y - $this->bMargin;
  5789. $resth -= $h;
  5790. }
  5791. $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
  5792. } // end for each column
  5793. } elseif ($page == $startpage) { // first page
  5794. for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
  5795. $this->selectColumn($column);
  5796. if ($this->rtl) {
  5797. $this->x -= $mc_margin['R'];
  5798. } else {
  5799. $this->x += $mc_margin['L'];
  5800. }
  5801. if ($column == $startcolumn) { // first column
  5802. $cborder = $border_start;
  5803. $this->y = $oy;
  5804. $h = $this->h - $this->y - $this->bMargin;
  5805. } else { // middle column
  5806. $cborder = $border_middle;
  5807. $h = $this->h - $this->y - $this->bMargin;
  5808. $resth -= $h;
  5809. }
  5810. $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
  5811. } // end for each column
  5812. } elseif ($page == $endpage) { // last page
  5813. for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
  5814. $this->selectColumn($column);
  5815. if ($this->rtl) {
  5816. $this->x -= $mc_margin['R'];
  5817. } else {
  5818. $this->x += $mc_margin['L'];
  5819. }
  5820. if ($column == $endcolumn) {
  5821. // end column
  5822. $cborder = $border_end;
  5823. $h = $currentY - $this->y;
  5824. if ($resth > $h) {
  5825. $h = $resth;
  5826. }
  5827. } else {
  5828. // middle column
  5829. $cborder = $border_middle;
  5830. $h = $this->h - $this->y - $this->bMargin;
  5831. $resth -= $h;
  5832. }
  5833. $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
  5834. } // end for each column
  5835. } else { // middle page
  5836. for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
  5837. $this->selectColumn($column);
  5838. if ($this->rtl) {
  5839. $this->x -= $mc_margin['R'];
  5840. } else {
  5841. $this->x += $mc_margin['L'];
  5842. }
  5843. $cborder = $border_middle;
  5844. $h = $this->h - $this->y - $this->bMargin;
  5845. $resth -= $h;
  5846. $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
  5847. } // end for each column
  5848. }
  5849. if ($cborder OR $fill) {
  5850. // draw border and fill
  5851. if ($this->inxobj) {
  5852. // we are inside an XObject template
  5853. if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
  5854. $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
  5855. $pagemark = &$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
  5856. } else {
  5857. $pagemark = &$this->xobjects[$this->xobjid]['intmrk'];
  5858. }
  5859. $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
  5860. $pstart = substr($pagebuff, 0, $pagemark);
  5861. $pend = substr($pagebuff, $pagemark);
  5862. $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
  5863. $pagemark += strlen($ccode);
  5864. } else {
  5865. if (end($this->transfmrk[$this->page]) !== false) {
  5866. $pagemarkkey = key($this->transfmrk[$this->page]);
  5867. $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
  5868. } elseif ($this->InFooter) {
  5869. $pagemark = &$this->footerpos[$this->page];
  5870. } else {
  5871. $pagemark = &$this->intmrk[$this->page];
  5872. }
  5873. $pagebuff = $this->getPageBuffer($this->page);
  5874. $pstart = substr($pagebuff, 0, $pagemark);
  5875. $pend = substr($pagebuff, $pagemark);
  5876. $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
  5877. $pagemark += strlen($ccode);
  5878. }
  5879. }
  5880. } // end for each page
  5881. // Get end-of-cell Y position
  5882. $currentY = $this->GetY();
  5883. // restore original margin values
  5884. $this->SetLeftMargin($lMargin);
  5885. $this->SetRightMargin($rMargin);
  5886. if ($ln > 0) {
  5887. //Go to the beginning of the next line
  5888. $this->SetY($currentY + $mc_margin['B']);
  5889. if ($ln == 2) {
  5890. $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
  5891. }
  5892. } else {
  5893. // go left or right by case
  5894. $this->setPage($startpage);
  5895. $this->y = $y;
  5896. $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
  5897. }
  5898. $this->setContentMark();
  5899. $this->cell_padding = $prev_cell_padding;
  5900. $this->cell_margin = $prev_cell_margin;
  5901. return $nl;
  5902. }
  5903. /**
  5904. * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
  5905. * @param mixed $brd Indicates if borders must be drawn around the cell block. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  5906. * @param string multicell position: 'start', 'middle', 'end'
  5907. * @return border mode array
  5908. * @access protected
  5909. * @since 4.4.002 (2008-12-09)
  5910. */
  5911. protected function getBorderMode($brd, $position='start') {
  5912. if ((!$this->opencell) OR empty($brd)) {
  5913. return $brd;
  5914. }
  5915. if ($brd == 1) {
  5916. $brd = 'LTRB';
  5917. }
  5918. if (is_string($brd)) {
  5919. // convert string to array
  5920. $slen = strlen($brd);
  5921. $newbrd = array();
  5922. for ($i = 0; $i < $slen; ++$i) {
  5923. $newbrd[$brd{$i}] = array('cap' => 'square', 'join' => 'miter');
  5924. }
  5925. $brd = $newbrd;
  5926. }
  5927. foreach ($brd as $border => $style) {
  5928. switch ($position) {
  5929. case 'start': {
  5930. if (strpos($border, 'B') !== false) {
  5931. // remove bottom line
  5932. $newkey = str_replace('B', '', $border);
  5933. if (strlen($newkey) > 0) {
  5934. $brd[$newkey] = $style;
  5935. }
  5936. unset($brd[$border]);
  5937. }
  5938. break;
  5939. }
  5940. case 'middle': {
  5941. if (strpos($border, 'B') !== false) {
  5942. // remove bottom line
  5943. $newkey = str_replace('B', '', $border);
  5944. if (strlen($newkey) > 0) {
  5945. $brd[$newkey] = $style;
  5946. }
  5947. unset($brd[$border]);
  5948. $border = $newkey;
  5949. }
  5950. if (strpos($border, 'T') !== false) {
  5951. // remove bottom line
  5952. $newkey = str_replace('T', '', $border);
  5953. if (strlen($newkey) > 0) {
  5954. $brd[$newkey] = $style;
  5955. }
  5956. unset($brd[$border]);
  5957. }
  5958. break;
  5959. }
  5960. case 'end': {
  5961. if (strpos($border, 'T') !== false) {
  5962. // remove bottom line
  5963. $newkey = str_replace('T', '', $border);
  5964. if (strlen($newkey) > 0) {
  5965. $brd[$newkey] = $style;
  5966. }
  5967. unset($brd[$border]);
  5968. }
  5969. break;
  5970. }
  5971. }
  5972. }
  5973. return $brd;
  5974. }
  5975. /**
  5976. * This method return the estimated number of lines for print a simple text string using Multicell() method.
  5977. * @param string $txt String for calculating his height
  5978. * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
  5979. * @param boolean $reseth if true reset the last cell height (default false).
  5980. * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width (default true).
  5981. * @param float $cellpadding Internal cell padding, if empty uses default cell padding.
  5982. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  5983. * @return float Return the minimal height needed for multicell method for printing the $txt param.
  5984. * @author Alexander Escalona Fernández, Nicola Asuni
  5985. * @access public
  5986. * @since 4.5.011
  5987. */
  5988. public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
  5989. if ($txt === '') {
  5990. // empty string
  5991. return 1;
  5992. }
  5993. // adjust internal padding
  5994. $prev_cell_padding = $this->cell_padding;
  5995. $prev_lasth = $this->lasth;
  5996. if (is_array($cellpadding)) {
  5997. $this->cell_padding = $cellpadding;
  5998. }
  5999. $this->adjustCellPadding($border);
  6000. if ($this->empty_string($w) OR ($w <= 0)) {
  6001. if ($this->rtl) {
  6002. $w = $this->x - $this->lMargin;
  6003. } else {
  6004. $w = $this->w - $this->rMargin - $this->x;
  6005. }
  6006. }
  6007. $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
  6008. if ($reseth) {
  6009. // reset row height
  6010. $this->resetLastH();
  6011. }
  6012. $lines = 1;
  6013. $sum = 0;
  6014. $chars = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
  6015. $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
  6016. $length = count($chars);
  6017. $lastSeparator = -1;
  6018. for ($i = 0; $i < $length; ++$i) {
  6019. $charWidth = $charsWidth[$i];
  6020. if (preg_match($this->re_spaces, $this->unichr($chars[$i]))) {
  6021. $lastSeparator = $i;
  6022. }
  6023. if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
  6024. ++$lines;
  6025. if ($lastSeparator != -1) {
  6026. $i = $lastSeparator;
  6027. $lastSeparator = -1;
  6028. $sum = 0;
  6029. } else {
  6030. $sum = $charWidth;
  6031. }
  6032. } else {
  6033. $sum += $charWidth;
  6034. }
  6035. }
  6036. if ($chars[($length - 1)] == 10) {
  6037. --$lines;
  6038. }
  6039. $this->cell_padding = $prev_cell_padding;
  6040. $this->lasth = $prev_lasth;
  6041. return $lines;
  6042. }
  6043. /**
  6044. * This method return the estimated needed height for print a simple text string in Multicell() method.
  6045. * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
  6046. * <pre>
  6047. * // store current object
  6048. * $pdf->startTransaction();
  6049. * // store starting values
  6050. * $start_y = $pdf->GetY();
  6051. * $start_page = $pdf->getPage();
  6052. * // call your printing functions with your parameters
  6053. * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6054. * $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
  6055. * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  6056. * // get the new Y
  6057. * $end_y = $pdf->GetY();
  6058. * $end_page = $pdf->getPage();
  6059. * // calculate height
  6060. * $height = 0;
  6061. * if ($end_page == $start_page) {
  6062. * $height = $end_y - $start_y;
  6063. * } else {
  6064. * for ($page=$start_page; $page <= $end_page; ++$page) {
  6065. * $this->setPage($page);
  6066. * if ($page == $start_page) {
  6067. * // first page
  6068. * $height = $this->h - $start_y - $this->bMargin;
  6069. * } elseif ($page == $end_page) {
  6070. * // last page
  6071. * $height = $end_y - $this->tMargin;
  6072. * } else {
  6073. * $height = $this->h - $this->tMargin - $this->bMargin;
  6074. * }
  6075. * }
  6076. * }
  6077. * // restore previous object
  6078. * $pdf = $pdf->rollbackTransaction();
  6079. * </pre>
  6080. * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
  6081. * @param string $txt String for calculating his height
  6082. * @param boolean $reseth if true reset the last cell height (default false).
  6083. * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width (default true).
  6084. * @param float $cellpadding Internal cell padding, if empty uses default cell padding.
  6085. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  6086. * @return float Return the minimal height needed for multicell method for printing the $txt param.
  6087. * @author Nicola Asuni, Alexander Escalona Fernández
  6088. * @access public
  6089. */
  6090. public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
  6091. // adjust internal padding
  6092. $prev_cell_padding = $this->cell_padding;
  6093. $prev_lasth = $this->lasth;
  6094. if (is_array($cellpadding)) {
  6095. $this->cell_padding = $cellpadding;
  6096. }
  6097. $this->adjustCellPadding($border);
  6098. $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
  6099. $height = $lines * ($this->FontSize * $this->cell_height_ratio);
  6100. if ($autopadding) {
  6101. // add top and bottom padding
  6102. $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
  6103. }
  6104. $this->cell_padding = $prev_cell_padding;
  6105. $this->lasth = $prev_lasth;
  6106. return $height;
  6107. }
  6108. /**
  6109. * This method prints text from the current position.<br />
  6110. * @param float $h Line height
  6111. * @param string $txt String to print
  6112. * @param mixed $link URL or identifier returned by AddLink()
  6113. * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
  6114. * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
  6115. * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
  6116. * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
  6117. * @param boolean $firstline if true prints only the first line and return the remaining string.
  6118. * @param boolean $firstblock if true the string is the starting of a line.
  6119. * @param float $maxh maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
  6120. * @param float $wadj first line width will be reduced by this amount (used in HTML mode).
  6121. * @param array $margin margin array of the parent container
  6122. * @return mixed Return the number of cells or the remaining string if $firstline = true.
  6123. * @access public
  6124. * @since 1.5
  6125. */
  6126. public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
  6127. // check page for no-write regions and adapt page margins if necessary
  6128. $this->checkPageRegions($h);
  6129. if (strlen($txt) == 0) {
  6130. // fix empty text
  6131. $txt = ' ';
  6132. }
  6133. if ($margin === '') {
  6134. // set default margins
  6135. $margin = $this->cell_margin;
  6136. }
  6137. // remove carriage returns
  6138. $s = str_replace("\r", '', $txt);
  6139. // check if string contains arabic text
  6140. if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $s)) {
  6141. $arabic = true;
  6142. } else {
  6143. $arabic = false;
  6144. }
  6145. // check if string contains RTL text
  6146. if ($arabic OR ($this->tmprtl == 'R') OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $s)) {
  6147. $rtlmode = true;
  6148. } else {
  6149. $rtlmode = false;
  6150. }
  6151. // get a char width
  6152. $chrwidth = $this->GetCharWidth('.');
  6153. // get array of unicode values
  6154. $chars = $this->UTF8StringToArray($s);
  6155. // get array of chars
  6156. $uchars = $this->UTF8ArrayToUniArray($chars);
  6157. // get the number of characters
  6158. $nb = count($chars);
  6159. // replacement for SHY character (minus symbol)
  6160. $shy_replacement = 45;
  6161. $shy_replacement_char = $this->unichr($shy_replacement);
  6162. // widht for SHY replacement
  6163. $shy_replacement_width = $this->GetCharWidth($shy_replacement);
  6164. // max Y
  6165. $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
  6166. // calculate remaining line width ($w)
  6167. if ($this->rtl) {
  6168. $w = $this->x - $this->lMargin;
  6169. } else {
  6170. $w = $this->w - $this->rMargin - $this->x;
  6171. }
  6172. // max column width
  6173. $wmax = $w - $wadj;
  6174. if (!$firstline) {
  6175. $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
  6176. }
  6177. if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
  6178. // a single character do not fit on column
  6179. return '';
  6180. }
  6181. // minimum row height
  6182. $row_height = max($h, $this->FontSize * $this->cell_height_ratio);
  6183. $start_page = $this->page;
  6184. $i = 0; // character position
  6185. $j = 0; // current starting position
  6186. $sep = -1; // position of the last blank space
  6187. $shy = false; // true if the last blank is a soft hypen (SHY)
  6188. $l = 0; // current string length
  6189. $nl = 0; //number of lines
  6190. $linebreak = false;
  6191. $pc = 0; // previous character
  6192. // for each character
  6193. while ($i < $nb) {
  6194. if (($maxh > 0) AND ($this->y >= $maxy) ) {
  6195. break;
  6196. }
  6197. //Get the current character
  6198. $c = $chars[$i];
  6199. if ($c == 10) { // 10 = "\n" = new line
  6200. //Explicit line break
  6201. if ($align == 'J') {
  6202. if ($this->rtl) {
  6203. $talign = 'R';
  6204. } else {
  6205. $talign = 'L';
  6206. }
  6207. } else {
  6208. $talign = $align;
  6209. }
  6210. $tmpstr = $this->UniArrSubString($uchars, $j, $i);
  6211. if ($firstline) {
  6212. $startx = $this->x;
  6213. $tmparr = array_slice($chars, $j, ($i - $j));
  6214. if ($rtlmode) {
  6215. $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
  6216. }
  6217. $linew = $this->GetArrStringWidth($tmparr);
  6218. unset($tmparr);
  6219. if ($this->rtl) {
  6220. $this->endlinex = $startx - $linew;
  6221. } else {
  6222. $this->endlinex = $startx + $linew;
  6223. }
  6224. $w = $linew;
  6225. $tmpcellpadding = $this->cell_padding;
  6226. if ($maxh == 0) {
  6227. $this->SetCellPadding(0);
  6228. }
  6229. }
  6230. if ($firstblock AND $this->isRTLTextDir()) {
  6231. $tmpstr = $this->stringRightTrim($tmpstr);
  6232. }
  6233. // Skip newlines at the begining of a page or column
  6234. if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
  6235. $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
  6236. }
  6237. unset($tmpstr);
  6238. if ($firstline) {
  6239. $this->cell_padding = $tmpcellpadding;
  6240. return ($this->UniArrSubString($uchars, $i));
  6241. }
  6242. ++$nl;
  6243. $j = $i + 1;
  6244. $l = 0;
  6245. $sep = -1;
  6246. $shy = false;
  6247. // account for margin changes
  6248. if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
  6249. $this->AcceptPageBreak();
  6250. if ($this->rtl) {
  6251. $this->x -= $margin['R'];
  6252. } else {
  6253. $this->x += $margin['L'];
  6254. }
  6255. $this->lMargin += $margin['L'];
  6256. $this->rMargin += $margin['R'];
  6257. }
  6258. $w = $this->getRemainingWidth();
  6259. $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
  6260. } else {
  6261. // 160 is the non-breaking space.
  6262. // 173 is SHY (Soft Hypen).
  6263. // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
  6264. // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
  6265. // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
  6266. if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
  6267. // update last blank space position
  6268. $sep = $i;
  6269. // check if is a SHY
  6270. if ($c == 173) {
  6271. $shy = true;
  6272. if ($pc == 45) {
  6273. $tmp_shy_replacement_width = 0;
  6274. $tmp_shy_replacement_char = '';
  6275. } else {
  6276. $tmp_shy_replacement_width = $shy_replacement_width;
  6277. $tmp_shy_replacement_char = $shy_replacement_char;
  6278. }
  6279. } else {
  6280. $shy = false;
  6281. }
  6282. }
  6283. // update string length
  6284. if ($this->isUnicodeFont() AND ($arabic)) {
  6285. // with bidirectional algorithm some chars may be changed affecting the line length
  6286. // *** very slow ***
  6287. $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl));
  6288. } else {
  6289. $l += $this->GetCharWidth($c);
  6290. }
  6291. if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
  6292. // we have reached the end of column
  6293. if ($sep == -1) {
  6294. // check if the line was already started
  6295. if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
  6296. OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
  6297. // print a void cell and go to next line
  6298. $this->Cell($w, $h, '', 0, 1);
  6299. $linebreak = true;
  6300. if ($firstline) {
  6301. return ($this->UniArrSubString($uchars, $j));
  6302. }
  6303. } else {
  6304. // truncate the word because do not fit on column
  6305. $tmpstr = $this->UniArrSubString($uchars, $j, $i);
  6306. if ($firstline) {
  6307. $startx = $this->x;
  6308. $tmparr = array_slice($chars, $j, ($i - $j));
  6309. if ($rtlmode) {
  6310. $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
  6311. }
  6312. $linew = $this->GetArrStringWidth($tmparr);
  6313. unset($tmparr);
  6314. if ($this->rtl) {
  6315. $this->endlinex = $startx - $linew;
  6316. } else {
  6317. $this->endlinex = $startx + $linew;
  6318. }
  6319. $w = $linew;
  6320. $tmpcellpadding = $this->cell_padding;
  6321. if ($maxh == 0) {
  6322. $this->SetCellPadding(0);
  6323. }
  6324. }
  6325. if ($firstblock AND $this->isRTLTextDir()) {
  6326. $tmpstr = $this->stringRightTrim($tmpstr);
  6327. }
  6328. $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
  6329. unset($tmpstr);
  6330. if ($firstline) {
  6331. $this->cell_padding = $tmpcellpadding;
  6332. return ($this->UniArrSubString($uchars, $i));
  6333. }
  6334. $j = $i;
  6335. --$i;
  6336. }
  6337. } else {
  6338. // word wrapping
  6339. if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
  6340. $endspace = 1;
  6341. } else {
  6342. $endspace = 0;
  6343. }
  6344. if ($shy) {
  6345. // add hypen (minus symbol) at the end of the line
  6346. $shy_width = $tmp_shy_replacement_width;
  6347. if ($this->rtl) {
  6348. $shy_char_left = $tmp_shy_replacement_char;
  6349. $shy_char_right = '';
  6350. } else {
  6351. $shy_char_left = '';
  6352. $shy_char_right = $tmp_shy_replacement_char;
  6353. }
  6354. } else {
  6355. $shy_width = 0;
  6356. $shy_char_left = '';
  6357. $shy_char_right = '';
  6358. }
  6359. $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
  6360. if ($firstline) {
  6361. $startx = $this->x;
  6362. $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
  6363. if ($rtlmode) {
  6364. $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
  6365. }
  6366. $linew = $this->GetArrStringWidth($tmparr);
  6367. unset($tmparr);
  6368. if ($this->rtl) {
  6369. $this->endlinex = $startx - $linew - $shy_width;
  6370. } else {
  6371. $this->endlinex = $startx + $linew + $shy_width;
  6372. }
  6373. $w = $linew;
  6374. $tmpcellpadding = $this->cell_padding;
  6375. if ($maxh == 0) {
  6376. $this->SetCellPadding(0);
  6377. }
  6378. }
  6379. // print the line
  6380. if ($firstblock AND $this->isRTLTextDir()) {
  6381. $tmpstr = $this->stringRightTrim($tmpstr);
  6382. }
  6383. $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
  6384. unset($tmpstr);
  6385. if ($firstline) {
  6386. // return the remaining text
  6387. $this->cell_padding = $tmpcellpadding;
  6388. return ($this->UniArrSubString($uchars, ($sep + $endspace)));
  6389. }
  6390. $i = $sep;
  6391. $sep = -1;
  6392. $shy = false;
  6393. $j = ($i+1);
  6394. }
  6395. // account for margin changes
  6396. if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
  6397. $this->AcceptPageBreak();
  6398. if ($this->rtl) {
  6399. $this->x -= $margin['R'];
  6400. } else {
  6401. $this->x += $margin['L'];
  6402. }
  6403. $this->lMargin += $margin['L'];
  6404. $this->rMargin += $margin['R'];
  6405. }
  6406. $w = $this->getRemainingWidth();
  6407. $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
  6408. if ($linebreak) {
  6409. $linebreak = false;
  6410. } else {
  6411. ++$nl;
  6412. $l = 0;
  6413. }
  6414. }
  6415. }
  6416. // save last character
  6417. $pc = $c;
  6418. ++$i;
  6419. } // end while i < nb
  6420. // print last substring (if any)
  6421. if ($l > 0) {
  6422. switch ($align) {
  6423. case 'J':
  6424. case 'C': {
  6425. $w = $w;
  6426. break;
  6427. }
  6428. case 'L': {
  6429. if ($this->rtl) {
  6430. $w = $w;
  6431. } else {
  6432. $w = $l;
  6433. }
  6434. break;
  6435. }
  6436. case 'R': {
  6437. if ($this->rtl) {
  6438. $w = $l;
  6439. } else {
  6440. $w = $w;
  6441. }
  6442. break;
  6443. }
  6444. default: {
  6445. $w = $l;
  6446. break;
  6447. }
  6448. }
  6449. $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
  6450. if ($firstline) {
  6451. $startx = $this->x;
  6452. $tmparr = array_slice($chars, $j, ($nb - $j));
  6453. if ($rtlmode) {
  6454. $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
  6455. }
  6456. $linew = $this->GetArrStringWidth($tmparr);
  6457. unset($tmparr);
  6458. if ($this->rtl) {
  6459. $this->endlinex = $startx - $linew;
  6460. } else {
  6461. $this->endlinex = $startx + $linew;
  6462. }
  6463. $w = $linew;
  6464. $tmpcellpadding = $this->cell_padding;
  6465. if ($maxh == 0) {
  6466. $this->SetCellPadding(0);
  6467. }
  6468. }
  6469. if ($firstblock AND $this->isRTLTextDir()) {
  6470. $tmpstr = $this->stringRightTrim($tmpstr);
  6471. }
  6472. $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
  6473. unset($tmpstr);
  6474. if ($firstline) {
  6475. $this->cell_padding = $tmpcellpadding;
  6476. return ($this->UniArrSubString($uchars, $nb));
  6477. }
  6478. ++$nl;
  6479. }
  6480. if ($firstline) {
  6481. return '';
  6482. }
  6483. return $nl;
  6484. }
  6485. /**
  6486. * Returns the remaining width between the current position and margins.
  6487. * @return int Return the remaining width
  6488. * @access protected
  6489. */
  6490. protected function getRemainingWidth() {
  6491. $this->checkPageRegions();
  6492. if ($this->rtl) {
  6493. return ($this->x - $this->lMargin);
  6494. } else {
  6495. return ($this->w - $this->rMargin - $this->x);
  6496. }
  6497. }
  6498. /**
  6499. * Extract a slice of the $strarr array and return it as string.
  6500. * @param string $strarr The input array of characters.
  6501. * @param int $start the starting element of $strarr.
  6502. * @param int $end first element that will not be returned.
  6503. * @return Return part of a string
  6504. * @access public
  6505. */
  6506. public function UTF8ArrSubString($strarr, $start='', $end='') {
  6507. if (strlen($start) == 0) {
  6508. $start = 0;
  6509. }
  6510. if (strlen($end) == 0) {
  6511. $end = count($strarr);
  6512. }
  6513. $string = '';
  6514. for ($i=$start; $i < $end; ++$i) {
  6515. $string .= $this->unichr($strarr[$i]);
  6516. }
  6517. return $string;
  6518. }
  6519. /**
  6520. * Extract a slice of the $uniarr array and return it as string.
  6521. * @param string $uniarr The input array of characters.
  6522. * @param int $start the starting element of $strarr.
  6523. * @param int $end first element that will not be returned.
  6524. * @return Return part of a string
  6525. * @access public
  6526. * @since 4.5.037 (2009-04-07)
  6527. */
  6528. public function UniArrSubString($uniarr, $start='', $end='') {
  6529. if (strlen($start) == 0) {
  6530. $start = 0;
  6531. }
  6532. if (strlen($end) == 0) {
  6533. $end = count($uniarr);
  6534. }
  6535. $string = '';
  6536. for ($i=$start; $i < $end; ++$i) {
  6537. $string .= $uniarr[$i];
  6538. }
  6539. return $string;
  6540. }
  6541. /**
  6542. * Convert an array of UTF8 values to array of unicode characters
  6543. * @param string $ta The input array of UTF8 values.
  6544. * @return Return array of unicode characters
  6545. * @access public
  6546. * @since 4.5.037 (2009-04-07)
  6547. */
  6548. public function UTF8ArrayToUniArray($ta) {
  6549. return array_map(array($this, 'unichr'), $ta);
  6550. }
  6551. /**
  6552. * Returns the unicode caracter specified by UTF-8 value
  6553. * @param int $c UTF-8 value
  6554. * @return Returns the specified character.
  6555. * @author Miguel Perez, Nicola Asuni
  6556. * @access public
  6557. * @since 2.3.000 (2008-03-05)
  6558. */
  6559. public function unichr($c) {
  6560. if (!$this->isunicode) {
  6561. return chr($c);
  6562. } elseif ($c <= 0x7F) {
  6563. // one byte
  6564. return chr($c);
  6565. } elseif ($c <= 0x7FF) {
  6566. // two bytes
  6567. return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
  6568. } elseif ($c <= 0xFFFF) {
  6569. // three bytes
  6570. return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
  6571. } elseif ($c <= 0x10FFFF) {
  6572. // four bytes
  6573. return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
  6574. } else {
  6575. return '';
  6576. }
  6577. }
  6578. /**
  6579. * Return the image type given the file name or array returned by getimagesize() function.
  6580. * @param string $imgfile image file name
  6581. * @param array $iminfo array of image information returned by getimagesize() function.
  6582. * @return string image type
  6583. * @since 4.8.017 (2009-11-27)
  6584. */
  6585. public function getImageFileType($imgfile, $iminfo=array()) {
  6586. $type = '';
  6587. if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) {
  6588. $mime = explode('/', $iminfo['mime']);
  6589. if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) {
  6590. $type = strtolower(trim($mime[1]));
  6591. }
  6592. }
  6593. if (empty($type)) {
  6594. $fileinfo = pathinfo($imgfile);
  6595. if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
  6596. $type = strtolower(trim($fileinfo['extension']));
  6597. }
  6598. }
  6599. if ($type == 'jpg') {
  6600. $type = 'jpeg';
  6601. }
  6602. return $type;
  6603. }
  6604. /**
  6605. * Set the block dimensions accounting for page breaks and page/column fitting
  6606. * @param float $w width
  6607. * @param float $h height
  6608. * @param float $x X coordinate
  6609. * @param float $y Y coodiante
  6610. * @param boolean $fitonpage if true the block is resized to not exceed page dimensions.
  6611. * @access protected
  6612. * @since 5.5.009 (2010-07-05)
  6613. */
  6614. protected function fitBlock(&$w, &$h, &$x, &$y, $fitonpage=false) {
  6615. // resize the block to be vertically contained on a single page or single column
  6616. if ($fitonpage OR $this->AutoPageBreak) {
  6617. $ratio_wh = ($w / $h);
  6618. if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
  6619. $h = $this->PageBreakTrigger - $this->tMargin;
  6620. $w = ($h * $ratio_wh);
  6621. }
  6622. // resize the block to be horizontally contained on a single page or single column
  6623. if ($fitonpage) {
  6624. $maxw = ($this->w - $this->lMargin - $this->rMargin);
  6625. if ($w > $maxw) {
  6626. $w = $maxw;
  6627. $h = ($w / $ratio_wh);
  6628. }
  6629. }
  6630. }
  6631. // Check whether we need a new page or new column first as this does not fit
  6632. $prev_x = $this->x;
  6633. $prev_y = $this->y;
  6634. if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
  6635. $y = $this->y;
  6636. if ($this->rtl) {
  6637. $x += ($prev_x - $this->x);
  6638. } else {
  6639. $x += ($this->x - $prev_x);
  6640. }
  6641. }
  6642. // resize the block to be contained on the remaining available page or column space
  6643. if ($fitonpage) {
  6644. $ratio_wh = ($w / $h);
  6645. if (($y + $h) > $this->PageBreakTrigger) {
  6646. $h = $this->PageBreakTrigger - $y;
  6647. $w = ($h * $ratio_wh);
  6648. }
  6649. if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
  6650. $w = $this->w - $this->rMargin - $x;
  6651. $h = ($w / $ratio_wh);
  6652. } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
  6653. $w = $x - $this->lMargin;
  6654. $h = ($w / $ratio_wh);
  6655. }
  6656. }
  6657. }
  6658. /**
  6659. * Puts an image in the page.
  6660. * The upper-left corner must be given.
  6661. * The dimensions can be specified in different ways:<ul>
  6662. * <li>explicit width and height (expressed in user unit)</li>
  6663. * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
  6664. * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
  6665. * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
  6666. * The format can be specified explicitly or inferred from the file extension.<br />
  6667. * It is possible to put a link on the image.<br />
  6668. * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
  6669. * @param string $file Name of the file containing the image.
  6670. * @param float $x Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
  6671. * @param float $y Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
  6672. * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
  6673. * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
  6674. * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
  6675. * @param mixed $link URL or identifier returned by AddLink().
  6676. * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
  6677. * @param mixed $resize If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling).
  6678. * @param int $dpi dot-per-inch resolution used on resize
  6679. * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
  6680. * @param boolean $ismask true if this image is a mask, false otherwise
  6681. * @param mixed $imgmask image object returned by this function or false
  6682. * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
  6683. * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box.
  6684. * @param boolean $hidden if true do not display the image.
  6685. * @param boolean $fitonpage if true the image is resized to not exceed page dimensions.
  6686. * @return image information
  6687. * @access public
  6688. * @since 1.1
  6689. */
  6690. 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, $hidden=false, $fitonpage=false) {
  6691. if ($x === '') {
  6692. $x = $this->x;
  6693. }
  6694. if ($y === '') {
  6695. $y = $this->y;
  6696. }
  6697. // check page for no-write regions and adapt page margins if necessary
  6698. $this->checkPageRegions($h, $x, $y);
  6699. $cached_file = false; // true when the file is cached
  6700. // get image dimensions
  6701. $imsize = @getimagesize($file);
  6702. if ($imsize === FALSE) {
  6703. // try to encode spaces on filename
  6704. $file = str_replace(' ', '%20', $file);
  6705. $imsize = @getimagesize($file);
  6706. if ($imsize === FALSE) {
  6707. if (function_exists('curl_init')) {
  6708. // try to get remote file data using cURL
  6709. $cs = curl_init(); // curl session
  6710. curl_setopt($cs, CURLOPT_URL, $file);
  6711. curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
  6712. curl_setopt($cs, CURLOPT_FAILONERROR, true);
  6713. curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
  6714. curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
  6715. curl_setopt($cs, CURLOPT_TIMEOUT, 30);
  6716. $imgdata = curl_exec($cs);
  6717. curl_close($cs);
  6718. if($imgdata !== FALSE) {
  6719. // copy image to cache
  6720. $file = tempnam(K_PATH_CACHE, 'img_');
  6721. $fp = fopen($file, 'w');
  6722. fwrite($fp, $imgdata);
  6723. fclose($fp);
  6724. unset($imgdata);
  6725. $cached_file = true;
  6726. $imsize = @getimagesize($file);
  6727. if ($imsize === FALSE) {
  6728. unlink($file);
  6729. $cached_file = false;
  6730. }
  6731. }
  6732. } elseif (($w > 0) AND ($h > 0)) {
  6733. // get measures from specified data
  6734. $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
  6735. $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
  6736. $imsize = array($pw, $ph);
  6737. }
  6738. }
  6739. }
  6740. if ($imsize === FALSE) {
  6741. $this->Error('[Image] Unable to get image: '.$file);
  6742. }
  6743. // get original image width and height in pixels
  6744. list($pixw, $pixh) = $imsize;
  6745. // calculate image width and height on document
  6746. if (($w <= 0) AND ($h <= 0)) {
  6747. // convert image size to document unit
  6748. $w = $this->pixelsToUnits($pixw);
  6749. $h = $this->pixelsToUnits($pixh);
  6750. } elseif ($w <= 0) {
  6751. $w = $h * $pixw / $pixh;
  6752. } elseif ($h <= 0) {
  6753. $h = $w * $pixh / $pixw;
  6754. } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
  6755. // scale image dimensions proportionally to fit within the ($w, $h) box
  6756. if ((($w * $pixh) / ($h * $pixw)) < 1) {
  6757. $h = $w * $pixh / $pixw;
  6758. } else {
  6759. $w = $h * $pixw / $pixh;
  6760. }
  6761. }
  6762. // fit the image on available space
  6763. $this->fitBlock($w, $h, $x, $y, $fitonpage);
  6764. // calculate new minimum dimensions in pixels
  6765. $neww = round($w * $this->k * $dpi / $this->dpi);
  6766. $newh = round($h * $this->k * $dpi / $this->dpi);
  6767. // check if resize is necessary (resize is used only to reduce the image)
  6768. $newsize = ($neww * $newh);
  6769. $pixsize = ($pixw * $pixh);
  6770. if (intval($resize) == 2) {
  6771. $resize = true;
  6772. } elseif ($newsize >= $pixsize) {
  6773. $resize = false;
  6774. }
  6775. // check if image has been already added on document
  6776. $newimage = true;
  6777. if (in_array($file, $this->imagekeys)) {
  6778. $newimage = false;
  6779. // get existing image data
  6780. $info = $this->getImageBuffer($file);
  6781. // check if the newer image is larger
  6782. $oldsize = ($info['w'] * $info['h']);
  6783. if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
  6784. $newimage = true;
  6785. }
  6786. }
  6787. if ($newimage) {
  6788. //First use of image, get info
  6789. $type = strtolower($type);
  6790. if ($type == '') {
  6791. $type = $this->getImageFileType($file, $imsize);
  6792. } elseif ($type == 'jpg') {
  6793. $type = 'jpeg';
  6794. }
  6795. $mqr = $this->get_mqr();
  6796. $this->set_mqr(false);
  6797. // Specific image handlers
  6798. $mtd = '_parse'.$type;
  6799. // GD image handler function
  6800. $gdfunction = 'imagecreatefrom'.$type;
  6801. $info = false;
  6802. if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
  6803. // TCPDF image functions
  6804. $info = $this->$mtd($file);
  6805. if ($info == 'pngalpha') {
  6806. return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
  6807. }
  6808. }
  6809. if (!$info) {
  6810. if (function_exists($gdfunction)) {
  6811. // GD library
  6812. $img = $gdfunction($file);
  6813. if ($resize) {
  6814. $imgr = imagecreatetruecolor($neww, $newh);
  6815. if (($type == 'gif') OR ($type == 'png')) {
  6816. $imgr = $this->_setGDImageTransparency($imgr, $img);
  6817. }
  6818. imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
  6819. if (($type == 'gif') OR ($type == 'png')) {
  6820. $info = $this->_toPNG($imgr);
  6821. } else {
  6822. $info = $this->_toJPEG($imgr);
  6823. }
  6824. } else {
  6825. if (($type == 'gif') OR ($type == 'png')) {
  6826. $info = $this->_toPNG($img);
  6827. } else {
  6828. $info = $this->_toJPEG($img);
  6829. }
  6830. }
  6831. } elseif (extension_loaded('imagick')) {
  6832. // ImageMagick library
  6833. $img = new Imagick();
  6834. if ($type == 'SVG') {
  6835. // get SVG file content
  6836. $svgimg = file_get_contents($file);
  6837. // get width and height
  6838. $regs = array();
  6839. if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
  6840. $svgtag = $regs[1];
  6841. $tmp = array();
  6842. if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
  6843. $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
  6844. $owu = sprintf('%.3F', ($ow * $dpi / 72)).$this->pdfunit;
  6845. $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
  6846. } else {
  6847. $ow = $w;
  6848. }
  6849. $tmp = array();
  6850. if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
  6851. $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
  6852. $ohu = sprintf('%.3F', ($oh * $dpi / 72)).$this->pdfunit;
  6853. $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
  6854. } else {
  6855. $oh = $h;
  6856. }
  6857. $tmp = array();
  6858. if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
  6859. $vbw = ($ow * $this->imgscale * $this->k);
  6860. $vbh = ($oh * $this->imgscale * $this->k);
  6861. $vbox = sprintf(' viewBox="0 0 %.3F %.3F" ', $vbw, $vbh);
  6862. $svgtag = $vbox.$svgtag;
  6863. }
  6864. $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
  6865. }
  6866. $img->readImageBlob($svgimg);
  6867. } else {
  6868. $img->readImage($file);
  6869. }
  6870. if ($resize) {
  6871. $img->resizeImage($neww, $newh, 10, 1, false);
  6872. }
  6873. $img->setCompressionQuality($this->jpeg_quality);
  6874. $img->setImageFormat('jpeg');
  6875. $tempname = tempnam(K_PATH_CACHE, 'jpg_');
  6876. $img->writeImage($tempname);
  6877. $info = $this->_parsejpeg($tempname);
  6878. unlink($tempname);
  6879. $img->destroy();
  6880. } else {
  6881. return;
  6882. }
  6883. }
  6884. if ($info === false) {
  6885. //If false, we cannot process image
  6886. return;
  6887. }
  6888. $this->set_mqr($mqr);
  6889. if ($ismask) {
  6890. // force grayscale
  6891. $info['cs'] = 'DeviceGray';
  6892. }
  6893. $info['i'] = $this->numimages;
  6894. if (!in_array($file, $this->imagekeys)) {
  6895. ++$info['i'];
  6896. }
  6897. if ($imgmask !== false) {
  6898. $info['masked'] = $imgmask;
  6899. }
  6900. // add image to document
  6901. $this->setImageBuffer($file, $info);
  6902. }
  6903. if ($cached_file) {
  6904. // remove cached file
  6905. unlink($file);
  6906. }
  6907. // set alignment
  6908. $this->img_rb_y = $y + $h;
  6909. // set alignment
  6910. if ($this->rtl) {
  6911. if ($palign == 'L') {
  6912. $ximg = $this->lMargin;
  6913. } elseif ($palign == 'C') {
  6914. $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
  6915. } elseif ($palign == 'R') {
  6916. $ximg = $this->w - $this->rMargin - $w;
  6917. } else {
  6918. $ximg = $x - $w;
  6919. }
  6920. $this->img_rb_x = $ximg;
  6921. } else {
  6922. if ($palign == 'L') {
  6923. $ximg = $this->lMargin;
  6924. } elseif ($palign == 'C') {
  6925. $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
  6926. } elseif ($palign == 'R') {
  6927. $ximg = $this->w - $this->rMargin - $w;
  6928. } else {
  6929. $ximg = $x;
  6930. }
  6931. $this->img_rb_x = $ximg + $w;
  6932. }
  6933. if ($ismask OR $hidden) {
  6934. // image is not displayed
  6935. return $info['i'];
  6936. }
  6937. $xkimg = $ximg * $this->k;
  6938. $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
  6939. if (!empty($border)) {
  6940. $bx = $this->x;
  6941. $by = $this->y;
  6942. $this->x = $ximg;
  6943. if ($this->rtl) {
  6944. $this->x += $w;
  6945. }
  6946. $this->y = $y;
  6947. $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
  6948. $this->x = $bx;
  6949. $this->y = $by;
  6950. }
  6951. if ($link) {
  6952. $this->Link($ximg, $y, $w, $h, $link, 0);
  6953. }
  6954. // set pointer to align the next text/objects
  6955. switch($align) {
  6956. case 'T': {
  6957. $this->y = $y;
  6958. $this->x = $this->img_rb_x;
  6959. break;
  6960. }
  6961. case 'M': {
  6962. $this->y = $y + round($h/2);
  6963. $this->x = $this->img_rb_x;
  6964. break;
  6965. }
  6966. case 'B': {
  6967. $this->y = $this->img_rb_y;
  6968. $this->x = $this->img_rb_x;
  6969. break;
  6970. }
  6971. case 'N': {
  6972. $this->SetY($this->img_rb_y);
  6973. break;
  6974. }
  6975. default:{
  6976. break;
  6977. }
  6978. }
  6979. $this->endlinex = $this->img_rb_x;
  6980. if ($this->inxobj) {
  6981. // we are inside an XObject template
  6982. $this->xobjects[$this->xobjid]['images'][] = $info['i'];
  6983. }
  6984. return $info['i'];
  6985. }
  6986. /**
  6987. * Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtime function exist)
  6988. * @param boolean $mqr FALSE for off, TRUE for on.
  6989. * @since 4.6.025 (2009-08-17)
  6990. */
  6991. public function set_mqr($mqr) {
  6992. if(!defined('PHP_VERSION_ID')) {
  6993. $version = PHP_VERSION;
  6994. define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
  6995. }
  6996. if (PHP_VERSION_ID < 50300) {
  6997. @set_magic_quotes_runtime($mqr);
  6998. }
  6999. }
  7000. /**
  7001. * Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtime function exist)
  7002. * @return Returns 0 if magic quotes runtime is off or get_magic_quotes_runtime doesn't exist, 1 otherwise.
  7003. * @since 4.6.025 (2009-08-17)
  7004. */
  7005. public function get_mqr() {
  7006. if(!defined('PHP_VERSION_ID')) {
  7007. $version = PHP_VERSION;
  7008. define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
  7009. }
  7010. if (PHP_VERSION_ID < 50300) {
  7011. return @get_magic_quotes_runtime();
  7012. }
  7013. return 0;
  7014. }
  7015. /**
  7016. * Convert the loaded image to a JPEG and then return a structure for the PDF creator.
  7017. * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
  7018. * @param string $file Image file name.
  7019. * @param image $image Image object.
  7020. * return image JPEG image object.
  7021. * @access protected
  7022. */
  7023. protected function _toJPEG($image) {
  7024. $tempname = tempnam(K_PATH_CACHE, 'jpg_');
  7025. imagejpeg($image, $tempname, $this->jpeg_quality);
  7026. imagedestroy($image);
  7027. $retvars = $this->_parsejpeg($tempname);
  7028. // tidy up by removing temporary image
  7029. unlink($tempname);
  7030. return $retvars;
  7031. }
  7032. /**
  7033. * Convert the loaded image to a PNG and then return a structure for the PDF creator.
  7034. * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
  7035. * @param string $file Image file name.
  7036. * @param image $image Image object.
  7037. * return image PNG image object.
  7038. * @access protected
  7039. * @since 4.9.016 (2010-04-20)
  7040. */
  7041. protected function _toPNG($image) {
  7042. $tempname = tempnam(K_PATH_CACHE, 'jpg_');
  7043. imagepng($image, $tempname);
  7044. imagedestroy($image);
  7045. $retvars = $this->_parsepng($tempname);
  7046. // tidy up by removing temporary image
  7047. unlink($tempname);
  7048. return $retvars;
  7049. }
  7050. /**
  7051. * Set the transparency for the given GD image.
  7052. * @param image $new_image GD image object
  7053. * @param image $image GD image object.
  7054. * return GD image object.
  7055. * @access protected
  7056. * @since 4.9.016 (2010-04-20)
  7057. */
  7058. protected function _setGDImageTransparency($new_image, $image) {
  7059. // transparency index
  7060. $tid = imagecolortransparent($image);
  7061. // default transparency color
  7062. $tcol = array('red' => 255, 'green' => 255, 'blue' => 255);
  7063. if ($tid >= 0) {
  7064. // get the colors for the transparency index
  7065. $tcol = imagecolorsforindex($image, $tid);
  7066. }
  7067. $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']);
  7068. imagefill($new_image, 0, 0, $tid);
  7069. imagecolortransparent($new_image, $tid);
  7070. return $new_image;
  7071. }
  7072. /**
  7073. * Extract info from a JPEG file without using the GD library.
  7074. * @param string $file image file to parse
  7075. * @return array structure containing the image data
  7076. * @access protected
  7077. */
  7078. protected function _parsejpeg($file) {
  7079. $a = getimagesize($file);
  7080. if (empty($a)) {
  7081. $this->Error('Missing or incorrect image file: '.$file);
  7082. }
  7083. if ($a[2] != 2) {
  7084. $this->Error('Not a JPEG file: '.$file);
  7085. }
  7086. if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
  7087. $colspace = 'DeviceRGB';
  7088. } elseif ($a['channels'] == 4) {
  7089. $colspace = 'DeviceCMYK';
  7090. } else {
  7091. $colspace = 'DeviceGray';
  7092. }
  7093. $bpc = isset($a['bits']) ? $a['bits'] : 8;
  7094. $data = file_get_contents($file);
  7095. return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
  7096. }
  7097. /**
  7098. * Extract info from a PNG file without using the GD library.
  7099. * @param string $file image file to parse
  7100. * @return array structure containing the image data
  7101. * @access protected
  7102. */
  7103. protected function _parsepng($file) {
  7104. $f = fopen($file, 'rb');
  7105. if ($f === false) {
  7106. $this->Error('Can\'t open image file: '.$file);
  7107. }
  7108. //Check signature
  7109. if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
  7110. $this->Error('Not a PNG file: '.$file);
  7111. }
  7112. //Read header chunk
  7113. fread($f, 4);
  7114. if (fread($f, 4) != 'IHDR') {
  7115. $this->Error('Incorrect PNG file: '.$file);
  7116. }
  7117. $w = $this->_freadint($f);
  7118. $h = $this->_freadint($f);
  7119. $bpc = ord(fread($f, 1));
  7120. if ($bpc > 8) {
  7121. //$this->Error('16-bit depth not supported: '.$file);
  7122. fclose($f);
  7123. return false;
  7124. }
  7125. $ct = ord(fread($f, 1));
  7126. if ($ct == 0) {
  7127. $colspace = 'DeviceGray';
  7128. } elseif ($ct == 2) {
  7129. $colspace = 'DeviceRGB';
  7130. } elseif ($ct == 3) {
  7131. $colspace = 'Indexed';
  7132. } else {
  7133. // alpha channel
  7134. fclose($f);
  7135. return 'pngalpha';
  7136. }
  7137. if (ord(fread($f, 1)) != 0) {
  7138. //$this->Error('Unknown compression method: '.$file);
  7139. fclose($f);
  7140. return false;
  7141. }
  7142. if (ord(fread($f, 1)) != 0) {
  7143. //$this->Error('Unknown filter method: '.$file);
  7144. fclose($f);
  7145. return false;
  7146. }
  7147. if (ord(fread($f, 1)) != 0) {
  7148. //$this->Error('Interlacing not supported: '.$file);
  7149. fclose($f);
  7150. return false;
  7151. }
  7152. fread($f, 4);
  7153. $parms = '/DecodeParms << /Predictor 15 /Colors '.($ct == 2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.' >>';
  7154. //Scan chunks looking for palette, transparency and image data
  7155. $pal = '';
  7156. $trns = '';
  7157. $data = '';
  7158. do {
  7159. $n = $this->_freadint($f);
  7160. $type = fread($f, 4);
  7161. if ($type == 'PLTE') {
  7162. //Read palette
  7163. $pal = $this->rfread($f, $n);
  7164. fread($f, 4);
  7165. } elseif ($type == 'tRNS') {
  7166. //Read transparency info
  7167. $t = $this->rfread($f, $n);
  7168. if ($ct == 0) {
  7169. $trns = array(ord(substr($t, 1, 1)));
  7170. } elseif ($ct == 2) {
  7171. $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
  7172. } else {
  7173. $pos = strpos($t, chr(0));
  7174. if ($pos !== false) {
  7175. $trns = array($pos);
  7176. }
  7177. }
  7178. fread($f, 4);
  7179. } elseif ($type == 'IDAT') {
  7180. //Read image data block
  7181. $data .= $this->rfread($f, $n);
  7182. fread($f, 4);
  7183. } elseif ($type == 'IEND') {
  7184. break;
  7185. } else {
  7186. $this->rfread($f, $n + 4);
  7187. }
  7188. } while ($n);
  7189. if (($colspace == 'Indexed') AND (empty($pal))) {
  7190. //$this->Error('Missing palette in '.$file);
  7191. fclose($f);
  7192. return false;
  7193. }
  7194. fclose($f);
  7195. return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
  7196. }
  7197. /**
  7198. * Binary-safe and URL-safe file read.
  7199. * Reads up to length bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.
  7200. * @param resource $handle
  7201. * @param int $length
  7202. * @return Returns the read string or FALSE in case of error.
  7203. * @author Nicola Asuni
  7204. * @access protected
  7205. * @since 4.5.027 (2009-03-16)
  7206. */
  7207. protected function rfread($handle, $length) {
  7208. $data = fread($handle, $length);
  7209. if ($data === false) {
  7210. return false;
  7211. }
  7212. $rest = $length - strlen($data);
  7213. if ($rest > 0) {
  7214. $data .= $this->rfread($handle, $rest);
  7215. }
  7216. return $data;
  7217. }
  7218. /**
  7219. * Extract info from a PNG image with alpha channel using the GD library.
  7220. * @param string $file Name of the file containing the image.
  7221. * @param float $x Abscissa of the upper-left corner.
  7222. * @param float $y Ordinate of the upper-left corner.
  7223. * @param float $wpx Original width of the image in pixels.
  7224. * @param float $hpx original height of the image in pixels.
  7225. * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
  7226. * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
  7227. * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
  7228. * @param mixed $link URL or identifier returned by AddLink().
  7229. * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
  7230. * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
  7231. * @param int $dpi dot-per-inch resolution used on resize
  7232. * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
  7233. * @author Nicola Asuni
  7234. * @access protected
  7235. * @since 4.3.007 (2008-12-04)
  7236. * @see Image()
  7237. */
  7238. protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign) {
  7239. // create temp image file (without alpha channel)
  7240. $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
  7241. // create temp alpha file
  7242. $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
  7243. if (extension_loaded('imagick')) { // ImageMagick
  7244. // ImageMagick library
  7245. $img = new Imagick();
  7246. $img->readImage($file);
  7247. // clone image object
  7248. $imga = $img->clone();
  7249. // extract alpha channel
  7250. $img->separateImageChannel(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
  7251. $img->negateImage(true);
  7252. $img->setImageFormat('png');
  7253. $img->writeImage($tempfile_alpha);
  7254. // remove alpha channel
  7255. $imga->separateImageChannel(imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
  7256. $imga->setImageFormat('png');
  7257. $imga->writeImage($tempfile_plain);
  7258. } else { // GD library
  7259. // generate images
  7260. $img = imagecreatefrompng($file);
  7261. $imgalpha = imagecreate($wpx, $hpx);
  7262. // generate gray scale palette (0 -> 255)
  7263. for ($c = 0; $c < 256; ++$c) {
  7264. ImageColorAllocate($imgalpha, $c, $c, $c);
  7265. }
  7266. // extract alpha channel
  7267. for ($xpx = 0; $xpx < $wpx; ++$xpx) {
  7268. for ($ypx = 0; $ypx < $hpx; ++$ypx) {
  7269. $color = imagecolorat($img, $xpx, $ypx);
  7270. $alpha = ($color >> 24); // shifts off the first 24 bits (where 8x3 are used for each color), and returns the remaining 7 allocated bits (commonly used for alpha)
  7271. $alpha = (((127 - $alpha) / 127) * 255); // GD alpha is only 7 bit (0 -> 127)
  7272. $alpha = $this->getGDgamma($alpha); // correct gamma
  7273. imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
  7274. }
  7275. }
  7276. imagepng($imgalpha, $tempfile_alpha);
  7277. imagedestroy($imgalpha);
  7278. // extract image without alpha channel
  7279. $imgplain = imagecreatetruecolor($wpx, $hpx);
  7280. imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
  7281. imagepng($imgplain, $tempfile_plain);
  7282. imagedestroy($imgplain);
  7283. }
  7284. // embed mask image
  7285. $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
  7286. // embed image, masked with previously embedded mask
  7287. $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
  7288. // remove temp files
  7289. unlink($tempfile_alpha);
  7290. unlink($tempfile_plain);
  7291. }
  7292. /**
  7293. * Correct the gamma value to be used with GD library
  7294. * @param float $v the gamma value to be corrected
  7295. * @access protected
  7296. * @since 4.3.007 (2008-12-04)
  7297. */
  7298. protected function getGDgamma($v) {
  7299. return (pow(($v / 255), 2.2) * 255);
  7300. }
  7301. /**
  7302. * Performs a line break.
  7303. * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
  7304. * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
  7305. * @param boolean $cell if true add the current left (or right o for RTL) padding to the X coordinate
  7306. * @access public
  7307. * @since 1.0
  7308. * @see Cell()
  7309. */
  7310. public function Ln($h='', $cell=false) {
  7311. if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) {
  7312. // revove vertical space from the top of the column
  7313. return;
  7314. }
  7315. if ($cell) {
  7316. if ($this->rtl) {
  7317. $cellpadding = $this->cell_padding['R'];
  7318. } else {
  7319. $cellpadding = $this->cell_padding['L'];
  7320. }
  7321. } else {
  7322. $cellpadding = 0;
  7323. }
  7324. if ($this->rtl) {
  7325. $this->x = $this->w - $this->rMargin - $cellpadding;
  7326. } else {
  7327. $this->x = $this->lMargin + $cellpadding;
  7328. }
  7329. if (is_string($h)) {
  7330. $this->y += $this->lasth;
  7331. } else {
  7332. $this->y += $h;
  7333. }
  7334. $this->newline = true;
  7335. }
  7336. /**
  7337. * Returns the relative X value of current position.
  7338. * The value is relative to the left border for LTR languages and to the right border for RTL languages.
  7339. * @return float
  7340. * @access public
  7341. * @since 1.2
  7342. * @see SetX(), GetY(), SetY()
  7343. */
  7344. public function GetX() {
  7345. //Get x position
  7346. if ($this->rtl) {
  7347. return ($this->w - $this->x);
  7348. } else {
  7349. return $this->x;
  7350. }
  7351. }
  7352. /**
  7353. * Returns the absolute X value of current position.
  7354. * @return float
  7355. * @access public
  7356. * @since 1.2
  7357. * @see SetX(), GetY(), SetY()
  7358. */
  7359. public function GetAbsX() {
  7360. return $this->x;
  7361. }
  7362. /**
  7363. * Returns the ordinate of the current position.
  7364. * @return float
  7365. * @access public
  7366. * @since 1.0
  7367. * @see SetY(), GetX(), SetX()
  7368. */
  7369. public function GetY() {
  7370. return $this->y;
  7371. }
  7372. /**
  7373. * Defines the abscissa of the current position.
  7374. * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
  7375. * @param float $x The value of the abscissa.
  7376. * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
  7377. * @access public
  7378. * @since 1.2
  7379. * @see GetX(), GetY(), SetY(), SetXY()
  7380. */
  7381. public function SetX($x, $rtloff=false) {
  7382. if (!$rtloff AND $this->rtl) {
  7383. if ($x >= 0) {
  7384. $this->x = $this->w - $x;
  7385. } else {
  7386. $this->x = abs($x);
  7387. }
  7388. } else {
  7389. if ($x >= 0) {
  7390. $this->x = $x;
  7391. } else {
  7392. $this->x = $this->w + $x;
  7393. }
  7394. }
  7395. if ($this->x < 0) {
  7396. $this->x = 0;
  7397. }
  7398. if ($this->x > $this->w) {
  7399. $this->x = $this->w;
  7400. }
  7401. }
  7402. /**
  7403. * Moves the current abscissa back to the left margin and sets the ordinate.
  7404. * If the passed value is negative, it is relative to the bottom of the page.
  7405. * @param float $y The value of the ordinate.
  7406. * @param bool $resetx if true (default) reset the X position.
  7407. * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
  7408. * @access public
  7409. * @since 1.0
  7410. * @see GetX(), GetY(), SetY(), SetXY()
  7411. */
  7412. public function SetY($y, $resetx=true, $rtloff=false) {
  7413. if ($resetx) {
  7414. //reset x
  7415. if (!$rtloff AND $this->rtl) {
  7416. $this->x = $this->w - $this->rMargin;
  7417. } else {
  7418. $this->x = $this->lMargin;
  7419. }
  7420. }
  7421. if ($y >= 0) {
  7422. $this->y = $y;
  7423. } else {
  7424. $this->y = $this->h + $y;
  7425. }
  7426. if ($this->y < 0) {
  7427. $this->y = 0;
  7428. }
  7429. if ($this->y > $this->h) {
  7430. $this->y = $this->h;
  7431. }
  7432. }
  7433. /**
  7434. * Defines the abscissa and ordinate of the current position.
  7435. * If the passed values are negative, they are relative respectively to the right and bottom of the page.
  7436. * @param float $x The value of the abscissa.
  7437. * @param float $y The value of the ordinate.
  7438. * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
  7439. * @access public
  7440. * @since 1.2
  7441. * @see SetX(), SetY()
  7442. */
  7443. public function SetXY($x, $y, $rtloff=false) {
  7444. $this->SetY($y, false, $rtloff);
  7445. $this->SetX($x, $rtloff);
  7446. }
  7447. /**
  7448. * Send the document to a given destination: string, local file or browser.
  7449. * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
  7450. * The method first calls Close() if necessary to terminate the document.
  7451. * @param string $name The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
  7452. * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li></ul>
  7453. * @access public
  7454. * @since 1.0
  7455. * @see Close()
  7456. */
  7457. public function Output($name='doc.pdf', $dest='I') {
  7458. //Output PDF to some destination
  7459. //Finish document if necessary
  7460. if ($this->state < 3) {
  7461. $this->Close();
  7462. }
  7463. //Normalize parameters
  7464. if (is_bool($dest)) {
  7465. $dest = $dest ? 'D' : 'F';
  7466. }
  7467. $dest = strtoupper($dest);
  7468. if ($dest{0} != 'F') {
  7469. $name = preg_replace('/[\s]+/', '_', $name);
  7470. $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
  7471. }
  7472. if ($this->sign) {
  7473. // *** apply digital signature to the document ***
  7474. // get the document content
  7475. $pdfdoc = $this->getBuffer();
  7476. // remove last newline
  7477. $pdfdoc = substr($pdfdoc, 0, -1);
  7478. // Remove the original buffer
  7479. if (isset($this->diskcache) AND $this->diskcache) {
  7480. // remove buffer file from cache
  7481. unlink($this->buffer);
  7482. }
  7483. unset($this->buffer);
  7484. // remove filler space
  7485. $byterange_string_len = strlen($this->byterange_string);
  7486. // define the ByteRange
  7487. $byte_range = array();
  7488. $byte_range[0] = 0;
  7489. $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
  7490. $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
  7491. $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
  7492. $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
  7493. // replace the ByteRange
  7494. $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
  7495. $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
  7496. $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
  7497. // write the document to a temporary folder
  7498. $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
  7499. $f = fopen($tempdoc, 'wb');
  7500. if (!$f) {
  7501. $this->Error('Unable to create temporary file: '.$tempdoc);
  7502. }
  7503. $pdfdoc_length = strlen($pdfdoc);
  7504. fwrite($f, $pdfdoc, $pdfdoc_length);
  7505. fclose($f);
  7506. // get digital signature via openssl library
  7507. $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
  7508. if (empty($this->signature_data['extracerts'])) {
  7509. openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
  7510. } else {
  7511. 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']);
  7512. }
  7513. unlink($tempdoc);
  7514. // read signature
  7515. $signature = file_get_contents($tempsign);
  7516. unlink($tempsign);
  7517. // extract signature
  7518. $signature = substr($signature, $pdfdoc_length);
  7519. $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
  7520. $tmparr = explode("\n\n", $signature);
  7521. $signature = $tmparr[1];
  7522. unset($tmparr);
  7523. // decode signature
  7524. $signature = base64_decode(trim($signature));
  7525. // convert signature to hex
  7526. $signature = current(unpack('H*', $signature));
  7527. $signature = str_pad($signature, $this->signature_max_length, '0');
  7528. // Add signature to the document
  7529. $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
  7530. $this->diskcache = false;
  7531. $this->buffer = &$pdfdoc;
  7532. $this->bufferlen = strlen($pdfdoc);
  7533. }
  7534. switch($dest) {
  7535. case 'I': {
  7536. // Send PDF to the standard output
  7537. if (ob_get_contents()) {
  7538. $this->Error('Some data has already been output, can\'t send PDF file');
  7539. }
  7540. if (php_sapi_name() != 'cli') {
  7541. //We send to a browser
  7542. header('Content-Type: application/pdf');
  7543. if (headers_sent()) {
  7544. $this->Error('Some data has already been output to browser, can\'t send PDF file');
  7545. }
  7546. header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
  7547. header('Pragma: public');
  7548. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
  7549. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  7550. header('Content-Length: '.$this->bufferlen);
  7551. header('Content-Disposition: inline; filename="'.basename($name).'";');
  7552. }
  7553. echo $this->getBuffer();
  7554. break;
  7555. }
  7556. case 'D': {
  7557. // Download PDF as file
  7558. if (ob_get_contents()) {
  7559. $this->Error('Some data has already been output, can\'t send PDF file');
  7560. }
  7561. header('Content-Description: File Transfer');
  7562. if (headers_sent()) {
  7563. $this->Error('Some data has already been output to browser, can\'t send PDF file');
  7564. }
  7565. header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
  7566. header('Pragma: public');
  7567. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
  7568. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  7569. // force download dialog
  7570. header('Content-Type: application/force-download');
  7571. header('Content-Type: application/octet-stream', false);
  7572. header('Content-Type: application/download', false);
  7573. header('Content-Type: application/pdf', false);
  7574. // use the Content-Disposition header to supply a recommended filename
  7575. header('Content-Disposition: attachment; filename="'.basename($name).'";');
  7576. header('Content-Transfer-Encoding: binary');
  7577. header('Content-Length: '.$this->bufferlen);
  7578. echo $this->getBuffer();
  7579. break;
  7580. }
  7581. case 'F':
  7582. case 'FI':
  7583. case 'FD': {
  7584. // Save PDF to a local file
  7585. if ($this->diskcache) {
  7586. copy($this->buffer, $name);
  7587. } else {
  7588. $f = fopen($name, 'wb');
  7589. if (!$f) {
  7590. $this->Error('Unable to create output file: '.$name);
  7591. }
  7592. fwrite($f, $this->getBuffer(), $this->bufferlen);
  7593. fclose($f);
  7594. }
  7595. if ($dest == 'FI') {
  7596. // send headers to browser
  7597. header('Content-Type: application/pdf');
  7598. header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
  7599. header('Pragma: public');
  7600. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
  7601. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  7602. header('Content-Length: '.filesize($name));
  7603. header('Content-Disposition: inline; filename="'.basename($name).'";');
  7604. // send document to the browser
  7605. echo file_get_contents($name);
  7606. } elseif ($dest == 'FD') {
  7607. // send headers to browser
  7608. if (ob_get_contents()) {
  7609. $this->Error('Some data has already been output, can\'t send PDF file');
  7610. }
  7611. header('Content-Description: File Transfer');
  7612. if (headers_sent()) {
  7613. $this->Error('Some data has already been output to browser, can\'t send PDF file');
  7614. }
  7615. header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
  7616. header('Pragma: public');
  7617. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
  7618. header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
  7619. // force download dialog
  7620. header('Content-Type: application/force-download');
  7621. header('Content-Type: application/octet-stream', false);
  7622. header('Content-Type: application/download', false);
  7623. header('Content-Type: application/pdf', false);
  7624. // use the Content-Disposition header to supply a recommended filename
  7625. header('Content-Disposition: attachment; filename="'.basename($name).'";');
  7626. header('Content-Transfer-Encoding: binary');
  7627. header('Content-Length: '.filesize($name));
  7628. // send document to the browser
  7629. echo file_get_contents($name);
  7630. }
  7631. break;
  7632. }
  7633. case 'S': {
  7634. // Returns PDF as a string
  7635. return $this->getBuffer();
  7636. }
  7637. default: {
  7638. $this->Error('Incorrect output destination: '.$dest);
  7639. }
  7640. }
  7641. return '';
  7642. }
  7643. /**
  7644. * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
  7645. * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.
  7646. * @param boolean $preserve_objcopy if true preserves the objcopy variable
  7647. * @access public
  7648. * @since 4.5.016 (2009-02-24)
  7649. */
  7650. public function _destroy($destroyall=false, $preserve_objcopy=false) {
  7651. if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
  7652. // remove buffer file from cache
  7653. unlink($this->buffer);
  7654. }
  7655. foreach (array_keys(get_object_vars($this)) as $val) {
  7656. if ($destroyall OR (
  7657. ($val != 'internal_encoding')
  7658. AND ($val != 'state')
  7659. AND ($val != 'bufferlen')
  7660. AND ($val != 'buffer')
  7661. AND ($val != 'diskcache')
  7662. AND ($val != 'sign')
  7663. AND ($val != 'signature_data')
  7664. AND ($val != 'signature_max_length')
  7665. AND ($val != 'byterange_string')
  7666. )) {
  7667. if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
  7668. unset($this->$val);
  7669. }
  7670. }
  7671. }
  7672. }
  7673. /**
  7674. * Check for locale-related bug
  7675. * @access protected
  7676. */
  7677. protected function _dochecks() {
  7678. //Check for locale-related bug
  7679. if (1.1 == 1) {
  7680. $this->Error('Don\'t alter the locale before including class file');
  7681. }
  7682. //Check for decimal separator
  7683. if (sprintf('%.1F', 1.0) != '1.0') {
  7684. setlocale(LC_NUMERIC, 'C');
  7685. }
  7686. }
  7687. /**
  7688. * Return fonts path
  7689. * @return string
  7690. * @access protected
  7691. */
  7692. protected function _getfontpath() {
  7693. if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
  7694. define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
  7695. }
  7696. return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
  7697. }
  7698. /**
  7699. * Output pages.
  7700. * @access protected
  7701. */
  7702. protected function _putpages() {
  7703. $nb = $this->numpages;
  7704. if (!empty($this->AliasNbPages)) {
  7705. $nbs = $this->formatPageNumber($nb);
  7706. $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
  7707. $alias_a = $this->_escape($this->AliasNbPages);
  7708. $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
  7709. if ($this->isunicode) {
  7710. $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
  7711. $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
  7712. $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
  7713. $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
  7714. }
  7715. }
  7716. if (!empty($this->AliasNumPage)) {
  7717. $alias_pa = $this->_escape($this->AliasNumPage);
  7718. $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
  7719. if ($this->isunicode) {
  7720. $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
  7721. $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
  7722. $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
  7723. $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
  7724. }
  7725. }
  7726. $pagegroupnum = 0;
  7727. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  7728. for ($n=1; $n <= $nb; ++$n) {
  7729. $temppage = $this->getPageBuffer($n);
  7730. if (!empty($this->pagegroups)) {
  7731. if(isset($this->newpagegroup[$n])) {
  7732. $pagegroupnum = 0;
  7733. }
  7734. ++$pagegroupnum;
  7735. foreach ($this->pagegroups as $k => $v) {
  7736. // replace total pages group numbers
  7737. $vs = $this->formatPageNumber($v);
  7738. $vu = $this->UTF8ToUTF16BE($vs, false);
  7739. $alias_ga = $this->_escape($k);
  7740. $alias_gau = $this->_escape('{'.$k.'}');
  7741. if ($this->isunicode) {
  7742. $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
  7743. $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
  7744. $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
  7745. $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
  7746. }
  7747. $temppage = str_replace($alias_gau, $vu, $temppage);
  7748. if ($this->isunicode) {
  7749. $temppage = str_replace($alias_gbu, $vu, $temppage);
  7750. $temppage = str_replace($alias_gcu, $vu, $temppage);
  7751. $temppage = str_replace($alias_gb, $vs, $temppage);
  7752. $temppage = str_replace($alias_gc, $vs, $temppage);
  7753. }
  7754. $temppage = str_replace($alias_ga, $vs, $temppage);
  7755. // replace page group numbers
  7756. $pvs = $this->formatPageNumber($pagegroupnum);
  7757. $pvu = $this->UTF8ToUTF16BE($pvs, false);
  7758. $pk = str_replace('{nb', '{pnb', $k);
  7759. $alias_pga = $this->_escape($pk);
  7760. $alias_pgau = $this->_escape('{'.$pk.'}');
  7761. if ($this->isunicode) {
  7762. $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
  7763. $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
  7764. $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
  7765. $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
  7766. }
  7767. $temppage = str_replace($alias_pgau, $pvu, $temppage);
  7768. if ($this->isunicode) {
  7769. $temppage = str_replace($alias_pgbu, $pvu, $temppage);
  7770. $temppage = str_replace($alias_pgcu, $pvu, $temppage);
  7771. $temppage = str_replace($alias_pgb, $pvs, $temppage);
  7772. $temppage = str_replace($alias_pgc, $pvs, $temppage);
  7773. }
  7774. $temppage = str_replace($alias_pga, $pvs, $temppage);
  7775. }
  7776. }
  7777. if (!empty($this->AliasNbPages)) {
  7778. // replace total pages number
  7779. $temppage = str_replace($alias_au, $nbu, $temppage);
  7780. if ($this->isunicode) {
  7781. $temppage = str_replace($alias_bu, $nbu, $temppage);
  7782. $temppage = str_replace($alias_cu, $nbu, $temppage);
  7783. $temppage = str_replace($alias_b, $nbs, $temppage);
  7784. $temppage = str_replace($alias_c, $nbs, $temppage);
  7785. }
  7786. $temppage = str_replace($alias_a, $nbs, $temppage);
  7787. }
  7788. if (!empty($this->AliasNumPage)) {
  7789. // replace page number
  7790. $pnbs = $this->formatPageNumber($n);
  7791. $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
  7792. $temppage = str_replace($alias_pau, $pnbu, $temppage);
  7793. if ($this->isunicode) {
  7794. $temppage = str_replace($alias_pbu, $pnbu, $temppage);
  7795. $temppage = str_replace($alias_pcu, $pnbu, $temppage);
  7796. $temppage = str_replace($alias_pb, $pnbs, $temppage);
  7797. $temppage = str_replace($alias_pc, $pnbs, $temppage);
  7798. }
  7799. $temppage = str_replace($alias_pa, $pnbs, $temppage);
  7800. }
  7801. $temppage = str_replace($this->epsmarker, '', $temppage);
  7802. //Page
  7803. $this->page_obj_id[$n] = $this->_newobj();
  7804. $out = '<<';
  7805. $out .= ' /Type /Page';
  7806. $out .= ' /Parent 1 0 R';
  7807. $out .= ' /LastModified '.$this->_datestring();
  7808. $out .= ' /Resources 2 0 R';
  7809. $boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
  7810. foreach ($boxes as $box) {
  7811. $out .= ' /'.$box;
  7812. $out .= sprintf(' [%.2F %.2F %.2F %.2F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
  7813. }
  7814. if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
  7815. $out .= ' /BoxColorInfo <<';
  7816. foreach ($boxes as $box) {
  7817. if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
  7818. $out .= ' /'.$box.' <<';
  7819. if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
  7820. $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
  7821. $out .= ' /C [';
  7822. $out .= sprintf(' %.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
  7823. $out .= ' ]';
  7824. }
  7825. if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
  7826. $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
  7827. }
  7828. if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
  7829. $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
  7830. }
  7831. if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
  7832. $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
  7833. $out .= ' /D [';
  7834. foreach ($dashes as $dash) {
  7835. $out .= sprintf(' %.3F', ($dash * $this->k));
  7836. }
  7837. $out .= ' ]';
  7838. }
  7839. $out .= ' >>';
  7840. }
  7841. }
  7842. $out .= ' >>';
  7843. }
  7844. $out .= ' /Contents '.($this->n + 1).' 0 R';
  7845. $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
  7846. $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
  7847. if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
  7848. // page transitions
  7849. if (isset($this->pagedim[$n]['trans']['Dur'])) {
  7850. $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
  7851. }
  7852. $out .= ' /Trans <<';
  7853. $out .= ' /Type /Trans';
  7854. if (isset($this->pagedim[$n]['trans']['S'])) {
  7855. $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
  7856. }
  7857. if (isset($this->pagedim[$n]['trans']['D'])) {
  7858. $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
  7859. }
  7860. if (isset($this->pagedim[$n]['trans']['Dm'])) {
  7861. $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
  7862. }
  7863. if (isset($this->pagedim[$n]['trans']['M'])) {
  7864. $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
  7865. }
  7866. if (isset($this->pagedim[$n]['trans']['Di'])) {
  7867. $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
  7868. }
  7869. if (isset($this->pagedim[$n]['trans']['SS'])) {
  7870. $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
  7871. }
  7872. if (isset($this->pagedim[$n]['trans']['B'])) {
  7873. $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
  7874. }
  7875. $out .= ' >>';
  7876. }
  7877. $out .= $this->_getannotsrefs($n);
  7878. $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
  7879. $out .= ' >>';
  7880. $out .= "\n".'endobj';
  7881. $this->_out($out);
  7882. //Page content
  7883. $p = ($this->compress) ? gzcompress($temppage) : $temppage;
  7884. $this->_newobj();
  7885. $p = $this->_getrawstream($p);
  7886. $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
  7887. if ($this->diskcache) {
  7888. // remove temporary files
  7889. unlink($this->pages[$n]);
  7890. }
  7891. }
  7892. //Pages root
  7893. $out = $this->_getobj(1)."\n";
  7894. $out .= '<< /Type /Pages /Kids [';
  7895. foreach($this->page_obj_id as $page_obj) {
  7896. $out .= ' '.$page_obj.' 0 R';
  7897. }
  7898. $out .= ' ] /Count '.$nb.' >>';
  7899. $out .= "\n".'endobj';
  7900. $this->_out($out);
  7901. }
  7902. /**
  7903. * Output references to page annotations
  7904. * @param int $n page number
  7905. * @access protected
  7906. * @author Nicola Asuni
  7907. * @since 4.7.000 (2008-08-29)
  7908. * @deprecated
  7909. */
  7910. protected function _putannotsrefs($n) {
  7911. $this->_out($this->_getannotsrefs($n));
  7912. }
  7913. /**
  7914. * Get references to page annotations.
  7915. * @param int $n page number
  7916. * @return string
  7917. * @access protected
  7918. * @author Nicola Asuni
  7919. * @since 5.0.010 (2010-05-17)
  7920. */
  7921. protected function _getannotsrefs($n) {
  7922. if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
  7923. return '';
  7924. }
  7925. $out = ' /Annots [';
  7926. if (isset($this->PageAnnots[$n])) {
  7927. foreach ($this->PageAnnots[$n] as $key => $val) {
  7928. if (!in_array($val['n'], $this->radio_groups)) {
  7929. $out .= ' '.$val['n'].' 0 R';
  7930. }
  7931. }
  7932. // add radiobutton groups
  7933. if (isset($this->radiobutton_groups[$n])) {
  7934. foreach ($this->radiobutton_groups[$n] as $key => $data) {
  7935. if (isset($data['n'])) {
  7936. $out .= ' '.$data['n'].' 0 R';
  7937. }
  7938. }
  7939. }
  7940. }
  7941. if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
  7942. // set reference for signature object
  7943. $out .= ' '.$this->sig_obj_id.' 0 R';
  7944. }
  7945. $out .= ' ]';
  7946. return $out;
  7947. }
  7948. /**
  7949. * Output annotations objects for all pages.
  7950. * !!! THIS METHOD IS NOT YET COMPLETED !!!
  7951. * See section 12.5 of PDF 32000_2008 reference.
  7952. * @access protected
  7953. * @author Nicola Asuni
  7954. * @since 4.0.018 (2008-08-06)
  7955. */
  7956. protected function _putannotsobjs() {
  7957. // reset object counter
  7958. for ($n=1; $n <= $this->numpages; ++$n) {
  7959. if (isset($this->PageAnnots[$n])) {
  7960. // set page annotations
  7961. foreach ($this->PageAnnots[$n] as $key => $pl) {
  7962. $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
  7963. // create annotation object for grouping radiobuttons
  7964. if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
  7965. $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
  7966. $annots = '<<';
  7967. $annots .= ' /Type /Annot';
  7968. $annots .= ' /Subtype /Widget';
  7969. $annots .= ' /Rect [0 0 0 0]';
  7970. $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
  7971. $annots .= ' /FT /Btn';
  7972. $annots .= ' /Ff 49152';
  7973. $annots .= ' /Kids [';
  7974. foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
  7975. if ($key !== 'n') {
  7976. $annots .= ' '.$data['kid'].' 0 R';
  7977. if ($data['def'] !== 'Off') {
  7978. $defval = $data['def'];
  7979. }
  7980. }
  7981. }
  7982. $annots .= ' ]';
  7983. if (isset($defval)) {
  7984. $annots .= ' /V /'.$defval;
  7985. }
  7986. $annots .= ' >>';
  7987. $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
  7988. $this->form_obj_id[] = $radio_button_obj_id;
  7989. // store object id to be used on Parent entry of Kids
  7990. $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
  7991. }
  7992. $formfield = false;
  7993. $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
  7994. $a = $pl['x'] * $this->k;
  7995. $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
  7996. $c = $pl['w'] * $this->k;
  7997. $d = $pl['h'] * $this->k;
  7998. $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
  7999. // create new annotation object
  8000. $annots = '<</Type /Annot';
  8001. $annots .= ' /Subtype /'.$pl['opt']['subtype'];
  8002. $annots .= ' /Rect ['.$rect.']';
  8003. $ft = array('Btn', 'Tx', 'Ch', 'Sig');
  8004. if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
  8005. $annots .= ' /FT /'.$pl['opt']['ft'];
  8006. $formfield = true;
  8007. }
  8008. $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
  8009. $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
  8010. $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
  8011. $annots .= ' /M '.$this->_datestring($annot_obj_id);
  8012. if (isset($pl['opt']['f'])) {
  8013. $val = 0;
  8014. if (is_array($pl['opt']['f'])) {
  8015. foreach ($pl['opt']['f'] as $f) {
  8016. switch (strtolower($f)) {
  8017. case 'invisible': {
  8018. $val += 1 << 0;
  8019. break;
  8020. }
  8021. case 'hidden': {
  8022. $val += 1 << 1;
  8023. break;
  8024. }
  8025. case 'print': {
  8026. $val += 1 << 2;
  8027. break;
  8028. }
  8029. case 'nozoom': {
  8030. $val += 1 << 3;
  8031. break;
  8032. }
  8033. case 'norotate': {
  8034. $val += 1 << 4;
  8035. break;
  8036. }
  8037. case 'noview': {
  8038. $val += 1 << 5;
  8039. break;
  8040. }
  8041. case 'readonly': {
  8042. $val += 1 << 6;
  8043. break;
  8044. }
  8045. case 'locked': {
  8046. $val += 1 << 8;
  8047. break;
  8048. }
  8049. case 'togglenoview': {
  8050. $val += 1 << 9;
  8051. break;
  8052. }
  8053. case 'lockedcontents': {
  8054. $val += 1 << 10;
  8055. break;
  8056. }
  8057. default: {
  8058. break;
  8059. }
  8060. }
  8061. }
  8062. } else {
  8063. $val = intval($pl['opt']['f']);
  8064. }
  8065. $annots .= ' /F '.intval($val);
  8066. }
  8067. if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
  8068. $annots .= ' /AS /'.$pl['opt']['as'];
  8069. }
  8070. if (isset($pl['opt']['ap'])) {
  8071. // appearance stream
  8072. $annots .= ' /AP <<';
  8073. if (is_array($pl['opt']['ap'])) {
  8074. foreach ($pl['opt']['ap'] as $apmode => $apdef) {
  8075. // $apmode can be: n = normal; r = rollover; d = down;
  8076. $annots .= ' /'.strtoupper($apmode);
  8077. if (is_array($apdef)) {
  8078. $annots .= ' <<';
  8079. foreach ($apdef as $apstate => $stream) {
  8080. // reference to XObject that define the appearance for this mode-state
  8081. $apsobjid = $this->_putAPXObject($c, $d, $stream);
  8082. $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
  8083. }
  8084. $annots .= ' >>';
  8085. } else {
  8086. // reference to XObject that define the appearance for this mode
  8087. $apsobjid = $this->_putAPXObject($c, $d, $apdef);
  8088. $annots .= ' '.$apsobjid.' 0 R';
  8089. }
  8090. }
  8091. } else {
  8092. $annots .= $pl['opt']['ap'];
  8093. }
  8094. $annots .= ' >>';
  8095. }
  8096. if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
  8097. $annots .= ' /BS <<';
  8098. $annots .= ' /Type /Border';
  8099. if (isset($pl['opt']['bs']['w'])) {
  8100. $annots .= ' /W '.intval($pl['opt']['bs']['w']);
  8101. }
  8102. $bstyles = array('S', 'D', 'B', 'I', 'U');
  8103. if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
  8104. $annots .= ' /S /'.$pl['opt']['bs']['s'];
  8105. }
  8106. if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
  8107. $annots .= ' /D [';
  8108. foreach ($pl['opt']['bs']['d'] as $cord) {
  8109. $annots .= ' '.intval($cord);
  8110. }
  8111. $annots .= ']';
  8112. }
  8113. $annots .= ' >>';
  8114. } else {
  8115. $annots .= ' /Border [';
  8116. if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
  8117. $annots .= intval($pl['opt']['border'][0]).' ';
  8118. $annots .= intval($pl['opt']['border'][1]).' ';
  8119. $annots .= intval($pl['opt']['border'][2]);
  8120. if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
  8121. $annots .= ' [';
  8122. foreach ($pl['opt']['border'][3] as $dash) {
  8123. $annots .= intval($dash).' ';
  8124. }
  8125. $annots .= ']';
  8126. }
  8127. } else {
  8128. $annots .= '0 0 0';
  8129. }
  8130. $annots .= ']';
  8131. }
  8132. if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
  8133. $annots .= ' /BE <<';
  8134. $bstyles = array('S', 'C');
  8135. if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
  8136. $annots .= ' /S /'.$pl['opt']['bs']['s'];
  8137. } else {
  8138. $annots .= ' /S /S';
  8139. }
  8140. if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
  8141. $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']);
  8142. }
  8143. $annots .= '>>';
  8144. }
  8145. if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
  8146. $annots .= ' /C [';
  8147. foreach ($pl['opt']['c'] as $col) {
  8148. $col = intval($col);
  8149. $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
  8150. $annots .= sprintf(' %.4F', $color);
  8151. }
  8152. $annots .= ']';
  8153. }
  8154. //$annots .= ' /StructParent ';
  8155. //$annots .= ' /OC ';
  8156. $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
  8157. if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
  8158. // this is a markup type
  8159. if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
  8160. $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
  8161. }
  8162. //$annots .= ' /Popup ';
  8163. if (isset($pl['opt']['ca'])) {
  8164. $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca']));
  8165. }
  8166. if (isset($pl['opt']['rc'])) {
  8167. $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
  8168. }
  8169. $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id);
  8170. //$annots .= ' /IRT ';
  8171. if (isset($pl['opt']['subj'])) {
  8172. $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
  8173. }
  8174. //$annots .= ' /RT ';
  8175. //$annots .= ' /IT ';
  8176. //$annots .= ' /ExData ';
  8177. }
  8178. $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
  8179. // Annotation types
  8180. switch (strtolower($pl['opt']['subtype'])) {
  8181. case 'text': {
  8182. if (isset($pl['opt']['open'])) {
  8183. $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
  8184. }
  8185. $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
  8186. if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
  8187. $annots .= ' /Name /'.$pl['opt']['name'];
  8188. } else {
  8189. $annots .= ' /Name /Note';
  8190. }
  8191. $statemodels = array('Marked', 'Review');
  8192. if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
  8193. $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
  8194. } else {
  8195. $pl['opt']['statemodel'] = 'Marked';
  8196. $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
  8197. }
  8198. if ($pl['opt']['statemodel'] == 'Marked') {
  8199. $states = array('Accepted', 'Unmarked');
  8200. } else {
  8201. $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
  8202. }
  8203. if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
  8204. $annots .= ' /State /'.$pl['opt']['state'];
  8205. } else {
  8206. if ($pl['opt']['statemodel'] == 'Marked') {
  8207. $annots .= ' /State /Unmarked';
  8208. } else {
  8209. $annots .= ' /State /None';
  8210. }
  8211. }
  8212. break;
  8213. }
  8214. case 'link': {
  8215. if(is_string($pl['txt'])) {
  8216. // external URI link
  8217. $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
  8218. } else {
  8219. // internal link
  8220. $l = $this->links[$pl['txt']];
  8221. $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %.2F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
  8222. }
  8223. $hmodes = array('N', 'I', 'O', 'P');
  8224. if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
  8225. $annots .= ' /H /'.$pl['opt']['h'];
  8226. } else {
  8227. $annots .= ' /H /I';
  8228. }
  8229. //$annots .= ' /PA ';
  8230. //$annots .= ' /Quadpoints ';
  8231. break;
  8232. }
  8233. case 'freetext': {
  8234. if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
  8235. $annots .= ' /DA ('.$pl['opt']['da'].')';
  8236. }
  8237. if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
  8238. $annots .= ' /Q '.intval($pl['opt']['q']);
  8239. }
  8240. if (isset($pl['opt']['rc'])) {
  8241. $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
  8242. }
  8243. if (isset($pl['opt']['ds'])) {
  8244. $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
  8245. }
  8246. if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
  8247. $annots .= ' /CL [';
  8248. foreach ($pl['opt']['cl'] as $cl) {
  8249. $annots .= sprintf('%.4F ', $cl * $this->k);
  8250. }
  8251. $annots .= ']';
  8252. }
  8253. $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
  8254. if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
  8255. $annots .= ' /IT /'.$pl['opt']['it'];
  8256. }
  8257. if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
  8258. $l = $pl['opt']['rd'][0] * $this->k;
  8259. $r = $pl['opt']['rd'][1] * $this->k;
  8260. $t = $pl['opt']['rd'][2] * $this->k;
  8261. $b = $pl['opt']['rd'][3] * $this->k;
  8262. $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
  8263. }
  8264. if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
  8265. $annots .= ' /LE /'.$pl['opt']['le'];
  8266. }
  8267. break;
  8268. }
  8269. case 'line': {
  8270. break;
  8271. }
  8272. case 'square': {
  8273. break;
  8274. }
  8275. case 'circle': {
  8276. break;
  8277. }
  8278. case 'polygon': {
  8279. break;
  8280. }
  8281. case 'polyline': {
  8282. break;
  8283. }
  8284. case 'highlight': {
  8285. break;
  8286. }
  8287. case 'underline': {
  8288. break;
  8289. }
  8290. case 'squiggly': {
  8291. break;
  8292. }
  8293. case 'strikeout': {
  8294. break;
  8295. }
  8296. case 'stamp': {
  8297. break;
  8298. }
  8299. case 'caret': {
  8300. break;
  8301. }
  8302. case 'ink': {
  8303. break;
  8304. }
  8305. case 'popup': {
  8306. break;
  8307. }
  8308. case 'fileattachment': {
  8309. if (!isset($pl['opt']['fs'])) {
  8310. break;
  8311. }
  8312. $filename = basename($pl['opt']['fs']);
  8313. if (isset($this->embeddedfiles[$filename]['n'])) {
  8314. $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
  8315. $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
  8316. if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
  8317. $annots .= ' /Name /'.$pl['opt']['name'];
  8318. } else {
  8319. $annots .= ' /Name /PushPin';
  8320. }
  8321. }
  8322. break;
  8323. }
  8324. case 'sound': {
  8325. if (!isset($pl['opt']['fs'])) {
  8326. break;
  8327. }
  8328. $filename = basename($pl['opt']['fs']);
  8329. if (isset($this->embeddedfiles[$filename]['n'])) {
  8330. // ... TO BE COMPLETED ...
  8331. // /R /C /B /E /CO /CP
  8332. $annots .= ' /Sound <</Type /Filespec /F '.$this->_datastring($filename, $annot_obj_id).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
  8333. $iconsapp = array('Speaker', 'Mic');
  8334. if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
  8335. $annots .= ' /Name /'.$pl['opt']['name'];
  8336. } else {
  8337. $annots .= ' /Name /Speaker';
  8338. }
  8339. }
  8340. break;
  8341. }
  8342. case 'movie': {
  8343. break;
  8344. }
  8345. case 'widget': {
  8346. $hmode = array('N', 'I', 'O', 'P', 'T');
  8347. if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
  8348. $annots .= ' /H /'.$pl['opt']['h'];
  8349. }
  8350. if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
  8351. $annots .= ' /MK <<';
  8352. if (isset($pl['opt']['mk']['r'])) {
  8353. $annots .= ' /R '.$pl['opt']['mk']['r'];
  8354. }
  8355. if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
  8356. $annots .= ' /BC [';
  8357. foreach($pl['opt']['mk']['bc'] AS $col) {
  8358. $col = intval($col);
  8359. $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
  8360. $annots .= sprintf(' %.2F', $color);
  8361. }
  8362. $annots .= ']';
  8363. }
  8364. if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
  8365. $annots .= ' /BG [';
  8366. foreach($pl['opt']['mk']['bg'] AS $col) {
  8367. $col = intval($col);
  8368. $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
  8369. $annots .= sprintf(' %.2F', $color);
  8370. }
  8371. $annots .= ']';
  8372. }
  8373. if (isset($pl['opt']['mk']['ca'])) {
  8374. $annots .= ' /CA '.$pl['opt']['mk']['ca'];
  8375. }
  8376. if (isset($pl['opt']['mk']['rc'])) {
  8377. $annots .= ' /RC '.$pl['opt']['mk']['rc'];
  8378. }
  8379. if (isset($pl['opt']['mk']['ac'])) {
  8380. $annots .= ' /AC '.$pl['opt']['mk']['ac'];
  8381. }
  8382. if (isset($pl['opt']['mk']['i'])) {
  8383. $info = $this->getImageBuffer($pl['opt']['mk']['i']);
  8384. if ($info !== false) {
  8385. $annots .= ' /I '.$info['n'].' 0 R';
  8386. }
  8387. }
  8388. if (isset($pl['opt']['mk']['ri'])) {
  8389. $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
  8390. if ($info !== false) {
  8391. $annots .= ' /RI '.$info['n'].' 0 R';
  8392. }
  8393. }
  8394. if (isset($pl['opt']['mk']['ix'])) {
  8395. $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
  8396. if ($info !== false) {
  8397. $annots .= ' /IX '.$info['n'].' 0 R';
  8398. }
  8399. }
  8400. if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
  8401. $annots .= ' /IF <<';
  8402. $if_sw = array('A', 'B', 'S', 'N');
  8403. if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
  8404. $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
  8405. }
  8406. $if_s = array('A', 'P');
  8407. if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
  8408. $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
  8409. }
  8410. if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
  8411. $annots .= sprintf(' /A [%.2F %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
  8412. }
  8413. if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
  8414. $annots .= ' /FB true';
  8415. }
  8416. $annots .= '>>';
  8417. }
  8418. if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
  8419. $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
  8420. } else {
  8421. $annots .= ' /TP 0';
  8422. }
  8423. $annots .= '>>';
  8424. } // end MK
  8425. // --- Entries for field dictionaries ---
  8426. if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
  8427. // set parent
  8428. $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
  8429. }
  8430. if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
  8431. $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
  8432. }
  8433. if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
  8434. $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
  8435. }
  8436. if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
  8437. $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
  8438. }
  8439. if (isset($pl['opt']['ff'])) {
  8440. if (is_array($pl['opt']['ff'])) {
  8441. // array of bit settings
  8442. $flag = 0;
  8443. foreach($pl['opt']['ff'] as $val) {
  8444. $flag += 1 << ($val - 1);
  8445. }
  8446. } else {
  8447. $flag = intval($pl['opt']['ff']);
  8448. }
  8449. $annots .= ' /Ff '.$flag;
  8450. }
  8451. if (isset($pl['opt']['maxlen'])) {
  8452. $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
  8453. }
  8454. if (isset($pl['opt']['v'])) {
  8455. $annots .= ' /V';
  8456. if (is_array($pl['opt']['v'])) {
  8457. foreach ($pl['opt']['v'] AS $optval) {
  8458. if (is_float($optval)) {
  8459. $optval = sprintf('%.2F', $optval);
  8460. }
  8461. $annots .= ' '.$optval;
  8462. }
  8463. } else {
  8464. $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
  8465. }
  8466. }
  8467. if (isset($pl['opt']['dv'])) {
  8468. $annots .= ' /DV';
  8469. if (is_array($pl['opt']['dv'])) {
  8470. foreach ($pl['opt']['dv'] AS $optval) {
  8471. if (is_float($optval)) {
  8472. $optval = sprintf('%.2F', $optval);
  8473. }
  8474. $annots .= ' '.$optval;
  8475. }
  8476. } else {
  8477. $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
  8478. }
  8479. }
  8480. if (isset($pl['opt']['rv'])) {
  8481. $annots .= ' /RV';
  8482. if (is_array($pl['opt']['rv'])) {
  8483. foreach ($pl['opt']['rv'] AS $optval) {
  8484. if (is_float($optval)) {
  8485. $optval = sprintf('%.2F', $optval);
  8486. }
  8487. $annots .= ' '.$optval;
  8488. }
  8489. } else {
  8490. $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
  8491. }
  8492. }
  8493. if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
  8494. $annots .= ' /A << '.$pl['opt']['a'].' >>';
  8495. }
  8496. if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
  8497. $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
  8498. }
  8499. if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
  8500. $annots .= ' /DA ('.$pl['opt']['da'].')';
  8501. }
  8502. if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
  8503. $annots .= ' /Q '.intval($pl['opt']['q']);
  8504. }
  8505. if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
  8506. $annots .= ' /Opt [';
  8507. foreach($pl['opt']['opt'] AS $copt) {
  8508. if (is_array($copt)) {
  8509. $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
  8510. } else {
  8511. $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
  8512. }
  8513. }
  8514. $annots .= ']';
  8515. }
  8516. if (isset($pl['opt']['ti'])) {
  8517. $annots .= ' /TI '.intval($pl['opt']['ti']);
  8518. }
  8519. if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
  8520. $annots .= ' /I [';
  8521. foreach($pl['opt']['i'] AS $copt) {
  8522. $annots .= intval($copt).' ';
  8523. }
  8524. $annots .= ']';
  8525. }
  8526. break;
  8527. }
  8528. case 'screen': {
  8529. break;
  8530. }
  8531. case 'printermark': {
  8532. break;
  8533. }
  8534. case 'trapnet': {
  8535. break;
  8536. }
  8537. case 'watermark': {
  8538. break;
  8539. }
  8540. case '3d': {
  8541. break;
  8542. }
  8543. default: {
  8544. break;
  8545. }
  8546. }
  8547. $annots .= '>>';
  8548. // create new annotation object
  8549. $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
  8550. if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
  8551. // store reference of form object
  8552. $this->form_obj_id[] = $annot_obj_id;
  8553. }
  8554. }
  8555. }
  8556. } // end for each page
  8557. }
  8558. /**
  8559. * Put appearance streams XObject used to define annotation's appearance states
  8560. * @param int $w annotation width
  8561. * @param int $h annotation height
  8562. * @param string $stream appearance stream
  8563. * @return int object ID
  8564. * @access protected
  8565. * @since 4.8.001 (2009-09-09)
  8566. */
  8567. protected function _putAPXObject($w=0, $h=0, $stream='') {
  8568. $stream = trim($stream);
  8569. $out = $this->_getobj()."\n";
  8570. $this->xobjects['AX'.$this->n] = array('n' => $this->n);
  8571. $out .= '<<';
  8572. $out .= ' /Type /XObject';
  8573. $out .= ' /Subtype /Form';
  8574. $out .= ' /FormType 1';
  8575. if ($this->compress) {
  8576. $stream = gzcompress($stream);
  8577. $out .= ' /Filter /FlateDecode';
  8578. }
  8579. $rect = sprintf('%.2F %.2F', $w, $h);
  8580. $out .= ' /BBox [0 0 '.$rect.']';
  8581. $out .= ' /Matrix [1 0 0 1 0 0]';
  8582. $out .= ' /Resources <<';
  8583. $out .= ' /ProcSet [/PDF /Text]';
  8584. $out .= ' /Font <<';
  8585. foreach ($this->annotation_fonts as $fontkey => $fontid) {
  8586. $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
  8587. }
  8588. $out .= ' >>';
  8589. $out .= ' >>';
  8590. $stream = $this->_getrawstream($stream);
  8591. $out .= ' /Length '.strlen($stream);
  8592. $out .= ' >>';
  8593. $out .= ' stream'."\n".$stream."\n".'endstream';
  8594. $out .= "\n".'endobj';
  8595. $this->_out($out);
  8596. return $this->n;
  8597. }
  8598. /**
  8599. * Get ULONG from string (Big Endian 32-bit unsigned integer).
  8600. * @param string $str string from where to extract value
  8601. * @param int $offset point from where to read the data
  8602. * @return int 32 bit value
  8603. * @author Nicola Asuni
  8604. * @access protected
  8605. * @since 5.2.000 (2010-06-02)
  8606. */
  8607. protected function _getULONG(&$str, &$offset) {
  8608. $v = unpack('Ni', substr($str, $offset, 4));
  8609. $offset += 4;
  8610. return $v['i'];
  8611. }
  8612. /**
  8613. * Get USHORT from string (Big Endian 16-bit unsigned integer).
  8614. * @param string $str string from where to extract value
  8615. * @param int $offset point from where to read the data
  8616. * @return int 16 bit value
  8617. * @author Nicola Asuni
  8618. * @access protected
  8619. * @since 5.2.000 (2010-06-02)
  8620. */
  8621. protected function _getUSHORT(&$str, &$offset) {
  8622. $v = unpack('ni', substr($str, $offset, 2));
  8623. $offset += 2;
  8624. return $v['i'];
  8625. }
  8626. /**
  8627. * Get SHORT from string (Big Endian 16-bit signed integer).
  8628. * @param string $str string from where to extract value
  8629. * @param int $offset point from where to read the data
  8630. * @return int 16 bit value
  8631. * @author Nicola Asuni
  8632. * @access protected
  8633. * @since 5.2.000 (2010-06-02)
  8634. */
  8635. protected function _getSHORT(&$str, &$offset) {
  8636. $v = unpack('si', substr($str, $offset, 2));
  8637. $offset += 2;
  8638. return $v['i'];
  8639. }
  8640. /**
  8641. * Get BYTE from string (8-bit unsigned integer).
  8642. * @param string $str string from where to extract value
  8643. * @param int $offset point from where to read the data
  8644. * @return int 8 bit value
  8645. * @author Nicola Asuni
  8646. * @access protected
  8647. * @since 5.2.000 (2010-06-02)
  8648. */
  8649. protected function _getBYTE(&$str, &$offset) {
  8650. $v = unpack('Ci', substr($str, $offset, 1));
  8651. ++$offset;
  8652. return $v['i'];
  8653. }
  8654. /**
  8655. * Returns a subset of the TrueType font data without the unused glyphs.
  8656. * @param string $font TrueType font data
  8657. * @param array $subsetchars array of used characters (the glyphs to keep)
  8658. * @return string a subset of TrueType font data without the unused glyphs
  8659. * @author Nicola Asuni
  8660. * @access protected
  8661. * @since 5.2.000 (2010-06-02)
  8662. */
  8663. protected function _getTrueTypeFontSubset($font, $subsetchars) {
  8664. ksort($subsetchars);
  8665. $offset = 0; // offset position of the font data
  8666. if ($this->_getULONG($font, $offset) != 0x10000) {
  8667. // sfnt version must be 0x00010000 for TrueType version 1.0.
  8668. return $font;
  8669. }
  8670. // get number of tables
  8671. $numTables = $this->_getUSHORT($font, $offset);
  8672. // skip searchRange, entrySelector and rangeShift
  8673. $offset += 6;
  8674. // tables array
  8675. $table = array();
  8676. // for each table
  8677. for ($i = 0; $i < $numTables; ++$i) {
  8678. // get table info
  8679. $tag = substr($font, $offset, 4);
  8680. $offset += 4;
  8681. $table[$tag] = array();
  8682. $table[$tag]['checkSum'] = $this->_getULONG($font, $offset);
  8683. $table[$tag]['offset'] = $this->_getULONG($font, $offset);
  8684. $table[$tag]['length'] = $this->_getULONG($font, $offset);
  8685. }
  8686. // check magicNumber
  8687. $offset = $table['head']['offset'] + 12;
  8688. if ($this->_getULONG($font, $offset) != 0x5F0F3CF5) {
  8689. // magicNumber must be 0x5F0F3CF5
  8690. return $font;
  8691. }
  8692. // get offset mode (indexToLocFormat : 0 = short, 1 = long)
  8693. $offset = $table['head']['offset'] + 50;
  8694. $short_offset = ($this->_getSHORT($font, $offset) == 0);
  8695. // get the offsets to the locations of the glyphs in the font, relative to the beginning of the glyphData table
  8696. $indexToLoc = array();
  8697. $offset = $table['loca']['offset'];
  8698. if ($short_offset) {
  8699. // short version
  8700. $n = $table['loca']['length'] / 2; // numGlyphs + 1
  8701. for ($i = 0; $i < $n; ++$i) {
  8702. $indexToLoc[$i] = $this->_getUSHORT($font, $offset) * 2;
  8703. }
  8704. } else {
  8705. // long version
  8706. $n = $table['loca']['length'] / 4; // numGlyphs + 1
  8707. for ($i = 0; $i < $n; ++$i) {
  8708. $indexToLoc[$i] = $this->_getULONG($font, $offset);
  8709. }
  8710. }
  8711. // get glyphs indexes of chars from cmap table
  8712. $subsetglyphs = array(); // glyph IDs on key
  8713. $subsetglyphs[0] = true; // character codes that do not correspond to any glyph in the font should be mapped to glyph index 0
  8714. $offset = $table['cmap']['offset'] + 2;
  8715. $numEncodingTables = $this->_getUSHORT($font, $offset);
  8716. $encodingTables = array();
  8717. for ($i = 0; $i < $numEncodingTables; ++$i) {
  8718. $encodingTables[$i]['platformID'] = $this->_getUSHORT($font, $offset);
  8719. $encodingTables[$i]['encodingID'] = $this->_getUSHORT($font, $offset);
  8720. $encodingTables[$i]['offset'] = $this->_getULONG($font, $offset);
  8721. }
  8722. foreach ($encodingTables as $enctable) {
  8723. if (($enctable['platformID'] == 3) AND ($enctable['encodingID'] == 0)) {
  8724. $modesymbol = true;
  8725. } else {
  8726. $modesymbol = false;
  8727. }
  8728. $offset = $table['cmap']['offset'] + $enctable['offset'];
  8729. $format = $this->_getUSHORT($font, $offset);
  8730. switch ($format) {
  8731. case 0: { // Format 0: Byte encoding table
  8732. $offset += 4; // skip length and version/language
  8733. for ($k = 0; $k < 256; ++$k) {
  8734. if (isset($subsetchars[$k])) {
  8735. $g = $this->_getBYTE($font, $offset);
  8736. $subsetglyphs[$g] = $k;
  8737. } else {
  8738. ++$offset;
  8739. }
  8740. }
  8741. break;
  8742. }
  8743. case 2: { // Format 2: High-byte mapping through table
  8744. $offset += 4; // skip length and version
  8745. // to be implemented ...
  8746. break;
  8747. }
  8748. case 4: { // Format 4: Segment mapping to delta values
  8749. $length = $this->_getUSHORT($font, $offset);
  8750. $offset += 2; // skip version/language
  8751. $segCount = ($this->_getUSHORT($font, $offset) / 2);
  8752. $offset += 6; // skip searchRange, entrySelector, rangeShift
  8753. $endCount = array(); // array of end character codes for each segment
  8754. for ($k = 0; $k < $segCount; ++$k) {
  8755. $endCount[$k] = $this->_getUSHORT($font, $offset);
  8756. }
  8757. $offset += 2; // skip reservedPad
  8758. $startCount = array(); // array of start character codes for each segment
  8759. for ($k = 0; $k < $segCount; ++$k) {
  8760. $startCount[$k] = $this->_getUSHORT($font, $offset);
  8761. }
  8762. $idDelta = array(); // delta for all character codes in segment
  8763. for ($k = 0; $k < $segCount; ++$k) {
  8764. $idDelta[$k] = $this->_getUSHORT($font, $offset);
  8765. }
  8766. $idRangeOffset = array(); // Offsets into glyphIdArray or 0
  8767. for ($k = 0; $k < $segCount; ++$k) {
  8768. $idRangeOffset[$k] = $this->_getUSHORT($font, $offset);
  8769. }
  8770. $gidlen = ($length / 2) - 8 - (4 * $segCount);
  8771. $glyphIdArray = array(); // glyph index array
  8772. for ($k = 0; $k < $gidlen; ++$k) {
  8773. $glyphIdArray[$k] = $this->_getUSHORT($font, $offset);
  8774. }
  8775. for ($k = 0; $k < $segCount; ++$k) {
  8776. for ($c = $startCount[$k]; $c <= $endCount[$k]; ++$c) {
  8777. if (isset($subsetchars[$c])) {
  8778. if ($idRangeOffset[$k] == 0) {
  8779. $g = $c;
  8780. } else {
  8781. $gid = (($idRangeOffset[$k] / 2) + ($c - $startCount[$k]) - ($segCount - $k));
  8782. $g = $glyphIdArray[$gid];
  8783. }
  8784. $g += ($idDelta[$k] - 65536);
  8785. if ($g < 0) {
  8786. $g = 0;
  8787. }
  8788. $subsetglyphs[$g] = $c;
  8789. }
  8790. }
  8791. }
  8792. break;
  8793. }
  8794. case 6: { // Format 6: Trimmed table mapping
  8795. $offset += 4; // skip length and version/language
  8796. $firstCode = $this->_getUSHORT($font, $offset);
  8797. $entryCount = $this->_getUSHORT($font, $offset);
  8798. for ($k = 0; $k < $entryCount; ++$k) {
  8799. $c = ($k + $firstCode);
  8800. if (isset($subsetchars[$c])) {
  8801. $g = $this->_getUSHORT($font, $offset);
  8802. $subsetglyphs[$g] = $c;
  8803. } else {
  8804. $offset += 2;
  8805. }
  8806. }
  8807. break;
  8808. }
  8809. case 8: { // Format 8: Mixed 16-bit and 32-bit coverage
  8810. $offset += 10; // skip length and version
  8811. // to be implemented ...
  8812. break;
  8813. }
  8814. case 10: { // Format 10: Trimmed array
  8815. $offset += 10; // skip length and version/language
  8816. $startCharCode = $this->_getULONG($font, $offset);
  8817. $numChars = $this->_getULONG($font, $offset);
  8818. for ($k = 0; $k < $numChars; ++$k) {
  8819. $c = ($k + $startCharCode);
  8820. if (isset($subsetchars[$c])) {
  8821. $g = $this->_getUSHORT($font, $offset);
  8822. $subsetglyphs[$g] = $c;
  8823. } else {
  8824. $offset += 2;
  8825. }
  8826. }
  8827. break;
  8828. }
  8829. case 12: { // Format 12: Segmented coverage
  8830. $offset += 10; // skip length and version/language
  8831. $nGroups = $this->_getULONG($font, $offset);
  8832. for ($k = 0; $k < $nGroups; ++$k) {
  8833. $startCharCode = $this->_getULONG($font, $offset);
  8834. $endCharCode = $this->_getULONG($font, $offset);
  8835. $startGlyphCode = $this->_getULONG($font, $offset);
  8836. for ($c = $startCharCode; $c <= $endCharCode; ++$c) {
  8837. if (isset($subsetchars[$c])) {
  8838. $subsetglyphs[$startGlyphCode] = $c;
  8839. }
  8840. ++$startGlyphCode;
  8841. }
  8842. }
  8843. break;
  8844. }
  8845. }
  8846. }
  8847. // sort glyphs by key
  8848. ksort($subsetglyphs);
  8849. // add composite glyps to $subsetglyphs and remove missing glyphs
  8850. foreach ($subsetglyphs as $key => $val) {
  8851. if (isset($indexToLoc[$key])) {
  8852. $offset = $table['glyf']['offset'] + $indexToLoc[$key];
  8853. $numberOfContours = $this->_getSHORT($font, $offset);
  8854. if ($numberOfContours < 0) { // composite glyph
  8855. $offset += 8; // skip xMin, yMin, xMax, yMax
  8856. do {
  8857. $flags = $this->_getUSHORT($font, $offset);
  8858. $glyphIndex = $this->_getUSHORT($font, $offset);
  8859. if (!isset($subsetglyphs[$glyphIndex]) AND isset($indexToLoc[$glyphIndex])) {
  8860. // add missing glyphs
  8861. $subsetglyphs[$glyphIndex] = true;
  8862. }
  8863. // skip some bytes by case
  8864. if ($flags & 1) {
  8865. $offset += 4;
  8866. } else {
  8867. $offset += 2;
  8868. }
  8869. if ($flags & 8) {
  8870. $offset += 2;
  8871. } elseif ($flags & 64) {
  8872. $offset += 4;
  8873. } elseif ($flags & 128) {
  8874. $offset += 8;
  8875. }
  8876. } while ($flags & 32);
  8877. }
  8878. } else {
  8879. unset($subsetglyphs[$key]);
  8880. }
  8881. }
  8882. // build new glyf table with only used glyphs
  8883. $glyf = '';
  8884. $glyfSize = 0;
  8885. // create new empty indexToLoc table
  8886. $newIndexToLoc = array_fill(0, count($indexToLoc), 0);
  8887. $goffset = 0;
  8888. foreach ($subsetglyphs as $glyphID => $char) {
  8889. if (isset($indexToLoc[$glyphID]) AND isset($indexToLoc[($glyphID + 1)])) {
  8890. $start = $indexToLoc[$glyphID];
  8891. $length = ($indexToLoc[($glyphID + 1)] - $start);
  8892. $glyf .= substr($font, ($table['glyf']['offset'] + $start), $length);
  8893. $newIndexToLoc[$glyphID] = $goffset;
  8894. $goffset += $length;
  8895. }
  8896. }
  8897. // build new loca table
  8898. $loca = '';
  8899. if ($short_offset) {
  8900. foreach ($newIndexToLoc as $glyphID => $offset) {
  8901. $loca .= pack('n', ($offset / 2));
  8902. }
  8903. } else {
  8904. foreach ($newIndexToLoc as $glyphID => $offset) {
  8905. $loca .= pack('N', $offset);
  8906. }
  8907. }
  8908. // array of table names to preserve (loca and glyf tables will be added later)
  8909. //$table_names = array ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2', 'post', 'cvt ', 'fpgm', 'prep');
  8910. // the cmap table is not needed and shall not be present, since the mapping from character codes to glyph descriptions is provided separately
  8911. $table_names = array ('head', 'hhea', 'hmtx', 'maxp', 'cvt ', 'fpgm', 'prep'); // minimum required table names
  8912. // get the tables to preserve
  8913. $offset = 12;
  8914. foreach ($table as $tag => $val) {
  8915. if (in_array($tag, $table_names)) {
  8916. $table[$tag]['data'] = substr($font, $table[$tag]['offset'], $table[$tag]['length']);
  8917. if ($tag == 'head') {
  8918. // set the checkSumAdjustment to 0
  8919. $table[$tag]['data'] = substr($table[$tag]['data'], 0, 8)."\x0\x0\x0\x0".substr($table[$tag]['data'], 12);
  8920. }
  8921. $pad = 4 - ($table[$tag]['length'] % 4);
  8922. if ($pad != 4) {
  8923. // the length of a table must be a multiple of four bytes
  8924. $table[$tag]['length'] += $pad;
  8925. $table[$tag]['data'] .= str_repeat("\x0", $pad);
  8926. }
  8927. $table[$tag]['offset'] = $offset;
  8928. $offset += $table[$tag]['length'];
  8929. // check sum is not changed (so keep the following line commented)
  8930. //$table[$tag]['checkSum'] = $this->_getTTFtableChecksum($table[$tag]['data'], $table[$tag]['length']);
  8931. } else {
  8932. unset($table[$tag]);
  8933. }
  8934. }
  8935. // add loca
  8936. $table['loca']['data'] = $loca;
  8937. $table['loca']['length'] = strlen($loca);
  8938. $pad = 4 - ($table['loca']['length'] % 4);
  8939. if ($pad != 4) {
  8940. // the length of a table must be a multiple of four bytes
  8941. $table['loca']['length'] += $pad;
  8942. $table['loca']['data'] .= str_repeat("\x0", $pad);
  8943. }
  8944. $table['loca']['offset'] = $offset;
  8945. $table['loca']['checkSum'] = $this->_getTTFtableChecksum($table['loca']['data'], $table['loca']['length']);
  8946. $offset += $table['loca']['length'];
  8947. // add glyf
  8948. $table['glyf']['data'] = $glyf;
  8949. $table['glyf']['length'] = strlen($glyf);
  8950. $pad = 4 - ($table['glyf']['length'] % 4);
  8951. if ($pad != 4) {
  8952. // the length of a table must be a multiple of four bytes
  8953. $table['glyf']['length'] += $pad;
  8954. $table['glyf']['data'] .= str_repeat("\x0", $pad);
  8955. }
  8956. $table['glyf']['offset'] = $offset;
  8957. $table['glyf']['checkSum'] = $this->_getTTFtableChecksum($table['glyf']['data'], $table['glyf']['length']);
  8958. // rebuild font
  8959. $font = '';
  8960. $font .= pack('N', 0x10000); // sfnt version
  8961. $numTables = count($table);
  8962. $font .= pack('n', $numTables); // numTables
  8963. $entrySelector = floor(log($numTables, 2));
  8964. $searchRange = pow(2, $entrySelector) * 16;
  8965. $rangeShift = ($numTables * 16) - $searchRange;
  8966. $font .= pack('n', $searchRange); // searchRange
  8967. $font .= pack('n', $entrySelector); // entrySelector
  8968. $font .= pack('n', $rangeShift); // rangeShift
  8969. $offset = ($numTables * 16);
  8970. foreach ($table as $tag => $data) {
  8971. $font .= $tag; // tag
  8972. $font .= pack('N', $data['checkSum']); // checkSum
  8973. $font .= pack('N', ($data['offset'] + $offset)); // offset
  8974. $font .= pack('N', $data['length']); // length
  8975. }
  8976. foreach ($table as $data) {
  8977. $font .= $data['data'];
  8978. }
  8979. // set checkSumAdjustment on head table
  8980. $checkSumAdjustment = 0xB1B0AFBA - $this->_getTTFtableChecksum($font, strlen($font));
  8981. $font = substr($font, 0, $table['head']['offset'] + 8).pack('N', $checkSumAdjustment).substr($font, $table['head']['offset'] + 12);
  8982. return $font;
  8983. }
  8984. /**
  8985. * Returs the checksum of a TTF table.
  8986. * @param string $table table to check
  8987. * @param int $length lenght of table in bytes
  8988. * @return int checksum
  8989. * @author Nicola Asuni
  8990. * @access protected
  8991. * @since 5.2.000 (2010-06-02)
  8992. */
  8993. protected function _getTTFtableChecksum($table, $length) {
  8994. $sum = 0;
  8995. $tlen = ($length / 4);
  8996. $offset = 0;
  8997. for ($i = 0; $i < $tlen; ++$i) {
  8998. $v = unpack('Ni', substr($table, $offset, 4));
  8999. $sum += $v['i'];
  9000. $offset += 4;
  9001. }
  9002. $sum = unpack('Ni', pack('N', $sum));
  9003. return $sum['i'];
  9004. }
  9005. /**
  9006. * Outputs font widths
  9007. * @param array $font font data
  9008. * @param int $cidoffset offset for CID values
  9009. * @return PDF command string for font widths
  9010. * @author Nicola Asuni
  9011. * @access protected
  9012. * @since 4.4.000 (2008-12-07)
  9013. */
  9014. protected function _putfontwidths($font, $cidoffset=0) {
  9015. ksort($font['cw']);
  9016. $rangeid = 0;
  9017. $range = array();
  9018. $prevcid = -2;
  9019. $prevwidth = -1;
  9020. $interval = false;
  9021. // for each character
  9022. foreach ($font['cw'] as $cid => $width) {
  9023. $cid -= $cidoffset;
  9024. if ($font['subset'] AND ($cid > 255) AND (!isset($font['subsetchars'][$cid]))) {
  9025. // ignore the unused characters (font subsetting)
  9026. continue;
  9027. }
  9028. if ($width != $font['dw']) {
  9029. if ($cid == ($prevcid + 1)) {
  9030. // consecutive CID
  9031. if ($width == $prevwidth) {
  9032. if ($width == $range[$rangeid][0]) {
  9033. $range[$rangeid][] = $width;
  9034. } else {
  9035. array_pop($range[$rangeid]);
  9036. // new range
  9037. $rangeid = $prevcid;
  9038. $range[$rangeid] = array();
  9039. $range[$rangeid][] = $prevwidth;
  9040. $range[$rangeid][] = $width;
  9041. }
  9042. $interval = true;
  9043. $range[$rangeid]['interval'] = true;
  9044. } else {
  9045. if ($interval) {
  9046. // new range
  9047. $rangeid = $cid;
  9048. $range[$rangeid] = array();
  9049. $range[$rangeid][] = $width;
  9050. } else {
  9051. $range[$rangeid][] = $width;
  9052. }
  9053. $interval = false;
  9054. }
  9055. } else {
  9056. // new range
  9057. $rangeid = $cid;
  9058. $range[$rangeid] = array();
  9059. $range[$rangeid][] = $width;
  9060. $interval = false;
  9061. }
  9062. $prevcid = $cid;
  9063. $prevwidth = $width;
  9064. }
  9065. }
  9066. // optimize ranges
  9067. $prevk = -1;
  9068. $nextk = -1;
  9069. $prevint = false;
  9070. foreach ($range as $k => $ws) {
  9071. $cws = count($ws);
  9072. if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
  9073. if (isset($range[$k]['interval'])) {
  9074. unset($range[$k]['interval']);
  9075. }
  9076. $range[$prevk] = array_merge($range[$prevk], $range[$k]);
  9077. unset($range[$k]);
  9078. } else {
  9079. $prevk = $k;
  9080. }
  9081. $nextk = $k + $cws;
  9082. if (isset($ws['interval'])) {
  9083. if ($cws > 3) {
  9084. $prevint = true;
  9085. } else {
  9086. $prevint = false;
  9087. }
  9088. unset($range[$k]['interval']);
  9089. --$nextk;
  9090. } else {
  9091. $prevint = false;
  9092. }
  9093. }
  9094. // output data
  9095. $w = '';
  9096. foreach ($range as $k => $ws) {
  9097. if (count(array_count_values($ws)) == 1) {
  9098. // interval mode is more compact
  9099. $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
  9100. } else {
  9101. // range mode
  9102. $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
  9103. }
  9104. }
  9105. return '/W ['.$w.' ]';
  9106. }
  9107. /**
  9108. * Output fonts.
  9109. * @author Nicola Asuni
  9110. * @access protected
  9111. */
  9112. protected function _putfonts() {
  9113. $nf = $this->n;
  9114. foreach ($this->diffs as $diff) {
  9115. //Encodings
  9116. $this->_newobj();
  9117. $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
  9118. }
  9119. $mqr = $this->get_mqr();
  9120. $this->set_mqr(false);
  9121. foreach ($this->FontFiles as $file => $info) {
  9122. // search and get font file to embedd
  9123. $fontdir = $info['fontdir'];
  9124. $file = strtolower($file);
  9125. $fontfile = '';
  9126. // search files on various directories
  9127. if (($fontdir !== false) AND file_exists($fontdir.$file)) {
  9128. $fontfile = $fontdir.$file;
  9129. } elseif (file_exists($this->_getfontpath().$file)) {
  9130. $fontfile = $this->_getfontpath().$file;
  9131. } elseif (file_exists($file)) {
  9132. $fontfile = $file;
  9133. }
  9134. if (!$this->empty_string($fontfile)) {
  9135. $font = file_get_contents($fontfile);
  9136. $compressed = (substr($file, -2) == '.z');
  9137. if ((!$compressed) AND (isset($info['length2']))) {
  9138. $header = (ord($font{0}) == 128);
  9139. if ($header) {
  9140. //Strip first binary header
  9141. $font = substr($font, 6);
  9142. }
  9143. if ($header AND (ord($font{$info['length1']}) == 128)) {
  9144. //Strip second binary header
  9145. $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
  9146. }
  9147. } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
  9148. if ($compressed) {
  9149. // uncompress font
  9150. $font = gzuncompress($font);
  9151. }
  9152. // merge subset characters
  9153. $subsetchars = array(); // used chars
  9154. foreach ($info['fontkeys'] as $fontkey) {
  9155. $fontinfo = $this->getFontBuffer($fontkey);
  9156. $subsetchars += $fontinfo['subsetchars'];
  9157. }
  9158. $font = $this->_getTrueTypeFontSubset($font, $subsetchars);
  9159. if ($compressed) {
  9160. // recompress font
  9161. $font = gzcompress($font);
  9162. }
  9163. }
  9164. $this->_newobj();
  9165. $this->FontFiles[$file]['n'] = $this->n;
  9166. $stream = $this->_getrawstream($font);
  9167. $out = '<< /Length '.strlen($stream);
  9168. if ($compressed) {
  9169. $out .= ' /Filter /FlateDecode';
  9170. }
  9171. $out .= ' /Length1 '.$info['length1'];
  9172. if (isset($info['length2'])) {
  9173. $out .= ' /Length2 '.$info['length2'].' /Length3 0';
  9174. }
  9175. $out .= ' >>';
  9176. $out .= ' stream'."\n".$stream."\n".'endstream';
  9177. $out .= "\n".'endobj';
  9178. $this->_out($out);
  9179. }
  9180. }
  9181. $this->set_mqr($mqr);
  9182. foreach ($this->fontkeys as $k) {
  9183. //Font objects
  9184. $font = $this->getFontBuffer($k);
  9185. $type = $font['type'];
  9186. $name = $font['name'];
  9187. if ($type == 'core') {
  9188. // standard core font
  9189. $out = $this->_getobj($this->font_obj_ids[$k])."\n";
  9190. $out .= '<</Type /Font';
  9191. $out .= ' /Subtype /Type1';
  9192. $out .= ' /BaseFont /'.$name;
  9193. $out .= ' /Name /F'.$font['i'];
  9194. if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
  9195. $out .= ' /Encoding /WinAnsiEncoding';
  9196. }
  9197. if ($k == 'helvetica') {
  9198. // add default font for annotations
  9199. $this->annotation_fonts[$k] = $font['i'];
  9200. }
  9201. $out .= ' >>';
  9202. $out .= "\n".'endobj';
  9203. $this->_out($out);
  9204. } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
  9205. // additional Type1 or TrueType font
  9206. $out = $this->_getobj($this->font_obj_ids[$k])."\n";
  9207. $out .= '<</Type /Font';
  9208. $out .= ' /Subtype /'.$type;
  9209. $out .= ' /BaseFont /'.$name;
  9210. $out .= ' /Name /F'.$font['i'];
  9211. $out .= ' /FirstChar 32 /LastChar 255';
  9212. $out .= ' /Widths '.($this->n + 1).' 0 R';
  9213. $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
  9214. if ($font['enc']) {
  9215. if (isset($font['diff'])) {
  9216. $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
  9217. } else {
  9218. $out .= ' /Encoding /WinAnsiEncoding';
  9219. }
  9220. }
  9221. $out .= ' >>';
  9222. $out .= "\n".'endobj';
  9223. $this->_out($out);
  9224. // Widths
  9225. $this->_newobj();
  9226. $cw = &$font['cw'];
  9227. $s = '[';
  9228. for ($i = 32; $i < 256; ++$i) {
  9229. $s .= $cw[$i].' ';
  9230. }
  9231. $s .= ']';
  9232. $s .= "\n".'endobj';
  9233. $this->_out($s);
  9234. //Descriptor
  9235. $this->_newobj();
  9236. $s = '<</Type /FontDescriptor /FontName /'.$name;
  9237. foreach ($font['desc'] as $fdk => $fdv) {
  9238. if(is_float($fdv)) {
  9239. $fdv = sprintf('%.3F', $fdv);
  9240. }
  9241. $s .= ' /'.$fdk.' '.$fdv.'';
  9242. }
  9243. if (!$this->empty_string($font['file'])) {
  9244. $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
  9245. }
  9246. $s .= '>>';
  9247. $s .= "\n".'endobj';
  9248. $this->_out($s);
  9249. } else {
  9250. // additional types
  9251. $mtd = '_put'.strtolower($type);
  9252. if (!method_exists($this, $mtd)) {
  9253. $this->Error('Unsupported font type: '.$type);
  9254. }
  9255. $this->$mtd($font);
  9256. }
  9257. }
  9258. }
  9259. /**
  9260. * Adds unicode fonts.<br>
  9261. * Based on PDF Reference 1.3 (section 5)
  9262. * @param array $font font data
  9263. * @access protected
  9264. * @author Nicola Asuni
  9265. * @since 1.52.0.TC005 (2005-01-05)
  9266. */
  9267. protected function _puttruetypeunicode($font) {
  9268. $fontname = '';
  9269. if ($font['subset']) {
  9270. // change name for font subsetting
  9271. $subtag = sprintf('%06u', $font['i']);
  9272. $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
  9273. $fontname .= $subtag.'+';
  9274. }
  9275. $fontname .= $font['name'];
  9276. // Type0 Font
  9277. // A composite font composed of other fonts, organized hierarchically
  9278. $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
  9279. $out .= '<< /Type /Font';
  9280. $out .= ' /Subtype /Type0';
  9281. $out .= ' /BaseFont /'.$fontname;
  9282. $out .= ' /Name /F'.$font['i'];
  9283. $out .= ' /Encoding /'.$font['enc'];
  9284. $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
  9285. $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
  9286. $out .= ' >>';
  9287. $out .= "\n".'endobj';
  9288. $this->_out($out);
  9289. // ToUnicode map for Identity-H
  9290. $stream = "/CIDInit /ProcSet findresource begin\n";
  9291. $stream .= "12 dict begin\n";
  9292. $stream .= "begincmap\n";
  9293. $stream .= "/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n";
  9294. $stream .= "/CMapName /Adobe-Identity-UCS def\n";
  9295. $stream .= "/CMapType 2 def\n";
  9296. $stream .= "/WMode 0 def\n";
  9297. $stream .= "1 begincodespacerange\n";
  9298. $stream .= "<0000> <FFFF>\n";
  9299. $stream .= "endcodespacerange\n";
  9300. $stream .= "100 beginbfrange\n";
  9301. $stream .= "<0000> <00ff> <0000>\n";
  9302. $stream .= "<0100> <01ff> <0100>\n";
  9303. $stream .= "<0200> <02ff> <0200>\n";
  9304. $stream .= "<0300> <03ff> <0300>\n";
  9305. $stream .= "<0400> <04ff> <0400>\n";
  9306. $stream .= "<0500> <05ff> <0500>\n";
  9307. $stream .= "<0600> <06ff> <0600>\n";
  9308. $stream .= "<0700> <07ff> <0700>\n";
  9309. $stream .= "<0800> <08ff> <0800>\n";
  9310. $stream .= "<0900> <09ff> <0900>\n";
  9311. $stream .= "<0a00> <0aff> <0a00>\n";
  9312. $stream .= "<0b00> <0bff> <0b00>\n";
  9313. $stream .= "<0c00> <0cff> <0c00>\n";
  9314. $stream .= "<0d00> <0dff> <0d00>\n";
  9315. $stream .= "<0e00> <0eff> <0e00>\n";
  9316. $stream .= "<0f00> <0fff> <0f00>\n";
  9317. $stream .= "<1000> <10ff> <1000>\n";
  9318. $stream .= "<1100> <11ff> <1100>\n";
  9319. $stream .= "<1200> <12ff> <1200>\n";
  9320. $stream .= "<1300> <13ff> <1300>\n";
  9321. $stream .= "<1400> <14ff> <1400>\n";
  9322. $stream .= "<1500> <15ff> <1500>\n";
  9323. $stream .= "<1600> <16ff> <1600>\n";
  9324. $stream .= "<1700> <17ff> <1700>\n";
  9325. $stream .= "<1800> <18ff> <1800>\n";
  9326. $stream .= "<1900> <19ff> <1900>\n";
  9327. $stream .= "<1a00> <1aff> <1a00>\n";
  9328. $stream .= "<1b00> <1bff> <1b00>\n";
  9329. $stream .= "<1c00> <1cff> <1c00>\n";
  9330. $stream .= "<1d00> <1dff> <1d00>\n";
  9331. $stream .= "<1e00> <1eff> <1e00>\n";
  9332. $stream .= "<1f00> <1fff> <1f00>\n";
  9333. $stream .= "<2000> <20ff> <2000>\n";
  9334. $stream .= "<2100> <21ff> <2100>\n";
  9335. $stream .= "<2200> <22ff> <2200>\n";
  9336. $stream .= "<2300> <23ff> <2300>\n";
  9337. $stream .= "<2400> <24ff> <2400>\n";
  9338. $stream .= "<2500> <25ff> <2500>\n";
  9339. $stream .= "<2600> <26ff> <2600>\n";
  9340. $stream .= "<2700> <27ff> <2700>\n";
  9341. $stream .= "<2800> <28ff> <2800>\n";
  9342. $stream .= "<2900> <29ff> <2900>\n";
  9343. $stream .= "<2a00> <2aff> <2a00>\n";
  9344. $stream .= "<2b00> <2bff> <2b00>\n";
  9345. $stream .= "<2c00> <2cff> <2c00>\n";
  9346. $stream .= "<2d00> <2dff> <2d00>\n";
  9347. $stream .= "<2e00> <2eff> <2e00>\n";
  9348. $stream .= "<2f00> <2fff> <2f00>\n";
  9349. $stream .= "<3000> <30ff> <3000>\n";
  9350. $stream .= "<3100> <31ff> <3100>\n";
  9351. $stream .= "<3200> <32ff> <3200>\n";
  9352. $stream .= "<3300> <33ff> <3300>\n";
  9353. $stream .= "<3400> <34ff> <3400>\n";
  9354. $stream .= "<3500> <35ff> <3500>\n";
  9355. $stream .= "<3600> <36ff> <3600>\n";
  9356. $stream .= "<3700> <37ff> <3700>\n";
  9357. $stream .= "<3800> <38ff> <3800>\n";
  9358. $stream .= "<3900> <39ff> <3900>\n";
  9359. $stream .= "<3a00> <3aff> <3a00>\n";
  9360. $stream .= "<3b00> <3bff> <3b00>\n";
  9361. $stream .= "<3c00> <3cff> <3c00>\n";
  9362. $stream .= "<3d00> <3dff> <3d00>\n";
  9363. $stream .= "<3e00> <3eff> <3e00>\n";
  9364. $stream .= "<3f00> <3fff> <3f00>\n";
  9365. $stream .= "<4000> <40ff> <4000>\n";
  9366. $stream .= "<4100> <41ff> <4100>\n";
  9367. $stream .= "<4200> <42ff> <4200>\n";
  9368. $stream .= "<4300> <43ff> <4300>\n";
  9369. $stream .= "<4400> <44ff> <4400>\n";
  9370. $stream .= "<4500> <45ff> <4500>\n";
  9371. $stream .= "<4600> <46ff> <4600>\n";
  9372. $stream .= "<4700> <47ff> <4700>\n";
  9373. $stream .= "<4800> <48ff> <4800>\n";
  9374. $stream .= "<4900> <49ff> <4900>\n";
  9375. $stream .= "<4a00> <4aff> <4a00>\n";
  9376. $stream .= "<4b00> <4bff> <4b00>\n";
  9377. $stream .= "<4c00> <4cff> <4c00>\n";
  9378. $stream .= "<4d00> <4dff> <4d00>\n";
  9379. $stream .= "<4e00> <4eff> <4e00>\n";
  9380. $stream .= "<4f00> <4fff> <4f00>\n";
  9381. $stream .= "<5000> <50ff> <5000>\n";
  9382. $stream .= "<5100> <51ff> <5100>\n";
  9383. $stream .= "<5200> <52ff> <5200>\n";
  9384. $stream .= "<5300> <53ff> <5300>\n";
  9385. $stream .= "<5400> <54ff> <5400>\n";
  9386. $stream .= "<5500> <55ff> <5500>\n";
  9387. $stream .= "<5600> <56ff> <5600>\n";
  9388. $stream .= "<5700> <57ff> <5700>\n";
  9389. $stream .= "<5800> <58ff> <5800>\n";
  9390. $stream .= "<5900> <59ff> <5900>\n";
  9391. $stream .= "<5a00> <5aff> <5a00>\n";
  9392. $stream .= "<5b00> <5bff> <5b00>\n";
  9393. $stream .= "<5c00> <5cff> <5c00>\n";
  9394. $stream .= "<5d00> <5dff> <5d00>\n";
  9395. $stream .= "<5e00> <5eff> <5e00>\n";
  9396. $stream .= "<5f00> <5fff> <5f00>\n";
  9397. $stream .= "<6000> <60ff> <6000>\n";
  9398. $stream .= "<6100> <61ff> <6100>\n";
  9399. $stream .= "<6200> <62ff> <6200>\n";
  9400. $stream .= "<6300> <63ff> <6300>\n";
  9401. $stream .= "endbfrange\n";
  9402. $stream .= "100 beginbfrange\n";
  9403. $stream .= "<6400> <64ff> <6400>\n";
  9404. $stream .= "<6500> <65ff> <6500>\n";
  9405. $stream .= "<6600> <66ff> <6600>\n";
  9406. $stream .= "<6700> <67ff> <6700>\n";
  9407. $stream .= "<6800> <68ff> <6800>\n";
  9408. $stream .= "<6900> <69ff> <6900>\n";
  9409. $stream .= "<6a00> <6aff> <6a00>\n";
  9410. $stream .= "<6b00> <6bff> <6b00>\n";
  9411. $stream .= "<6c00> <6cff> <6c00>\n";
  9412. $stream .= "<6d00> <6dff> <6d00>\n";
  9413. $stream .= "<6e00> <6eff> <6e00>\n";
  9414. $stream .= "<6f00> <6fff> <6f00>\n";
  9415. $stream .= "<7000> <70ff> <7000>\n";
  9416. $stream .= "<7100> <71ff> <7100>\n";
  9417. $stream .= "<7200> <72ff> <7200>\n";
  9418. $stream .= "<7300> <73ff> <7300>\n";
  9419. $stream .= "<7400> <74ff> <7400>\n";
  9420. $stream .= "<7500> <75ff> <7500>\n";
  9421. $stream .= "<7600> <76ff> <7600>\n";
  9422. $stream .= "<7700> <77ff> <7700>\n";
  9423. $stream .= "<7800> <78ff> <7800>\n";
  9424. $stream .= "<7900> <79ff> <7900>\n";
  9425. $stream .= "<7a00> <7aff> <7a00>\n";
  9426. $stream .= "<7b00> <7bff> <7b00>\n";
  9427. $stream .= "<7c00> <7cff> <7c00>\n";
  9428. $stream .= "<7d00> <7dff> <7d00>\n";
  9429. $stream .= "<7e00> <7eff> <7e00>\n";
  9430. $stream .= "<7f00> <7fff> <7f00>\n";
  9431. $stream .= "<8000> <80ff> <8000>\n";
  9432. $stream .= "<8100> <81ff> <8100>\n";
  9433. $stream .= "<8200> <82ff> <8200>\n";
  9434. $stream .= "<8300> <83ff> <8300>\n";
  9435. $stream .= "<8400> <84ff> <8400>\n";
  9436. $stream .= "<8500> <85ff> <8500>\n";
  9437. $stream .= "<8600> <86ff> <8600>\n";
  9438. $stream .= "<8700> <87ff> <8700>\n";
  9439. $stream .= "<8800> <88ff> <8800>\n";
  9440. $stream .= "<8900> <89ff> <8900>\n";
  9441. $stream .= "<8a00> <8aff> <8a00>\n";
  9442. $stream .= "<8b00> <8bff> <8b00>\n";
  9443. $stream .= "<8c00> <8cff> <8c00>\n";
  9444. $stream .= "<8d00> <8dff> <8d00>\n";
  9445. $stream .= "<8e00> <8eff> <8e00>\n";
  9446. $stream .= "<8f00> <8fff> <8f00>\n";
  9447. $stream .= "<9000> <90ff> <9000>\n";
  9448. $stream .= "<9100> <91ff> <9100>\n";
  9449. $stream .= "<9200> <92ff> <9200>\n";
  9450. $stream .= "<9300> <93ff> <9300>\n";
  9451. $stream .= "<9400> <94ff> <9400>\n";
  9452. $stream .= "<9500> <95ff> <9500>\n";
  9453. $stream .= "<9600> <96ff> <9600>\n";
  9454. $stream .= "<9700> <97ff> <9700>\n";
  9455. $stream .= "<9800> <98ff> <9800>\n";
  9456. $stream .= "<9900> <99ff> <9900>\n";
  9457. $stream .= "<9a00> <9aff> <9a00>\n";
  9458. $stream .= "<9b00> <9bff> <9b00>\n";
  9459. $stream .= "<9c00> <9cff> <9c00>\n";
  9460. $stream .= "<9d00> <9dff> <9d00>\n";
  9461. $stream .= "<9e00> <9eff> <9e00>\n";
  9462. $stream .= "<9f00> <9fff> <9f00>\n";
  9463. $stream .= "<a000> <a0ff> <a000>\n";
  9464. $stream .= "<a100> <a1ff> <a100>\n";
  9465. $stream .= "<a200> <a2ff> <a200>\n";
  9466. $stream .= "<a300> <a3ff> <a300>\n";
  9467. $stream .= "<a400> <a4ff> <a400>\n";
  9468. $stream .= "<a500> <a5ff> <a500>\n";
  9469. $stream .= "<a600> <a6ff> <a600>\n";
  9470. $stream .= "<a700> <a7ff> <a700>\n";
  9471. $stream .= "<a800> <a8ff> <a800>\n";
  9472. $stream .= "<a900> <a9ff> <a900>\n";
  9473. $stream .= "<aa00> <aaff> <aa00>\n";
  9474. $stream .= "<ab00> <abff> <ab00>\n";
  9475. $stream .= "<ac00> <acff> <ac00>\n";
  9476. $stream .= "<ad00> <adff> <ad00>\n";
  9477. $stream .= "<ae00> <aeff> <ae00>\n";
  9478. $stream .= "<af00> <afff> <af00>\n";
  9479. $stream .= "<b000> <b0ff> <b000>\n";
  9480. $stream .= "<b100> <b1ff> <b100>\n";
  9481. $stream .= "<b200> <b2ff> <b200>\n";
  9482. $stream .= "<b300> <b3ff> <b300>\n";
  9483. $stream .= "<b400> <b4ff> <b400>\n";
  9484. $stream .= "<b500> <b5ff> <b500>\n";
  9485. $stream .= "<b600> <b6ff> <b600>\n";
  9486. $stream .= "<b700> <b7ff> <b700>\n";
  9487. $stream .= "<b800> <b8ff> <b800>\n";
  9488. $stream .= "<b900> <b9ff> <b900>\n";
  9489. $stream .= "<ba00> <baff> <ba00>\n";
  9490. $stream .= "<bb00> <bbff> <bb00>\n";
  9491. $stream .= "<bc00> <bcff> <bc00>\n";
  9492. $stream .= "<bd00> <bdff> <bd00>\n";
  9493. $stream .= "<be00> <beff> <be00>\n";
  9494. $stream .= "<bf00> <bfff> <bf00>\n";
  9495. $stream .= "<c000> <c0ff> <c000>\n";
  9496. $stream .= "<c100> <c1ff> <c100>\n";
  9497. $stream .= "<c200> <c2ff> <c200>\n";
  9498. $stream .= "<c300> <c3ff> <c300>\n";
  9499. $stream .= "<c400> <c4ff> <c400>\n";
  9500. $stream .= "<c500> <c5ff> <c500>\n";
  9501. $stream .= "<c600> <c6ff> <c600>\n";
  9502. $stream .= "<c700> <c7ff> <c700>\n";
  9503. $stream .= "endbfrange\n";
  9504. $stream .= "56 beginbfrange\n";
  9505. $stream .= "<c800> <c8ff> <c800>\n";
  9506. $stream .= "<c900> <c9ff> <c900>\n";
  9507. $stream .= "<ca00> <caff> <ca00>\n";
  9508. $stream .= "<cb00> <cbff> <cb00>\n";
  9509. $stream .= "<cc00> <ccff> <cc00>\n";
  9510. $stream .= "<cd00> <cdff> <cd00>\n";
  9511. $stream .= "<ce00> <ceff> <ce00>\n";
  9512. $stream .= "<cf00> <cfff> <cf00>\n";
  9513. $stream .= "<d000> <d0ff> <d000>\n";
  9514. $stream .= "<d100> <d1ff> <d100>\n";
  9515. $stream .= "<d200> <d2ff> <d200>\n";
  9516. $stream .= "<d300> <d3ff> <d300>\n";
  9517. $stream .= "<d400> <d4ff> <d400>\n";
  9518. $stream .= "<d500> <d5ff> <d500>\n";
  9519. $stream .= "<d600> <d6ff> <d600>\n";
  9520. $stream .= "<d700> <d7ff> <d700>\n";
  9521. $stream .= "<d800> <d8ff> <d800>\n";
  9522. $stream .= "<d900> <d9ff> <d900>\n";
  9523. $stream .= "<da00> <daff> <da00>\n";
  9524. $stream .= "<db00> <dbff> <db00>\n";
  9525. $stream .= "<dc00> <dcff> <dc00>\n";
  9526. $stream .= "<dd00> <ddff> <dd00>\n";
  9527. $stream .= "<de00> <deff> <de00>\n";
  9528. $stream .= "<df00> <dfff> <df00>\n";
  9529. $stream .= "<e000> <e0ff> <e000>\n";
  9530. $stream .= "<e100> <e1ff> <e100>\n";
  9531. $stream .= "<e200> <e2ff> <e200>\n";
  9532. $stream .= "<e300> <e3ff> <e300>\n";
  9533. $stream .= "<e400> <e4ff> <e400>\n";
  9534. $stream .= "<e500> <e5ff> <e500>\n";
  9535. $stream .= "<e600> <e6ff> <e600>\n";
  9536. $stream .= "<e700> <e7ff> <e700>\n";
  9537. $stream .= "<e800> <e8ff> <e800>\n";
  9538. $stream .= "<e900> <e9ff> <e900>\n";
  9539. $stream .= "<ea00> <eaff> <ea00>\n";
  9540. $stream .= "<eb00> <ebff> <eb00>\n";
  9541. $stream .= "<ec00> <ecff> <ec00>\n";
  9542. $stream .= "<ed00> <edff> <ed00>\n";
  9543. $stream .= "<ee00> <eeff> <ee00>\n";
  9544. $stream .= "<ef00> <efff> <ef00>\n";
  9545. $stream .= "<f000> <f0ff> <f000>\n";
  9546. $stream .= "<f100> <f1ff> <f100>\n";
  9547. $stream .= "<f200> <f2ff> <f200>\n";
  9548. $stream .= "<f300> <f3ff> <f300>\n";
  9549. $stream .= "<f400> <f4ff> <f400>\n";
  9550. $stream .= "<f500> <f5ff> <f500>\n";
  9551. $stream .= "<f600> <f6ff> <f600>\n";
  9552. $stream .= "<f700> <f7ff> <f700>\n";
  9553. $stream .= "<f800> <f8ff> <f800>\n";
  9554. $stream .= "<f900> <f9ff> <f900>\n";
  9555. $stream .= "<fa00> <faff> <fa00>\n";
  9556. $stream .= "<fb00> <fbff> <fb00>\n";
  9557. $stream .= "<fc00> <fcff> <fc00>\n";
  9558. $stream .= "<fd00> <fdff> <fd00>\n";
  9559. $stream .= "<fe00> <feff> <fe00>\n";
  9560. $stream .= "<ff00> <ffff> <ff00>\n";
  9561. $stream .= "endbfrange\n";
  9562. $stream .= "endcmap\n";
  9563. $stream .= "CMapName currentdict /CMap defineresource pop\n";
  9564. $stream .= "end\n";
  9565. $stream .= "end";
  9566. // ToUnicode Object
  9567. $this->_newobj();
  9568. $stream = ($this->compress) ? gzcompress($stream) : $stream;
  9569. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  9570. $stream = $this->_getrawstream($stream);
  9571. $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
  9572. // CIDFontType2
  9573. // A CIDFont whose glyph descriptions are based on TrueType font technology
  9574. $oid = $this->_newobj();
  9575. $out = '<< /Type /Font';
  9576. $out .= ' /Subtype /CIDFontType2';
  9577. $out .= ' /BaseFont /'.$fontname;
  9578. // A dictionary containing entries that define the character collection of the CIDFont.
  9579. $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
  9580. $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
  9581. $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
  9582. $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
  9583. $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
  9584. $out .= ' /DW '.$font['dw']; // default width
  9585. $out .= "\n".$this->_putfontwidths($font, 0);
  9586. if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
  9587. $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
  9588. }
  9589. $out .= ' >>';
  9590. $out .= "\n".'endobj';
  9591. $this->_out($out);
  9592. // Font descriptor
  9593. // A font descriptor describing the CIDFont default metrics other than its glyph widths
  9594. $this->_newobj();
  9595. $out = '<< /Type /FontDescriptor';
  9596. $out .= ' /FontName /'.$fontname;
  9597. foreach ($font['desc'] as $key => $value) {
  9598. if(is_float($value)) {
  9599. $value = sprintf('%.3F', $value);
  9600. }
  9601. $out .= ' /'.$key.' '.$value;
  9602. }
  9603. $fontdir = false;
  9604. if (!$this->empty_string($font['file'])) {
  9605. // A stream containing a TrueType font
  9606. $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
  9607. $fontdir = $this->FontFiles[$font['file']]['fontdir'];
  9608. }
  9609. $out .= ' >>';
  9610. $out .= "\n".'endobj';
  9611. $this->_out($out);
  9612. if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
  9613. $this->_newobj();
  9614. // Embed CIDToGIDMap
  9615. // A specification of the mapping from CIDs to glyph indices
  9616. // search and get CTG font file to embedd
  9617. $ctgfile = strtolower($font['ctg']);
  9618. // search and get ctg font file to embedd
  9619. $fontfile = '';
  9620. // search files on various directories
  9621. if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
  9622. $fontfile = $fontdir.$ctgfile;
  9623. } elseif (file_exists($this->_getfontpath().$ctgfile)) {
  9624. $fontfile = $this->_getfontpath().$ctgfile;
  9625. } elseif (file_exists($ctgfile)) {
  9626. $fontfile = $ctgfile;
  9627. }
  9628. if ($this->empty_string($fontfile)) {
  9629. $this->Error('Font file not found: '.$ctgfile);
  9630. }
  9631. $stream = $this->_getrawstream(file_get_contents($fontfile));
  9632. $out = '<< /Length '.strlen($stream).'';
  9633. if (substr($fontfile, -2) == '.z') { // check file extension
  9634. // Decompresses data encoded using the public-domain
  9635. // zlib/deflate compression method, reproducing the
  9636. // original text or binary data
  9637. $out .= ' /Filter /FlateDecode';
  9638. }
  9639. $out .= ' >>';
  9640. $out .= ' stream'."\n".$stream."\n".'endstream';
  9641. $out .= "\n".'endobj';
  9642. $this->_out($out);
  9643. }
  9644. }
  9645. /**
  9646. * Output CID-0 fonts.
  9647. * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
  9648. * @param array $font font data
  9649. * @access protected
  9650. * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
  9651. * @since 3.2.000 (2008-06-23)
  9652. */
  9653. protected function _putcidfont0($font) {
  9654. $cidoffset = 0;
  9655. if (!isset($font['cw'][1])) {
  9656. $cidoffset = 31;
  9657. }
  9658. if (isset($font['cidinfo']['uni2cid'])) {
  9659. // convert unicode to cid.
  9660. $uni2cid = $font['cidinfo']['uni2cid'];
  9661. $cw = array();
  9662. foreach ($font['cw'] as $uni => $width) {
  9663. if (isset($uni2cid[$uni])) {
  9664. $cw[($uni2cid[$uni] + $cidoffset)] = $width;
  9665. } elseif ($uni < 256) {
  9666. $cw[$uni] = $width;
  9667. } // else unknown character
  9668. }
  9669. $font = array_merge($font, array('cw' => $cw));
  9670. }
  9671. $name = $font['name'];
  9672. $enc = $font['enc'];
  9673. if ($enc) {
  9674. $longname = $name.'-'.$enc;
  9675. } else {
  9676. $longname = $name;
  9677. }
  9678. $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
  9679. $out .= '<</Type /Font';
  9680. $out .= ' /Subtype /Type0';
  9681. $out .= ' /BaseFont /'.$longname;
  9682. $out .= ' /Name /F'.$font['i'];
  9683. if ($enc) {
  9684. $out .= ' /Encoding /'.$enc;
  9685. }
  9686. $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
  9687. $out .= ' >>';
  9688. $out .= "\n".'endobj';
  9689. $this->_out($out);
  9690. $oid = $this->_newobj();
  9691. $out = '<</Type /Font';
  9692. $out .= ' /Subtype /CIDFontType0';
  9693. $out .= ' /BaseFont /'.$name;
  9694. $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
  9695. $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
  9696. $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
  9697. $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
  9698. $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
  9699. $out .= ' /DW '.$font['dw'];
  9700. $out .= "\n".$this->_putfontwidths($font, $cidoffset);
  9701. $out .= ' >>';
  9702. $out .= "\n".'endobj';
  9703. $this->_out($out);
  9704. $this->_newobj();
  9705. $s = '<</Type /FontDescriptor /FontName /'.$name;
  9706. foreach ($font['desc'] as $k => $v) {
  9707. if ($k != 'Style') {
  9708. if(is_float($v)) {
  9709. $v = sprintf('%.3F', $v);
  9710. }
  9711. $s .= ' /'.$k.' '.$v.'';
  9712. }
  9713. }
  9714. $s .= '>>';
  9715. $s .= "\n".'endobj';
  9716. $this->_out($s);
  9717. }
  9718. /**
  9719. * Output images.
  9720. * @access protected
  9721. */
  9722. protected function _putimages() {
  9723. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  9724. foreach ($this->imagekeys as $file) {
  9725. $info = $this->getImageBuffer($file);
  9726. $oid = $this->_newobj();
  9727. $this->xobjects['I'.$info['i']] = array('n' => $oid);
  9728. $this->setImageSubBuffer($file, 'n', $this->n);
  9729. $out = '<</Type /XObject';
  9730. $out .= ' /Subtype /Image';
  9731. $out .= ' /Width '.$info['w'];
  9732. $out .= ' /Height '.$info['h'];
  9733. if (array_key_exists('masked', $info)) {
  9734. $out .= ' /SMask '.($this->n - 1).' 0 R';
  9735. }
  9736. if ($info['cs'] == 'Indexed') {
  9737. $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
  9738. } else {
  9739. $out .= ' /ColorSpace /'.$info['cs'];
  9740. if ($info['cs'] == 'DeviceCMYK') {
  9741. $out .= ' /Decode [1 0 1 0 1 0 1 0]';
  9742. }
  9743. }
  9744. $out .= ' /BitsPerComponent '.$info['bpc'];
  9745. if (isset($info['f'])) {
  9746. $out .= ' /Filter /'.$info['f'];
  9747. }
  9748. if (isset($info['parms'])) {
  9749. $out .= ' '.$info['parms'];
  9750. }
  9751. if (isset($info['trns']) AND is_array($info['trns'])) {
  9752. $trns='';
  9753. $count_info = count($info['trns']);
  9754. for ($i=0; $i < $count_info; ++$i) {
  9755. $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
  9756. }
  9757. $out .= ' /Mask ['.$trns.']';
  9758. }
  9759. $stream = $this->_getrawstream($info['data']);
  9760. $out .= ' /Length '.strlen($stream).' >>';
  9761. $out .= ' stream'."\n".$stream."\n".'endstream';
  9762. $out .= "\n".'endobj';
  9763. $this->_out($out);
  9764. //Palette
  9765. if ($info['cs'] == 'Indexed') {
  9766. $this->_newobj();
  9767. $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
  9768. $pal = $this->_getrawstream($pal);
  9769. $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
  9770. }
  9771. }
  9772. }
  9773. /**
  9774. * Output Form XObjects Templates.
  9775. * @author Nicola Asuni
  9776. * @since 5.8.017 (2010-08-24)
  9777. * @access protected
  9778. * @see startTemplate(), endTemplate(), printTemplate()
  9779. */
  9780. protected function _putxobjects() {
  9781. foreach ($this->xobjects as $key => $data) {
  9782. if (isset($data['outdata'])) {
  9783. $stream = trim($data['outdata']);
  9784. $out = $this->_getobj($data['n'])."\n";
  9785. $out .= '<<';
  9786. $out .= ' /Type /XObject';
  9787. $out .= ' /Subtype /Form';
  9788. $out .= ' /FormType 1';
  9789. if ($this->compress) {
  9790. $stream = gzcompress($stream);
  9791. $out .= ' /Filter /FlateDecode';
  9792. }
  9793. $out .= sprintf(' /BBox [%.2F %.2F %.2F %.2F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
  9794. $out .= ' /Matrix [1 0 0 1 0 0]';
  9795. $out .= ' /Resources <<';
  9796. $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
  9797. // fonts
  9798. if (!empty($data['fonts'])) {
  9799. $out .= ' /Font <<';
  9800. foreach ($data['fonts'] as $fontkey => $fontid) {
  9801. $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
  9802. }
  9803. $out .= ' >>';
  9804. }
  9805. // images or nested xobjects
  9806. if (!empty($data['images']) OR !empty($data['xobjects'])) {
  9807. $out .= ' /XObject <<';
  9808. foreach ($data['images'] as $imgid) {
  9809. $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
  9810. }
  9811. foreach ($data['xobjects'] as $sub_id => $sub_objid) {
  9812. $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
  9813. }
  9814. $out .= ' >>';
  9815. }
  9816. $out .= ' >>';
  9817. $stream = $this->_getrawstream($stream);
  9818. $out .= ' /Length '.strlen($stream);
  9819. $out .= ' >>';
  9820. $out .= ' stream'."\n".$stream."\n".'endstream';
  9821. $out .= "\n".'endobj';
  9822. $this->_out($out);
  9823. }
  9824. }
  9825. }
  9826. /**
  9827. * Output Spot Colors Resources.
  9828. * @access protected
  9829. * @since 4.0.024 (2008-09-12)
  9830. */
  9831. protected function _putspotcolors() {
  9832. foreach ($this->spot_colors as $name => $color) {
  9833. $this->_newobj();
  9834. $this->spot_colors[$name]['n'] = $this->n;
  9835. $out = '[/Separation /'.str_replace(' ', '#20', $name);
  9836. $out .= ' /DeviceCMYK <<';
  9837. $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
  9838. $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100);
  9839. $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
  9840. $out .= "\n".'endobj';
  9841. $this->_out($out);
  9842. }
  9843. }
  9844. /**
  9845. * Return XObjects Dictionary.
  9846. * @return string XObjects dictionary
  9847. * @access protected
  9848. * @since 5.8.014 (2010-08-23)
  9849. */
  9850. protected function _getxobjectdict() {
  9851. $out = '';
  9852. foreach ($this->xobjects as $id => $objid) {
  9853. $out .= ' /'.$id.' '.$objid['n'].' 0 R';
  9854. }
  9855. return $out;
  9856. }
  9857. /**
  9858. * Output Resources Dictionary.
  9859. * @access protected
  9860. */
  9861. protected function _putresourcedict() {
  9862. $out = $this->_getobj(2)."\n";
  9863. $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
  9864. $out .= ' /Font <<';
  9865. foreach ($this->fontkeys as $fontkey) {
  9866. $font = $this->getFontBuffer($fontkey);
  9867. $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
  9868. }
  9869. $out .= ' >>';
  9870. $out .= ' /XObject <<';
  9871. $out .= $this->_getxobjectdict();
  9872. $out .= ' >>';
  9873. // visibility
  9874. $out .= ' /Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>';
  9875. // transparency
  9876. $out .= ' /ExtGState <<';
  9877. foreach ($this->extgstates as $k => $extgstate) {
  9878. if (isset($extgstate['name'])) {
  9879. $out .= ' /'.$extgstate['name'];
  9880. } else {
  9881. $out .= ' /GS'.$k;
  9882. }
  9883. $out .= ' '.$extgstate['n'].' 0 R';
  9884. }
  9885. $out .= ' >>';
  9886. // gradient patterns
  9887. if (isset($this->gradients) AND (count($this->gradients) > 0)) {
  9888. $out .= ' /Pattern <<';
  9889. foreach ($this->gradients as $id => $grad) {
  9890. $out .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
  9891. }
  9892. $out .= ' >>';
  9893. }
  9894. // gradient shadings
  9895. if (isset($this->gradients) AND (count($this->gradients) > 0)) {
  9896. $out .= ' /Shading <<';
  9897. foreach ($this->gradients as $id => $grad) {
  9898. $out .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
  9899. }
  9900. $out .= ' >>';
  9901. }
  9902. // spot colors
  9903. if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
  9904. $out .= ' /ColorSpace <<';
  9905. foreach ($this->spot_colors as $color) {
  9906. $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
  9907. }
  9908. $out .= ' >>';
  9909. }
  9910. $out .= ' >>';
  9911. $out .= "\n".'endobj';
  9912. $this->_out($out);
  9913. }
  9914. /**
  9915. * Output Resources.
  9916. * @access protected
  9917. */
  9918. protected function _putresources() {
  9919. $this->_putextgstates();
  9920. $this->_putocg();
  9921. $this->_putfonts();
  9922. $this->_putimages();
  9923. $this->_putxobjects();
  9924. $this->_putspotcolors();
  9925. $this->_putshaders();
  9926. $this->_putresourcedict();
  9927. $this->_putbookmarks();
  9928. $this->_putEmbeddedFiles();
  9929. $this->_putannotsobjs();
  9930. $this->_putjavascript();
  9931. $this->_putencryption();
  9932. }
  9933. /**
  9934. * Adds some Metadata information (Document Information Dictionary)
  9935. * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
  9936. * @return int object id
  9937. * @access protected
  9938. */
  9939. protected function _putinfo() {
  9940. $oid = $this->_newobj();
  9941. $out = '<<';
  9942. if (!$this->empty_string($this->title)) {
  9943. // The document's title.
  9944. $out .= ' /Title '.$this->_textstring($this->title, $oid);
  9945. }
  9946. if (!$this->empty_string($this->author)) {
  9947. // The name of the person who created the document.
  9948. $out .= ' /Author '.$this->_textstring($this->author, $oid);
  9949. }
  9950. if (!$this->empty_string($this->subject)) {
  9951. // The subject of the document.
  9952. $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
  9953. }
  9954. if (!$this->empty_string($this->keywords)) {
  9955. // Keywords associated with the document.
  9956. $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCP'.'DF', $oid);
  9957. }
  9958. if (!$this->empty_string($this->creator)) {
  9959. // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
  9960. $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
  9961. }
  9962. if (defined('PDF_PRODUCER')) {
  9963. // If the document was converted to PDF from another format, the name of the conforming product that converted it to PDF.
  9964. $out .= ' /Producer '.$this->_textstring(PDF_PRODUCER.' (TCP'.'DF)', $oid);
  9965. } else {
  9966. // default producer
  9967. $out .= ' /Producer '.$this->_textstring('TCP'.'DF', $oid);
  9968. }
  9969. // The date and time the document was created, in human-readable form
  9970. $out .= ' /CreationDate '.$this->_datestring();
  9971. // The date and time the document was most recently modified, in human-readable form
  9972. $out .= ' /ModDate '.$this->_datestring();
  9973. // A name object indicating whether the document has been modified to include trapping information
  9974. $out .= ' /Trapped /False';
  9975. $out .= ' >>';
  9976. $out .= "\n".'endobj';
  9977. $this->_out($out);
  9978. return $oid;
  9979. }
  9980. /**
  9981. * Output Catalog.
  9982. * @return int object id
  9983. * @access protected
  9984. */
  9985. protected function _putcatalog() {
  9986. $oid = $this->_newobj();
  9987. $out = '<< /Type /Catalog';
  9988. $out .= ' /Pages 1 0 R';
  9989. if ($this->ZoomMode == 'fullpage') {
  9990. $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
  9991. } elseif ($this->ZoomMode == 'fullwidth') {
  9992. $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
  9993. } elseif ($this->ZoomMode == 'real') {
  9994. $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
  9995. } elseif (!is_string($this->ZoomMode)) {
  9996. $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %.2F]',($this->ZoomMode / 100));
  9997. }
  9998. if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
  9999. $out .= ' /PageLayout /'.$this->LayoutMode;
  10000. }
  10001. if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
  10002. $out .= ' /PageMode /'.$this->PageMode;
  10003. }
  10004. if (isset($this->l['a_meta_language'])) {
  10005. $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
  10006. }
  10007. $out .= ' /Names <<';
  10008. if ((!empty($this->javascript)) OR (!empty($this->js_objects))) {
  10009. $out .= ' /JavaScript '.($this->n_js).' 0 R';
  10010. }
  10011. $out .= ' >>';
  10012. if (count($this->outlines) > 0) {
  10013. $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
  10014. $out .= ' /PageMode /UseOutlines';
  10015. }
  10016. $out .= ' '.$this->_putviewerpreferences();
  10017. $p = $this->n_ocg_print.' 0 R';
  10018. $v = $this->n_ocg_view.' 0 R';
  10019. $as = '<< /Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print] >> << /Event /View /OCGs ['.$p.' '.$v.'] /Category [/View] >>';
  10020. $out .= ' /OCProperties << /OCGs ['.$p.' '.$v.'] /D << /ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.'] >> >>';
  10021. // AcroForm
  10022. if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
  10023. $out .= ' /AcroForm <<';
  10024. $objrefs = '';
  10025. if ($this->sign AND isset($this->signature_data['cert_type'])) {
  10026. $objrefs .= $this->sig_obj_id.' 0 R';
  10027. }
  10028. if (!empty($this->form_obj_id)) {
  10029. foreach($this->form_obj_id as $objid) {
  10030. $objrefs .= ' '.$objid.' 0 R';
  10031. }
  10032. }
  10033. $out .= ' /Fields ['.$objrefs.']';
  10034. if (!empty($this->form_obj_id) AND !$this->sign) {
  10035. // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
  10036. $out .= ' /NeedAppearances true';
  10037. }
  10038. if ($this->sign AND isset($this->signature_data['cert_type'])) {
  10039. if ($this->signature_data['cert_type'] > 0) {
  10040. $out .= ' /SigFlags 3';
  10041. } else {
  10042. $out .= ' /SigFlags 1';
  10043. }
  10044. }
  10045. //$out .= ' /CO ';
  10046. if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
  10047. $out .= ' /DR <<';
  10048. $out .= ' /Font <<';
  10049. foreach ($this->annotation_fonts as $fontkey => $fontid) {
  10050. $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
  10051. }
  10052. $out .= ' >> >>';
  10053. }
  10054. $font = $this->getFontBuffer('helvetica');
  10055. $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
  10056. $out .= ' /Q '.(($this->rtl)?'2':'0');
  10057. //$out .= ' /XFA ';
  10058. $out .= ' >>';
  10059. // signatures
  10060. if ($this->sign AND isset($this->signature_data['cert_type'])) {
  10061. if ($this->signature_data['cert_type'] > 0) {
  10062. $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
  10063. } else {
  10064. $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
  10065. }
  10066. }
  10067. }
  10068. $out .= ' >>';
  10069. $out .= "\n".'endobj';
  10070. $this->_out($out);
  10071. return $oid;
  10072. }
  10073. /**
  10074. * Output viewer preferences.
  10075. * @return string for viewer preferences
  10076. * @author Nicola asuni
  10077. * @since 3.1.000 (2008-06-09)
  10078. * @access protected
  10079. */
  10080. protected function _putviewerpreferences() {
  10081. $out = '/ViewerPreferences <<';
  10082. if ($this->rtl) {
  10083. $out .= ' /Direction /R2L';
  10084. } else {
  10085. $out .= ' /Direction /L2R';
  10086. }
  10087. if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
  10088. $out .= ' /HideToolbar true';
  10089. }
  10090. if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
  10091. $out .= ' /HideMenubar true';
  10092. }
  10093. if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
  10094. $out .= ' /HideWindowUI true';
  10095. }
  10096. if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
  10097. $out .= ' /FitWindow true';
  10098. }
  10099. if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
  10100. $out .= ' /CenterWindow true';
  10101. }
  10102. if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
  10103. $out .= ' /DisplayDocTitle true';
  10104. }
  10105. if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
  10106. $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'];
  10107. }
  10108. if (isset($this->viewer_preferences['ViewArea'])) {
  10109. $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea'];
  10110. }
  10111. if (isset($this->viewer_preferences['ViewClip'])) {
  10112. $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip'];
  10113. }
  10114. if (isset($this->viewer_preferences['PrintArea'])) {
  10115. $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea'];
  10116. }
  10117. if (isset($this->viewer_preferences['PrintClip'])) {
  10118. $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip'];
  10119. }
  10120. if (isset($this->viewer_preferences['PrintScaling'])) {
  10121. $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling'];
  10122. }
  10123. if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
  10124. $out .= ' /Duplex /'.$this->viewer_preferences['Duplex'];
  10125. }
  10126. if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
  10127. if ($this->viewer_preferences['PickTrayByPDFSize']) {
  10128. $out .= ' /PickTrayByPDFSize true';
  10129. } else {
  10130. $out .= ' /PickTrayByPDFSize false';
  10131. }
  10132. }
  10133. if (isset($this->viewer_preferences['PrintPageRange'])) {
  10134. $PrintPageRangeNum = '';
  10135. foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
  10136. $PrintPageRangeNum .= ' '.($v - 1).'';
  10137. }
  10138. $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
  10139. }
  10140. if (isset($this->viewer_preferences['NumCopies'])) {
  10141. $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']);
  10142. }
  10143. $out .= ' >>';
  10144. return $out;
  10145. }
  10146. /**
  10147. * Output PDF header.
  10148. * @access protected
  10149. */
  10150. protected function _putheader() {
  10151. $this->_out('%PDF-'.$this->PDFVersion);
  10152. }
  10153. /**
  10154. * Output end of document (EOF).
  10155. * @access protected
  10156. */
  10157. protected function _enddoc() {
  10158. $this->state = 1;
  10159. $this->_putheader();
  10160. $this->_putpages();
  10161. $this->_putresources();
  10162. // Signature
  10163. if ($this->sign AND isset($this->signature_data['cert_type'])) {
  10164. // widget annotation for signature
  10165. $out = $this->_getobj($this->sig_obj_id)."\n";
  10166. $out .= '<< /Type /Annot';
  10167. $out .= ' /Subtype /Widget';
  10168. $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
  10169. $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
  10170. $out .= ' /F 4';
  10171. $out .= ' /FT /Sig';
  10172. $out .= ' /T '.$this->_textstring('Signature', $this->sig_obj_id);
  10173. $out .= ' /Ff 0';
  10174. $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
  10175. $out .= ' >>';
  10176. $out .= "\n".'endobj';
  10177. $this->_out($out);
  10178. // signature
  10179. $this->_putsignature();
  10180. }
  10181. // Info
  10182. $objid_info = $this->_putinfo();
  10183. // Catalog
  10184. $objid_catalog = $this->_putcatalog();
  10185. // Cross-ref
  10186. $o = $this->bufferlen;
  10187. // XREF section
  10188. $this->_out('xref');
  10189. $this->_out('0 '.($this->n + 1));
  10190. $this->_out('0000000000 65535 f ');
  10191. for ($i=1; $i <= $this->n; ++$i) {
  10192. $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
  10193. }
  10194. // TRAILER
  10195. $out = 'trailer <<';
  10196. $out .= ' /Size '.($this->n + 1);
  10197. $out .= ' /Root '.$objid_catalog.' 0 R';
  10198. $out .= ' /Info '.$objid_info.' 0 R';
  10199. if ($this->encrypted) {
  10200. $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
  10201. }
  10202. $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
  10203. $out .= ' >>';
  10204. $this->_out($out);
  10205. $this->_out('startxref');
  10206. $this->_out($o);
  10207. $this->_out('%%EOF');
  10208. $this->state = 3; // end-of-doc
  10209. if ($this->diskcache) {
  10210. // remove temporary files used for images
  10211. foreach ($this->imagekeys as $key) {
  10212. // remove temporary files
  10213. unlink($this->images[$key]);
  10214. }
  10215. foreach ($this->fontkeys as $key) {
  10216. // remove temporary files
  10217. unlink($this->fonts[$key]);
  10218. }
  10219. }
  10220. }
  10221. /**
  10222. * Initialize a new page.
  10223. * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
  10224. * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
  10225. * @access protected
  10226. * @see getPageSizeFromFormat(), setPageFormat()
  10227. */
  10228. protected function _beginpage($orientation='', $format='') {
  10229. ++$this->page;
  10230. $this->setPageBuffer($this->page, '');
  10231. // initialize array for graphics tranformation positions inside a page buffer
  10232. $this->transfmrk[$this->page] = array();
  10233. $this->state = 2;
  10234. if ($this->empty_string($orientation)) {
  10235. if (isset($this->CurOrientation)) {
  10236. $orientation = $this->CurOrientation;
  10237. } elseif ($this->fwPt > $this->fhPt) {
  10238. // landscape
  10239. $orientation = 'L';
  10240. } else {
  10241. // portrait
  10242. $orientation = 'P';
  10243. }
  10244. }
  10245. if ($this->empty_string($format)) {
  10246. $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
  10247. $this->setPageOrientation($orientation);
  10248. } else {
  10249. $this->setPageFormat($format, $orientation);
  10250. }
  10251. if ($this->rtl) {
  10252. $this->x = $this->w - $this->rMargin;
  10253. } else {
  10254. $this->x = $this->lMargin;
  10255. }
  10256. $this->y = $this->tMargin;
  10257. if (isset($this->newpagegroup[$this->page])) {
  10258. // start a new group
  10259. $n = sizeof($this->pagegroups) + 1;
  10260. $alias = '{nb'.$n.'}';
  10261. $this->pagegroups[$alias] = 1;
  10262. $this->currpagegroup = $alias;
  10263. } elseif ($this->currpagegroup) {
  10264. ++$this->pagegroups[$this->currpagegroup];
  10265. }
  10266. }
  10267. /**
  10268. * Mark end of page.
  10269. * @access protected
  10270. */
  10271. protected function _endpage() {
  10272. $this->setVisibility('all');
  10273. $this->state = 1;
  10274. }
  10275. /**
  10276. * Begin a new object and return the object number.
  10277. * @return int object number
  10278. * @access protected
  10279. */
  10280. protected function _newobj() {
  10281. $this->_out($this->_getobj());
  10282. return $this->n;
  10283. }
  10284. /**
  10285. * Return the starting object string for the selected object ID.
  10286. * @param int $objid Object ID (leave empty to get a new ID).
  10287. * @return string the starting object string
  10288. * @access protected
  10289. * @since 5.8.009 (2010-08-20)
  10290. */
  10291. protected function _getobj($objid='') {
  10292. if ($objid === '') {
  10293. ++$this->n;
  10294. $objid = $this->n;
  10295. }
  10296. $this->offsets[$objid] = $this->bufferlen;
  10297. return $objid.' 0 obj';
  10298. }
  10299. /**
  10300. * Underline text.
  10301. * @param int $x X coordinate
  10302. * @param int $y Y coordinate
  10303. * @param string $txt text to underline
  10304. * @access protected
  10305. */
  10306. protected function _dounderline($x, $y, $txt) {
  10307. $w = $this->GetStringWidth($txt);
  10308. return $this->_dounderlinew($x, $y, $w);
  10309. }
  10310. /**
  10311. * Underline for rectangular text area.
  10312. * @param int $x X coordinate
  10313. * @param int $y Y coordinate
  10314. * @param int $w width to underline
  10315. * @access protected
  10316. * @since 4.8.008 (2009-09-29)
  10317. */
  10318. protected function _dounderlinew($x, $y, $w) {
  10319. $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
  10320. return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
  10321. }
  10322. /**
  10323. * Line through text.
  10324. * @param int $x X coordinate
  10325. * @param int $y Y coordinate
  10326. * @param string $txt text to linethrough
  10327. * @access protected
  10328. */
  10329. protected function _dolinethrough($x, $y, $txt) {
  10330. $w = $this->GetStringWidth($txt);
  10331. return $this->_dolinethroughw($x, $y, $w);
  10332. }
  10333. /**
  10334. * Line through for rectangular text area.
  10335. * @param int $x X coordinate
  10336. * @param int $y Y coordinate
  10337. * @param string $txt text to linethrough
  10338. * @access protected
  10339. * @since 4.9.008 (2009-09-29)
  10340. */
  10341. protected function _dolinethroughw($x, $y, $w) {
  10342. $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
  10343. return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
  10344. }
  10345. /**
  10346. * Overline text.
  10347. * @param int $x X coordinate
  10348. * @param int $y Y coordinate
  10349. * @param string $txt text to overline
  10350. * @access protected
  10351. * @since 4.9.015 (2010-04-19)
  10352. */
  10353. protected function _dooverline($x, $y, $txt) {
  10354. $w = $this->GetStringWidth($txt);
  10355. return $this->_dooverlinew($x, $y, $w);
  10356. }
  10357. /**
  10358. * Overline for rectangular text area.
  10359. * @param int $x X coordinate
  10360. * @param int $y Y coordinate
  10361. * @param int $w width to overline
  10362. * @access protected
  10363. * @since 4.9.015 (2010-04-19)
  10364. */
  10365. protected function _dooverlinew($x, $y, $w) {
  10366. $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
  10367. return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
  10368. }
  10369. /**
  10370. * Read a 4-byte (32 bit) integer from file.
  10371. * @param string $f file name.
  10372. * @return 4-byte integer
  10373. * @access protected
  10374. */
  10375. protected function _freadint($f) {
  10376. $a = unpack('Ni', fread($f, 4));
  10377. return $a['i'];
  10378. }
  10379. /**
  10380. * Add "\" before "\", "(" and ")"
  10381. * @param string $s string to escape.
  10382. * @return string escaped string.
  10383. * @access protected
  10384. */
  10385. protected function _escape($s) {
  10386. // the chr(13) substitution fixes the Bugs item #1421290.
  10387. return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
  10388. }
  10389. /**
  10390. * Format a data string for meta information
  10391. * @param string $s data string to escape.
  10392. * @param int $n object ID
  10393. * @return string escaped string.
  10394. * @access protected
  10395. */
  10396. protected function _datastring($s, $n=0) {
  10397. if ($n == 0) {
  10398. $n = $this->n;
  10399. }
  10400. $s = $this->_encrypt_data($n, $s);
  10401. return '('. $this->_escape($s).')';
  10402. }
  10403. /**
  10404. * Returns a formatted date for meta information
  10405. * @param int $n object ID
  10406. * @return string escaped date string.
  10407. * @access protected
  10408. * @since 4.6.028 (2009-08-25)
  10409. */
  10410. protected function _datestring($n=0) {
  10411. $current_time = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\'';
  10412. return $this->_datastring('D:'.$current_time, $n);
  10413. }
  10414. /**
  10415. * Format a text string for meta information
  10416. * @param string $s string to escape.
  10417. * @param int $n object ID
  10418. * @return string escaped string.
  10419. * @access protected
  10420. */
  10421. protected function _textstring($s, $n=0) {
  10422. if ($this->isunicode) {
  10423. //Convert string to UTF-16BE
  10424. $s = $this->UTF8ToUTF16BE($s, true);
  10425. }
  10426. return $this->_datastring($s, $n);
  10427. }
  10428. /**
  10429. * THIS METHOD IS DEPRECATED
  10430. * Format a text string
  10431. * @param string $s string to escape.
  10432. * @return string escaped string.
  10433. * @access protected
  10434. * @deprecated
  10435. */
  10436. protected function _escapetext($s) {
  10437. if ($this->isunicode) {
  10438. if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
  10439. $s = $this->UTF8ToLatin1($s);
  10440. } else {
  10441. //Convert string to UTF-16BE and reverse RTL language
  10442. $s = $this->utf8StrRev($s, false, $this->tmprtl);
  10443. }
  10444. }
  10445. return $this->_escape($s);
  10446. }
  10447. /**
  10448. * get raw output stream.
  10449. * @param string $s string to output.
  10450. * @param int $n object reference for encryption mode
  10451. * @access protected
  10452. * @author Nicola Asuni
  10453. * @since 5.5.000 (2010-06-22)
  10454. */
  10455. protected function _getrawstream($s, $n=0) {
  10456. if ($n <= 0) {
  10457. // default to current object
  10458. $n = $this->n;
  10459. }
  10460. return $this->_encrypt_data($n, $s);
  10461. }
  10462. /**
  10463. * Format output stream (DEPRECATED).
  10464. * @param string $s string to output.
  10465. * @param int $n object reference for encryption mode
  10466. * @access protected
  10467. * @deprecated
  10468. */
  10469. protected function _getstream($s, $n=0) {
  10470. return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
  10471. }
  10472. /**
  10473. * Output a stream (DEPRECATED).
  10474. * @param string $s string to output.
  10475. * @param int $n object reference for encryption mode
  10476. * @access protected
  10477. * @deprecated
  10478. */
  10479. protected function _putstream($s, $n=0) {
  10480. $this->_out($this->_getstream($s, $n));
  10481. }
  10482. /**
  10483. * Output a string to the document.
  10484. * @param string $s string to output.
  10485. * @access protected
  10486. */
  10487. protected function _out($s) {
  10488. if ($this->state == 2) {
  10489. if ($this->inxobj) {
  10490. // we are inside an XObject template
  10491. $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
  10492. } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
  10493. // puts data before page footer
  10494. $pagebuff = $this->getPageBuffer($this->page);
  10495. $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
  10496. $footer = substr($pagebuff, -$this->footerlen[$this->page]);
  10497. $this->setPageBuffer($this->page, $page.$s."\n".$footer);
  10498. // update footer position
  10499. $this->footerpos[$this->page] += strlen($s."\n");
  10500. } else {
  10501. $this->setPageBuffer($this->page, $s."\n", true);
  10502. }
  10503. } else {
  10504. $this->setBuffer($s."\n");
  10505. }
  10506. }
  10507. /**
  10508. * Converts UTF-8 strings to codepoints array.<br>
  10509. * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
  10510. * Based on: http://www.faqs.org/rfcs/rfc3629.html
  10511. * <pre>
  10512. * Char. number range | UTF-8 octet sequence
  10513. * (hexadecimal) | (binary)
  10514. * --------------------+-----------------------------------------------
  10515. * 0000 0000-0000 007F | 0xxxxxxx
  10516. * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
  10517. * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
  10518. * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  10519. * ---------------------------------------------------------------------
  10520. *
  10521. * ABFN notation:
  10522. * ---------------------------------------------------------------------
  10523. * UTF8-octets = *( UTF8-char )
  10524. * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
  10525. * UTF8-1 = %x00-7F
  10526. * UTF8-2 = %xC2-DF UTF8-tail
  10527. *
  10528. * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
  10529. * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
  10530. * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
  10531. * %xF4 %x80-8F 2( UTF8-tail )
  10532. * UTF8-tail = %x80-BF
  10533. * ---------------------------------------------------------------------
  10534. * </pre>
  10535. * @param string $str string to process.
  10536. * @return array containing codepoints (UTF-8 characters values)
  10537. * @access protected
  10538. * @author Nicola Asuni
  10539. * @since 1.53.0.TC005 (2005-01-05)
  10540. */
  10541. protected function UTF8StringToArray($str) {
  10542. // build a unique string key
  10543. $strkey = md5($str);
  10544. if (isset($this->cache_UTF8StringToArray[$strkey])) {
  10545. // return cached value
  10546. $chrarray = $this->cache_UTF8StringToArray[$strkey]['s'];
  10547. if (!isset($this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']])) {
  10548. if ($this->isunicode) {
  10549. foreach ($chrarray as $chr) {
  10550. // store this char for font subsetting
  10551. $this->CurrentFont['subsetchars'][$chr] = true;
  10552. }
  10553. // update font subsetchars
  10554. $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
  10555. }
  10556. $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
  10557. }
  10558. return $chrarray;
  10559. }
  10560. // check cache size
  10561. if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
  10562. // remove first element
  10563. array_shift($this->cache_UTF8StringToArray);
  10564. }
  10565. // new cache array for selected string
  10566. $this->cache_UTF8StringToArray[$strkey] = array('s' => array(), 'f' => array());
  10567. ++$this->cache_size_UTF8StringToArray;
  10568. if (!$this->isunicode) {
  10569. // split string into array of equivalent codes
  10570. $strarr = array();
  10571. $strlen = strlen($str);
  10572. for ($i=0; $i < $strlen; ++$i) {
  10573. $strarr[] = ord($str{$i});
  10574. }
  10575. // insert new value on cache
  10576. $this->cache_UTF8StringToArray[$strkey]['s'] = $strarr;
  10577. $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
  10578. return $strarr;
  10579. }
  10580. $unichar = -1; // last unicode char
  10581. $unicode = array(); // array containing unicode values
  10582. $bytes = array(); // array containing single character byte sequences
  10583. $numbytes = 1; // number of octetc needed to represent the UTF-8 character
  10584. $str .= ''; // force $str to be a string
  10585. $length = strlen($str);
  10586. for ($i = 0; $i < $length; ++$i) {
  10587. $char = ord($str{$i}); // get one string character at time
  10588. if (count($bytes) == 0) { // get starting octect
  10589. if ($char <= 0x7F) {
  10590. $unichar = $char; // use the character "as is" because is ASCII
  10591. $numbytes = 1;
  10592. } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
  10593. $bytes[] = ($char - 0xC0) << 0x06;
  10594. $numbytes = 2;
  10595. } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
  10596. $bytes[] = ($char - 0xE0) << 0x0C;
  10597. $numbytes = 3;
  10598. } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
  10599. $bytes[] = ($char - 0xF0) << 0x12;
  10600. $numbytes = 4;
  10601. } else {
  10602. // use replacement character for other invalid sequences
  10603. $unichar = 0xFFFD;
  10604. $bytes = array();
  10605. $numbytes = 1;
  10606. }
  10607. } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
  10608. $bytes[] = $char - 0x80;
  10609. if (count($bytes) == $numbytes) {
  10610. // compose UTF-8 bytes to a single unicode value
  10611. $char = $bytes[0];
  10612. for ($j = 1; $j < $numbytes; ++$j) {
  10613. $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
  10614. }
  10615. if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
  10616. /* The definition of UTF-8 prohibits encoding character numbers between
  10617. U+D800 and U+DFFF, which are reserved for use with the UTF-16
  10618. encoding form (as surrogate pairs) and do not directly represent
  10619. characters. */
  10620. $unichar = 0xFFFD; // use replacement character
  10621. } else {
  10622. $unichar = $char; // add char to array
  10623. }
  10624. // reset data for next char
  10625. $bytes = array();
  10626. $numbytes = 1;
  10627. }
  10628. } else {
  10629. // use replacement character for other invalid sequences
  10630. $unichar = 0xFFFD;
  10631. $bytes = array();
  10632. $numbytes = 1;
  10633. }
  10634. if ($unichar >= 0) {
  10635. // insert unicode value into array
  10636. $unicode[] = $unichar;
  10637. // store this char for font subsetting
  10638. $this->CurrentFont['subsetchars'][$unichar] = true;
  10639. $unichar = -1;
  10640. }
  10641. }
  10642. // update font subsetchars
  10643. $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
  10644. // insert new value on cache
  10645. $this->cache_UTF8StringToArray[$strkey]['s'] = $unicode;
  10646. $this->cache_UTF8StringToArray[$strkey]['f'][$this->CurrentFont['fontkey']] = true;
  10647. return $unicode;
  10648. }
  10649. /**
  10650. * Converts UTF-8 strings to UTF16-BE.<br>
  10651. * @param string $str string to process.
  10652. * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
  10653. * @return string
  10654. * @access protected
  10655. * @author Nicola Asuni
  10656. * @since 1.53.0.TC005 (2005-01-05)
  10657. * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
  10658. */
  10659. protected function UTF8ToUTF16BE($str, $setbom=true) {
  10660. if (!$this->isunicode) {
  10661. return $str; // string is not in unicode
  10662. }
  10663. $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
  10664. return $this->arrUTF8ToUTF16BE($unicode, $setbom);
  10665. }
  10666. /**
  10667. * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
  10668. * @param string $str string to process.
  10669. * @return string
  10670. * @author Andrew Whitehead, Nicola Asuni
  10671. * @access protected
  10672. * @since 3.2.000 (2008-06-23)
  10673. */
  10674. protected function UTF8ToLatin1($str) {
  10675. if (!$this->isunicode) {
  10676. return $str; // string is not in unicode
  10677. }
  10678. $outstr = ''; // string to be returned
  10679. $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
  10680. foreach ($unicode as $char) {
  10681. if ($char < 256) {
  10682. $outstr .= chr($char);
  10683. } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
  10684. // map from UTF-8
  10685. $outstr .= chr($this->unicode->uni_utf8tolatin[$char]);
  10686. } elseif ($char == 0xFFFD) {
  10687. // skip
  10688. } else {
  10689. $outstr .= '?';
  10690. }
  10691. }
  10692. return $outstr;
  10693. }
  10694. /**
  10695. * Converts UTF-8 characters array to array of Latin1 characters<br>
  10696. * @param array $unicode array containing UTF-8 unicode values
  10697. * @return array
  10698. * @author Nicola Asuni
  10699. * @access protected
  10700. * @since 4.8.023 (2010-01-15)
  10701. */
  10702. protected function UTF8ArrToLatin1($unicode) {
  10703. if ((!$this->isunicode) OR $this->isUnicodeFont()) {
  10704. return $unicode;
  10705. }
  10706. $outarr = array(); // array to be returned
  10707. foreach ($unicode as $char) {
  10708. if ($char < 256) {
  10709. $outarr[] = $char;
  10710. } elseif (array_key_exists($char, $this->unicode->uni_utf8tolatin)) {
  10711. // map from UTF-8
  10712. $outarr[] = $this->unicode->uni_utf8tolatin[$char];
  10713. } elseif ($char == 0xFFFD) {
  10714. // skip
  10715. } else {
  10716. $outarr[] = 63; // '?' character
  10717. }
  10718. }
  10719. return $outarr;
  10720. }
  10721. /**
  10722. * Converts array of UTF-8 characters to UTF16-BE string.<br>
  10723. * Based on: http://www.faqs.org/rfcs/rfc2781.html
  10724. * <pre>
  10725. * Encoding UTF-16:
  10726. *
  10727. * Encoding of a single character from an ISO 10646 character value to
  10728. * UTF-16 proceeds as follows. Let U be the character number, no greater
  10729. * than 0x10FFFF.
  10730. *
  10731. * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and
  10732. * terminate.
  10733. *
  10734. * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
  10735. * U' must be less than or equal to 0xFFFFF. That is, U' can be
  10736. * represented in 20 bits.
  10737. *
  10738. * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
  10739. * 0xDC00, respectively. These integers each have 10 bits free to
  10740. * encode the character value, for a total of 20 bits.
  10741. *
  10742. * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
  10743. * bits of W1 and the 10 low-order bits of U' to the 10 low-order
  10744. * bits of W2. Terminate.
  10745. *
  10746. * Graphically, steps 2 through 4 look like:
  10747. * U' = yyyyyyyyyyxxxxxxxxxx
  10748. * W1 = 110110yyyyyyyyyy
  10749. * W2 = 110111xxxxxxxxxx
  10750. * </pre>
  10751. * @param array $unicode array containing UTF-8 unicode values
  10752. * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
  10753. * @return string
  10754. * @access protected
  10755. * @author Nicola Asuni
  10756. * @since 2.1.000 (2008-01-08)
  10757. * @see UTF8ToUTF16BE()
  10758. */
  10759. protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
  10760. $outstr = ''; // string to be returned
  10761. if ($setbom) {
  10762. $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
  10763. }
  10764. foreach ($unicode as $char) {
  10765. if ($char == 0x200b) {
  10766. // skip Unicode Character 'ZERO WIDTH SPACE' (DEC:8203, U+200B)
  10767. } elseif ($char == 0xFFFD) {
  10768. $outstr .= "\xFF\xFD"; // replacement character
  10769. } elseif ($char < 0x10000) {
  10770. $outstr .= chr($char >> 0x08);
  10771. $outstr .= chr($char & 0xFF);
  10772. } else {
  10773. $char -= 0x10000;
  10774. $w1 = 0xD800 | ($char >> 0x10);
  10775. $w2 = 0xDC00 | ($char & 0x3FF);
  10776. $outstr .= chr($w1 >> 0x08);
  10777. $outstr .= chr($w1 & 0xFF);
  10778. $outstr .= chr($w2 >> 0x08);
  10779. $outstr .= chr($w2 & 0xFF);
  10780. }
  10781. }
  10782. return $outstr;
  10783. }
  10784. // ====================================================
  10785. /**
  10786. * Set header font.
  10787. * @param array $font font
  10788. * @access public
  10789. * @since 1.1
  10790. */
  10791. public function setHeaderFont($font) {
  10792. $this->header_font = $font;
  10793. }
  10794. /**
  10795. * Get header font.
  10796. * @return array()
  10797. * @access public
  10798. * @since 4.0.012 (2008-07-24)
  10799. */
  10800. public function getHeaderFont() {
  10801. return $this->header_font;
  10802. }
  10803. /**
  10804. * Set footer font.
  10805. * @param array $font font
  10806. * @access public
  10807. * @since 1.1
  10808. */
  10809. public function setFooterFont($font) {
  10810. $this->footer_font = $font;
  10811. }
  10812. /**
  10813. * Get Footer font.
  10814. * @return array()
  10815. * @access public
  10816. * @since 4.0.012 (2008-07-24)
  10817. */
  10818. public function getFooterFont() {
  10819. return $this->footer_font;
  10820. }
  10821. /**
  10822. * Set language array.
  10823. * @param array $language
  10824. * @access public
  10825. * @since 1.1
  10826. */
  10827. public function setLanguageArray($language) {
  10828. $this->l = $language;
  10829. if (isset($this->l['a_meta_dir'])) {
  10830. $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
  10831. } else {
  10832. $this->rtl = false;
  10833. }
  10834. }
  10835. /**
  10836. * Returns the PDF data.
  10837. * @access public
  10838. */
  10839. public function getPDFData() {
  10840. if ($this->state < 3) {
  10841. $this->Close();
  10842. }
  10843. return $this->buffer;
  10844. }
  10845. /**
  10846. * Output anchor link.
  10847. * @param string $url link URL or internal link (i.e.: &lt;a href="#23,4.5"&gt;link to page 23 at 4.5 Y position&lt;/a&gt;)
  10848. * @param string $name link name
  10849. * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
  10850. * @param boolean $firstline if true prints only the first line and return the remaining string.
  10851. * @param array $color array of RGB text color
  10852. * @param string $style font style (U, D, B, I)
  10853. * @param boolean $firstblock if true the string is the starting of a line.
  10854. * @return the number of cells used or the remaining text if $firstline = true;
  10855. * @access public
  10856. */
  10857. public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
  10858. if (!$this->empty_string($url) AND ($url{0} == '#')) {
  10859. // convert url to internal link
  10860. $lnkdata = explode(',', $url);
  10861. if (isset($lnkdata[0])) {
  10862. $page = intval(substr($lnkdata[0], 1));
  10863. if (empty($page) OR ($page <= 0)) {
  10864. $page = $this->page;
  10865. }
  10866. if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
  10867. $lnky = floatval($lnkdata[1]);
  10868. } else {
  10869. $lnky = 0;
  10870. }
  10871. $url = $this->AddLink();
  10872. $this->SetLink($url, $lnky, $page);
  10873. }
  10874. }
  10875. // store current settings
  10876. $prevcolor = $this->fgcolor;
  10877. $prevstyle = $this->FontStyle;
  10878. if (empty($color)) {
  10879. $this->SetTextColorArray($this->htmlLinkColorArray);
  10880. } else {
  10881. $this->SetTextColorArray($color);
  10882. }
  10883. if ($style == -1) {
  10884. $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
  10885. } else {
  10886. $this->SetFont('', $this->FontStyle.$style);
  10887. }
  10888. $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
  10889. // restore settings
  10890. $this->SetFont('', $prevstyle);
  10891. $this->SetTextColorArray($prevcolor);
  10892. return $ret;
  10893. }
  10894. /**
  10895. * Returns an associative array (keys: R,G,B) from an html color name or a six-digit or three-digit hexadecimal color representation (i.e. #3FE5AA or #7FF).
  10896. * @param string $color html color
  10897. * @return array RGB color or false in case of error.
  10898. * @access public
  10899. */
  10900. public function convertHTMLColorToDec($color='#FFFFFF') {
  10901. $returncolor = false;
  10902. $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
  10903. $color = strtolower($color);
  10904. if (($dotpos = strpos($color, '.')) !== false) {
  10905. // remove class parent (i.e.: color.red)
  10906. $color = substr($color, ($dotpos + 1));
  10907. }
  10908. if (strlen($color) == 0) {
  10909. return false;
  10910. }
  10911. // RGB ARRAY
  10912. if (substr($color, 0, 3) == 'rgb') {
  10913. $codes = substr($color, 4);
  10914. $codes = str_replace(')', '', $codes);
  10915. $returncolor = explode(',', $codes);
  10916. return $returncolor;
  10917. }
  10918. // CMYK ARRAY
  10919. if (substr($color, 0, 4) == 'cmyk') {
  10920. $codes = substr($color, 5);
  10921. $codes = str_replace(')', '', $codes);
  10922. $returncolor = explode(',', $codes);
  10923. return $returncolor;
  10924. }
  10925. // COLOR NAME
  10926. if (substr($color, 0, 1) != '#') {
  10927. // decode color name
  10928. if (isset($this->webcolor[$color])) {
  10929. $color_code = $this->webcolor[$color];
  10930. } else {
  10931. return false;
  10932. }
  10933. } else {
  10934. $color_code = substr($color, 1);
  10935. }
  10936. // RGB VALUE
  10937. switch (strlen($color_code)) {
  10938. case 3: {
  10939. // three-digit hexadecimal representation
  10940. $r = substr($color_code, 0, 1);
  10941. $g = substr($color_code, 1, 1);
  10942. $b = substr($color_code, 2, 1);
  10943. $returncolor['R'] = hexdec($r.$r);
  10944. $returncolor['G'] = hexdec($g.$g);
  10945. $returncolor['B'] = hexdec($b.$b);
  10946. break;
  10947. }
  10948. case 6: {
  10949. // six-digit hexadecimal representation
  10950. $returncolor['R'] = hexdec(substr($color_code, 0, 2));
  10951. $returncolor['G'] = hexdec(substr($color_code, 2, 2));
  10952. $returncolor['B'] = hexdec(substr($color_code, 4, 2));
  10953. break;
  10954. }
  10955. }
  10956. return $returncolor;
  10957. }
  10958. /**
  10959. * Converts pixels to User's Units.
  10960. * @param int $px pixels
  10961. * @return float value in user's unit
  10962. * @access public
  10963. * @see setImageScale(), getImageScale()
  10964. */
  10965. public function pixelsToUnits($px) {
  10966. return ($px / ($this->imgscale * $this->k));
  10967. }
  10968. /**
  10969. * Reverse function for htmlentities.
  10970. * Convert entities in UTF-8.
  10971. * @param string $text_to_convert Text to convert.
  10972. * @return string converted text string
  10973. * @access public
  10974. */
  10975. public function unhtmlentities($text_to_convert) {
  10976. return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
  10977. }
  10978. // ENCRYPTION METHODS ----------------------------------
  10979. /**
  10980. * Returns a string containing random data to be used as a seed for encryption methods.
  10981. * @param string $seed starting seed value
  10982. * @return string containing random data
  10983. * @author Nicola Asuni
  10984. * @since 5.9.006 (2010-10-19)
  10985. * @access protected
  10986. */
  10987. protected function getRandomSeed($seed='') {
  10988. $seed .= microtime();
  10989. if (function_exists('openssl_random_pseudo_bytes')) {
  10990. $seed .= openssl_random_pseudo_bytes(512);
  10991. }
  10992. $seed .= uniqid('', true);
  10993. $seed .= rand();
  10994. $seed .= getmypid();
  10995. $seed .= __FILE__;
  10996. $seed .= $this->bufferlen;
  10997. if (isset($_SERVER['REMOTE_ADDR'])) {
  10998. $seed .= $_SERVER['REMOTE_ADDR'];
  10999. }
  11000. if (isset($_SERVER['HTTP_USER_AGENT'])) {
  11001. $seed .= $_SERVER['HTTP_USER_AGENT'];
  11002. }
  11003. if (isset($_SERVER['HTTP_ACCEPT'])) {
  11004. $seed .= $_SERVER['HTTP_ACCEPT'];
  11005. }
  11006. if (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  11007. $seed .= $_SERVER['HTTP_ACCEPT_ENCODING'];
  11008. }
  11009. if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  11010. $seed .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
  11011. }
  11012. if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) {
  11013. $seed .= $_SERVER['HTTP_ACCEPT_CHARSET'];
  11014. }
  11015. $seed .= rand();
  11016. $seed .= uniqid('', true);
  11017. $seed .= microtime();
  11018. return $seed;
  11019. }
  11020. /**
  11021. * Compute encryption key depending on object number where the encrypted data is stored.
  11022. * This is used for all strings and streams without crypt filter specifier.
  11023. * @param int $n object number
  11024. * @return int object key
  11025. * @access protected
  11026. * @author Nicola Asuni
  11027. * @since 2.0.000 (2008-01-02)
  11028. */
  11029. protected function _objectkey($n) {
  11030. $objkey = $this->encryptdata['key'].pack('VXxx', $n);
  11031. if ($this->encryptdata['mode'] == 2) { // AES-128
  11032. // AES padding
  11033. $objkey .= "\x73\x41\x6C\x54"; // sAlT
  11034. }
  11035. $objkey = substr($this->_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
  11036. $objkey = substr($objkey, 0, 16);
  11037. return $objkey;
  11038. }
  11039. /**
  11040. * Encrypt the input string.
  11041. * @param int $n object number
  11042. * @param string $s data string to encrypt
  11043. * @return encrypted string
  11044. * @access protected
  11045. * @author Nicola Asuni
  11046. * @since 5.0.005 (2010-05-11)
  11047. */
  11048. protected function _encrypt_data($n, $s) {
  11049. if (!$this->encrypted) {
  11050. return $s;
  11051. }
  11052. switch ($this->encryptdata['mode']) {
  11053. case 0: // RC4-40
  11054. case 1: { // RC4-128
  11055. $s = $this->_RC4($this->_objectkey($n), $s);
  11056. break;
  11057. }
  11058. case 2: { // AES-128
  11059. $s = $this->_AES($this->_objectkey($n), $s);
  11060. break;
  11061. }
  11062. case 3: { // AES-256
  11063. $s = $this->_AES($this->encryptdata['key'], $s);
  11064. break;
  11065. }
  11066. }
  11067. return $s;
  11068. }
  11069. /**
  11070. * Put encryption on PDF document.
  11071. * @access protected
  11072. * @author Nicola Asuni
  11073. * @since 2.0.000 (2008-01-02)
  11074. */
  11075. protected function _putencryption() {
  11076. if (!$this->encrypted) {
  11077. return;
  11078. }
  11079. $this->encryptdata['objid'] = $this->_newobj();
  11080. $out = '<<';
  11081. if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
  11082. $this->encryptdata['Filter'] = 'Standard';
  11083. }
  11084. $out .= ' /Filter /'.$this->encryptdata['Filter'];
  11085. if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
  11086. $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
  11087. }
  11088. if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
  11089. $this->encryptdata['V'] = 1;
  11090. }
  11091. // V is a code specifying the algorithm to be used in encrypting and decrypting the document
  11092. $out .= ' /V '.$this->encryptdata['V'];
  11093. if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
  11094. // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
  11095. $out .= ' /Length '.$this->encryptdata['Length'];
  11096. } else {
  11097. $out .= ' /Length 40';
  11098. }
  11099. if ($this->encryptdata['V'] >= 4) {
  11100. if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
  11101. $this->encryptdata['StmF'] = 'Identity';
  11102. }
  11103. if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
  11104. // The name of the crypt filter that shall be used when decrypting all strings in the document.
  11105. $this->encryptdata['StrF'] = 'Identity';
  11106. }
  11107. // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
  11108. if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
  11109. $out .= ' /CF <<';
  11110. $out .= ' /'.$this->encryptdata['StmF'].' <<';
  11111. $out .= ' /Type /CryptFilter';
  11112. if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
  11113. // The method used
  11114. $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
  11115. if ($this->encryptdata['pubkey']) {
  11116. $out .= ' /Recipients [';
  11117. foreach ($this->encryptdata['Recipients'] as $rec) {
  11118. $out .= ' <'.$rec.'>';
  11119. }
  11120. $out .= ' ]';
  11121. if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
  11122. $out .= ' /EncryptMetadata false';
  11123. } else {
  11124. $out .= ' /EncryptMetadata true';
  11125. }
  11126. }
  11127. } else {
  11128. $out .= ' /CFM /None';
  11129. }
  11130. if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
  11131. // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
  11132. $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
  11133. } else {
  11134. $out .= ' /AuthEvent /DocOpen';
  11135. }
  11136. if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
  11137. // The bit length of the encryption key.
  11138. $out .= ' /Length '.$this->encryptdata['CF']['Length'];
  11139. }
  11140. $out .= ' >> >>';
  11141. }
  11142. // The name of the crypt filter that shall be used by default when decrypting streams.
  11143. $out .= ' /StmF /'.$this->encryptdata['StmF'];
  11144. // The name of the crypt filter that shall be used when decrypting all strings in the document.
  11145. $out .= ' /StrF /'.$this->encryptdata['StrF'];
  11146. if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
  11147. // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
  11148. $out .= ' /EFF /'.$this->encryptdata[''];
  11149. }
  11150. }
  11151. // Additional encryption dictionary entries for the standard security handler
  11152. if ($this->encryptdata['pubkey']) {
  11153. if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
  11154. $out .= ' /Recipients [';
  11155. foreach ($this->encryptdata['Recipients'] as $rec) {
  11156. $out .= ' <'.$rec.'>';
  11157. }
  11158. $out .= ' ]';
  11159. }
  11160. } else {
  11161. $out .= ' /R';
  11162. if ($this->encryptdata['V'] == 5) { // AES-256
  11163. $out .= ' 5';
  11164. $out .= ' /OE ('.$this->_escape($this->encryptdata['OE']).')';
  11165. $out .= ' /UE ('.$this->_escape($this->encryptdata['UE']).')';
  11166. $out .= ' /Perms ('.$this->_escape($this->encryptdata['perms']).')';
  11167. } elseif ($this->encryptdata['V'] == 4) { // AES-128
  11168. $out .= ' 4';
  11169. } elseif ($this->encryptdata['V'] < 2) { // RC-40
  11170. $out .= ' 2';
  11171. } else { // RC-128
  11172. $out .= ' 3';
  11173. }
  11174. $out .= ' /O ('.$this->_escape($this->encryptdata['O']).')';
  11175. $out .= ' /U ('.$this->_escape($this->encryptdata['U']).')';
  11176. $out .= ' /P '.$this->encryptdata['P'];
  11177. if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
  11178. $out .= ' /EncryptMetadata false';
  11179. } else {
  11180. $out .= ' /EncryptMetadata true';
  11181. }
  11182. }
  11183. $out .= ' >>';
  11184. $out .= "\n".'endobj';
  11185. $this->_out($out);
  11186. }
  11187. /**
  11188. * Returns the input text encrypted using RC4 algorithm and the specified key.
  11189. * RC4 is the standard encryption algorithm used in PDF format
  11190. * @param string $key encryption key
  11191. * @param String $text input text to be encrypted
  11192. * @return String encrypted text
  11193. * @access protected
  11194. * @since 2.0.000 (2008-01-02)
  11195. * @author Klemen Vodopivec, Nicola Asuni
  11196. */
  11197. protected function _RC4($key, $text) {
  11198. if (function_exists('mcrypt_decrypt') AND ($out = @mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, ''))) {
  11199. // try to use mcrypt function if exist
  11200. return $out;
  11201. }
  11202. if ($this->last_enc_key != $key) {
  11203. $k = str_repeat($key, ((256 / strlen($key)) + 1));
  11204. $rc4 = range(0, 255);
  11205. $j = 0;
  11206. for ($i = 0; $i < 256; ++$i) {
  11207. $t = $rc4[$i];
  11208. $j = ($j + $t + ord($k{$i})) % 256;
  11209. $rc4[$i] = $rc4[$j];
  11210. $rc4[$j] = $t;
  11211. }
  11212. $this->last_enc_key = $key;
  11213. $this->last_enc_key_c = $rc4;
  11214. } else {
  11215. $rc4 = $this->last_enc_key_c;
  11216. }
  11217. $len = strlen($text);
  11218. $a = 0;
  11219. $b = 0;
  11220. $out = '';
  11221. for ($i = 0; $i < $len; ++$i) {
  11222. $a = ($a + 1) % 256;
  11223. $t = $rc4[$a];
  11224. $b = ($b + $t) % 256;
  11225. $rc4[$a] = $rc4[$b];
  11226. $rc4[$b] = $t;
  11227. $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
  11228. $out .= chr(ord($text{$i}) ^ $k);
  11229. }
  11230. return $out;
  11231. }
  11232. /**
  11233. * Returns the input text exrypted using AES algorithm and the specified key.
  11234. * This method requires mcrypt.
  11235. * @param string $key encryption key
  11236. * @param String $text input text to be encrypted
  11237. * @return String encrypted text
  11238. * @access protected
  11239. * @author Nicola Asuni
  11240. * @since 5.0.005 (2010-05-11)
  11241. */
  11242. protected function _AES($key, $text) {
  11243. // padding (RFC 2898, PKCS #5: Password-Based Cryptography Specification Version 2.0)
  11244. $padding = 16 - (strlen($text) % 16);
  11245. $text .= str_repeat(chr($padding), $padding);
  11246. $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
  11247. $text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_CBC, $iv);
  11248. $text = $iv.$text;
  11249. return $text;
  11250. }
  11251. /**
  11252. * Encrypts a string using MD5 and returns it's value as a binary string.
  11253. * @param string $str input string
  11254. * @return String MD5 encrypted binary string
  11255. * @access protected
  11256. * @since 2.0.000 (2008-01-02)
  11257. * @author Klemen Vodopivec
  11258. */
  11259. protected function _md5_16($str) {
  11260. return pack('H*', md5($str));
  11261. }
  11262. /**
  11263. * Compute U value (used for encryption)
  11264. * @return string U value
  11265. * @access protected
  11266. * @since 2.0.000 (2008-01-02)
  11267. * @author Nicola Asuni
  11268. */
  11269. protected function _Uvalue() {
  11270. if ($this->encryptdata['mode'] == 0) { // RC4-40
  11271. return $this->_RC4($this->encryptdata['key'], $this->enc_padding);
  11272. } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
  11273. $tmp = $this->_md5_16($this->enc_padding.$this->encryptdata['fileid']);
  11274. $enc = $this->_RC4($this->encryptdata['key'], $tmp);
  11275. $len = strlen($tmp);
  11276. for ($i = 1; $i <= 19; ++$i) {
  11277. $ek = '';
  11278. for ($j = 0; $j < $len; ++$j) {
  11279. $ek .= chr(ord($this->encryptdata['key']{$j}) ^ $i);
  11280. }
  11281. $enc = $this->_RC4($ek, $enc);
  11282. }
  11283. $enc .= str_repeat("\x00", 16);
  11284. return substr($enc, 0, 32);
  11285. } elseif ($this->encryptdata['mode'] == 3) { // AES-256
  11286. $seed = $this->_md5_16($this->getRandomSeed());
  11287. // User Validation Salt
  11288. $this->encryptdata['UVS'] = substr($seed, 0, 8);
  11289. // User Key Salt
  11290. $this->encryptdata['UKS'] = substr($seed, 8, 16);
  11291. return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
  11292. }
  11293. }
  11294. /**
  11295. * Compute UE value (used for encryption)
  11296. * @return string UE value
  11297. * @access protected
  11298. * @since 5.9.006 (2010-10-19)
  11299. * @author Nicola Asuni
  11300. */
  11301. protected function _UEvalue() {
  11302. $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
  11303. $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
  11304. return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
  11305. }
  11306. /**
  11307. * Compute O value (used for encryption)
  11308. * @return string O value
  11309. * @access protected
  11310. * @since 2.0.000 (2008-01-02)
  11311. * @author Nicola Asuni
  11312. */
  11313. protected function _Ovalue() {
  11314. if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
  11315. $tmp = $this->_md5_16($this->encryptdata['owner_password']);
  11316. if ($this->encryptdata['mode'] > 0) {
  11317. for ($i = 0; $i < 50; ++$i) {
  11318. $tmp = $this->_md5_16($tmp);
  11319. }
  11320. }
  11321. $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
  11322. $enc = $this->_RC4($owner_key, $this->encryptdata['user_password']);
  11323. if ($this->encryptdata['mode'] > 0) {
  11324. $len = strlen($owner_key);
  11325. for ($i = 1; $i <= 19; ++$i) {
  11326. $ek = '';
  11327. for ($j = 0; $j < $len; ++$j) {
  11328. $ek .= chr(ord($owner_key{$j}) ^ $i);
  11329. }
  11330. $enc = $this->_RC4($ek, $enc);
  11331. }
  11332. }
  11333. return $enc;
  11334. } elseif ($this->encryptdata['mode'] == 3) { // AES-256
  11335. $seed = $this->_md5_16($this->getRandomSeed());
  11336. // Owner Validation Salt
  11337. $this->encryptdata['OVS'] = substr($seed, 0, 8);
  11338. // Owner Key Salt
  11339. $this->encryptdata['OKS'] = substr($seed, 8, 16);
  11340. return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
  11341. }
  11342. }
  11343. /**
  11344. * Compute OE value (used for encryption)
  11345. * @return string OE value
  11346. * @access protected
  11347. * @since 5.9.006 (2010-10-19)
  11348. * @author Nicola Asuni
  11349. */
  11350. protected function _OEvalue() {
  11351. $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
  11352. $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
  11353. return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
  11354. }
  11355. /**
  11356. * Convert password for AES-256 encryption mode
  11357. * @return string password
  11358. * @access protected
  11359. * @since 5.9.006 (2010-10-19)
  11360. * @author Nicola Asuni
  11361. */
  11362. protected function _fixAES256Password($password) {
  11363. $psw = ''; // password to be returned
  11364. $psw_array = $this->utf8Bidi($this->UTF8StringToArray($password), $password, $this->rtl);
  11365. foreach ($psw_array as $c) {
  11366. $psw .= $this->unichr($c);
  11367. }
  11368. return substr($psw, 0, 127);
  11369. }
  11370. /**
  11371. * Compute encryption key
  11372. * @access protected
  11373. * @since 2.0.000 (2008-01-02)
  11374. * @author Nicola Asuni
  11375. */
  11376. protected function _generateencryptionkey() {
  11377. $keybytelen = ($this->encryptdata['Length'] / 8);
  11378. if (!$this->encryptdata['pubkey']) { // standard mode
  11379. if ($this->encryptdata['mode'] == 3) { // AES-256
  11380. // generate 256 bit random key
  11381. $this->encryptdata['key'] = substr(hash('sha256', $this->getRandomSeed(), true), 0, $keybytelen);
  11382. // truncate passwords
  11383. $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
  11384. $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
  11385. // Compute U value
  11386. $this->encryptdata['U'] = $this->_Uvalue();
  11387. // Compute UE value
  11388. $this->encryptdata['UE'] = $this->_UEvalue();
  11389. // Compute O value
  11390. $this->encryptdata['O'] = $this->_Ovalue();
  11391. // Compute OE value
  11392. $this->encryptdata['OE'] = $this->_OEvalue();
  11393. // Compute P value
  11394. $this->encryptdata['P'] = $this->encryptdata['protection'];
  11395. // Computing the encryption dictionary's Perms (permissions) value
  11396. $perms = $this->getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
  11397. $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
  11398. if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
  11399. $perms .= 'F';
  11400. } else {
  11401. $perms .= 'T';
  11402. }
  11403. $perms .= 'adb'; // bytes 9-11
  11404. $perms .= 'nick'; // bytes 12-15
  11405. $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
  11406. $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
  11407. } else { // RC4-40, RC4-128, AES-128
  11408. // Pad passwords
  11409. $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].$this->enc_padding, 0, 32);
  11410. $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].$this->enc_padding, 0, 32);
  11411. // Compute O value
  11412. $this->encryptdata['O'] = $this->_Ovalue();
  11413. // get default permissions (reverse byte order)
  11414. $permissions = $this->getEncPermissionsString($this->encryptdata['protection']);
  11415. // Compute encryption key
  11416. $tmp = $this->_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
  11417. if ($this->encryptdata['mode'] > 0) {
  11418. for ($i = 0; $i < 50; ++$i) {
  11419. $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen));
  11420. }
  11421. }
  11422. $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
  11423. // Compute U value
  11424. $this->encryptdata['U'] = $this->_Uvalue();
  11425. // Compute P value
  11426. $this->encryptdata['P'] = $this->encryptdata['protection'];
  11427. }
  11428. } else { // Public-Key mode
  11429. // random 20-byte seed
  11430. $seed = sha1($this->getRandomSeed(), true);
  11431. $recipient_bytes = '';
  11432. foreach ($this->encryptdata['pubkeys'] as $pubkey) {
  11433. // for each public certificate
  11434. if (isset($pubkey['p'])) {
  11435. $pkprotection = $this->getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
  11436. } else {
  11437. $pkprotection = $this->encryptdata['protection'];
  11438. }
  11439. // get default permissions (reverse byte order)
  11440. $pkpermissions = $this->getEncPermissionsString($pkprotection);
  11441. // envelope data
  11442. $envelope = $seed.$pkpermissions;
  11443. // write the envelope data to a temporary file
  11444. $tempkeyfile = tempnam(K_PATH_CACHE, 'tmpkey_');
  11445. $f = fopen($tempkeyfile, 'wb');
  11446. if (!$f) {
  11447. $this->Error('Unable to create temporary key file: '.$tempkeyfile);
  11448. }
  11449. $envelope_lenght = strlen($envelope);
  11450. fwrite($f, $envelope, $envelope_lenght);
  11451. fclose($f);
  11452. $tempencfile = tempnam(K_PATH_CACHE, 'tmpenc_');
  11453. if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_DETACHED | PKCS7_BINARY)) {
  11454. $this->Error('Unable to encrypt the file: '.$tempkeyfile);
  11455. }
  11456. unlink($tempkeyfile);
  11457. // read encryption signature
  11458. $signature = file_get_contents($tempencfile, false, null, $envelope_lenght);
  11459. unlink($tempencfile);
  11460. // extract signature
  11461. $signature = substr($signature, strpos($signature, 'Content-Disposition'));
  11462. $tmparr = explode("\n\n", $signature);
  11463. $signature = trim($tmparr[1]);
  11464. unset($tmparr);
  11465. // decode signature
  11466. $signature = base64_decode($signature);
  11467. // convert signature to hex
  11468. $hexsignature = current(unpack('H*', $signature));
  11469. // store signature on recipients array
  11470. $this->encryptdata['Recipients'][] = $hexsignature;
  11471. // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
  11472. $recipient_bytes .= $signature;
  11473. }
  11474. // calculate encryption key
  11475. if ($this->encryptdata['mode'] == 3) { // AES-256
  11476. $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
  11477. } else { // RC4-40, RC4-128, AES-128
  11478. $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
  11479. }
  11480. }
  11481. }
  11482. /**
  11483. * Return the premission code used on encryption (P value).
  11484. * @param Array $permissions the set of permissions (specify the ones you want to block).
  11485. * @param int $mode encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
  11486. * @access protected
  11487. * @since 5.0.005 (2010-05-12)
  11488. * @author Nicola Asuni
  11489. */
  11490. protected function getUserPermissionCode($permissions, $mode=0) {
  11491. $options = array(
  11492. 'owner' => 2, // bit 2 -- inverted logic: cleared by default
  11493. 'print' => 4, // bit 3
  11494. 'modify' => 8, // bit 4
  11495. 'copy' => 16, // bit 5
  11496. 'annot-forms' => 32, // bit 6
  11497. 'fill-forms' => 256, // bit 9
  11498. 'extract' => 512, // bit 10
  11499. 'assemble' => 1024,// bit 11
  11500. 'print-high' => 2048 // bit 12
  11501. );
  11502. $protection = 2147422012; // 32 bit: (01111111 11111111 00001111 00111100)
  11503. foreach ($permissions as $permission) {
  11504. if (!isset($options[$permission])) {
  11505. $this->Error('Incorrect permission: '.$permission);
  11506. }
  11507. if (($mode > 0) OR ($options[$permission] <= 32)) {
  11508. // set only valid permissions
  11509. if ($options[$permission] == 2) {
  11510. // the logic for bit 2 is inverted (cleared by default)
  11511. $protection += $options[$permission];
  11512. } else {
  11513. $protection -= $options[$permission];
  11514. }
  11515. }
  11516. }
  11517. return $protection;
  11518. }
  11519. /**
  11520. * Set document protection
  11521. * Remark: the protection against modification is for people who have the full Acrobat product.
  11522. * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access.
  11523. * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts.
  11524. * @param Array $permissions the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul>
  11525. * @param String $user_pass user password. Empty by default.
  11526. * @param String $owner_pass owner password. If not specified, a random value is used.
  11527. * @param int $mode encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
  11528. * @param String $pubkeys array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../tcpdf.crt', 'p' => array('print')))
  11529. * @access public
  11530. * @since 2.0.000 (2008-01-02)
  11531. * @author Nicola Asuni
  11532. */
  11533. public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
  11534. $this->encryptdata['protection'] = $this->getUserPermissionCode($permissions, $mode);
  11535. if (($pubkeys !== null) AND (is_array($pubkeys))) {
  11536. // public-key mode
  11537. $this->encryptdata['pubkeys'] = $pubkeys;
  11538. if ($mode == 0) {
  11539. // public-Key Security requires at least 128 bit
  11540. $mode = 1;
  11541. }
  11542. if (!function_exists('openssl_pkcs7_encrypt')) {
  11543. $this->Error('Public-Key Security requires openssl library.');
  11544. }
  11545. // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
  11546. $this->encryptdata['pubkey'] = true;
  11547. $this->encryptdata['Filter'] = 'Adobe.PubSec';
  11548. $this->encryptdata['StmF'] = 'DefaultCryptFilter';
  11549. $this->encryptdata['StrF'] = 'DefaultCryptFilter';
  11550. } else {
  11551. // standard mode (password mode)
  11552. $this->encryptdata['pubkey'] = false;
  11553. $this->encryptdata['Filter'] = 'Standard';
  11554. $this->encryptdata['StmF'] = 'StdCF';
  11555. $this->encryptdata['StrF'] = 'StdCF';
  11556. }
  11557. if ($mode > 1) { // AES
  11558. if (!extension_loaded('mcrypt')) {
  11559. $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
  11560. }
  11561. if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
  11562. $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
  11563. }
  11564. if (($mode == 3) AND !function_exists('hash')) {
  11565. // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
  11566. $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
  11567. }
  11568. }
  11569. if ($owner_pass === null) {
  11570. $owner_pass = md5($this->getRandomSeed());
  11571. }
  11572. $this->encryptdata['user_password'] = $user_pass;
  11573. $this->encryptdata['owner_password'] = $owner_pass;
  11574. $this->encryptdata['mode'] = $mode;
  11575. switch ($mode) {
  11576. case 0: { // RC4 40 bit
  11577. $this->encryptdata['V'] = 1;
  11578. $this->encryptdata['Length'] = 40;
  11579. $this->encryptdata['CF']['CFM'] = 'V2';
  11580. break;
  11581. }
  11582. case 1: { // RC4 128 bit
  11583. $this->encryptdata['V'] = 2;
  11584. $this->encryptdata['Length'] = 128;
  11585. $this->encryptdata['CF']['CFM'] = 'V2';
  11586. if ($this->encryptdata['pubkey']) {
  11587. $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
  11588. $this->encryptdata['Recipients'] = array();
  11589. }
  11590. break;
  11591. }
  11592. case 2: { // AES 128 bit
  11593. $this->encryptdata['V'] = 4;
  11594. $this->encryptdata['Length'] = 128;
  11595. $this->encryptdata['CF']['CFM'] = 'AESV2';
  11596. $this->encryptdata['CF']['Length'] = 128;
  11597. if ($this->encryptdata['pubkey']) {
  11598. $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
  11599. $this->encryptdata['Recipients'] = array();
  11600. }
  11601. break;
  11602. }
  11603. case 3: { // AES 256 bit
  11604. $this->encryptdata['V'] = 5;
  11605. $this->encryptdata['Length'] = 256;
  11606. $this->encryptdata['CF']['CFM'] = 'AESV3';
  11607. $this->encryptdata['CF']['Length'] = 256;
  11608. if ($this->encryptdata['pubkey']) {
  11609. $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
  11610. $this->encryptdata['Recipients'] = array();
  11611. }
  11612. break;
  11613. }
  11614. }
  11615. $this->encrypted = true;
  11616. $this->encryptdata['fileid'] = $this->convertHexStringToString($this->file_id);
  11617. $this->_generateencryptionkey();
  11618. }
  11619. /**
  11620. * Convert hexadecimal string to string
  11621. * @param string $bs byte-string to convert
  11622. * @return String
  11623. * @access protected
  11624. * @since 5.0.005 (2010-05-12)
  11625. * @author Nicola Asuni
  11626. */
  11627. protected function convertHexStringToString($bs) {
  11628. $string = ''; // string to be returned
  11629. $bslenght = strlen($bs);
  11630. if (($bslenght % 2) != 0) {
  11631. // padding
  11632. $bs .= '0';
  11633. ++$bslenght;
  11634. }
  11635. for ($i = 0; $i < $bslenght; $i += 2) {
  11636. $string .= chr(hexdec($bs{$i}.$bs{($i + 1)}));
  11637. }
  11638. return $string;
  11639. }
  11640. /**
  11641. * Convert string to hexadecimal string (byte string)
  11642. * @param string $s string to convert
  11643. * @return byte string
  11644. * @access protected
  11645. * @since 5.0.010 (2010-05-17)
  11646. * @author Nicola Asuni
  11647. */
  11648. protected function convertStringToHexString($s) {
  11649. $bs = '';
  11650. $chars = preg_split('//', $s, -1, PREG_SPLIT_NO_EMPTY);
  11651. foreach ($chars as $c) {
  11652. $bs .= sprintf('%02s', dechex(ord($c)));
  11653. }
  11654. return $bs;
  11655. }
  11656. /**
  11657. * Convert encryption P value to a string of bytes, low-order byte first.
  11658. * @param string $protection 32bit encryption permission value (P value)
  11659. * @return String
  11660. * @access protected
  11661. * @since 5.0.005 (2010-05-12)
  11662. * @author Nicola Asuni
  11663. */
  11664. protected function getEncPermissionsString($protection) {
  11665. $binprot = sprintf('%032b', $protection);
  11666. $str = chr(bindec(substr($binprot, 24, 8)));
  11667. $str .= chr(bindec(substr($binprot, 16, 8)));
  11668. $str .= chr(bindec(substr($binprot, 8, 8)));
  11669. $str .= chr(bindec(substr($binprot, 0, 8)));
  11670. return $str;
  11671. }
  11672. // END OF ENCRYPTION FUNCTIONS -------------------------
  11673. // START TRANSFORMATIONS SECTION -----------------------
  11674. /**
  11675. * Starts a 2D tranformation saving current graphic state.
  11676. * This function must be called before scaling, mirroring, translation, rotation and skewing.
  11677. * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
  11678. * @access public
  11679. * @since 2.1.000 (2008-01-07)
  11680. * @see StartTransform(), StopTransform()
  11681. */
  11682. public function StartTransform() {
  11683. $this->_out('q');
  11684. if ($this->inxobj) {
  11685. // we are inside an XObject template
  11686. $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
  11687. } else {
  11688. $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
  11689. }
  11690. ++$this->transfmatrix_key;
  11691. $this->transfmatrix[$this->transfmatrix_key] = array();
  11692. }
  11693. /**
  11694. * Stops a 2D tranformation restoring previous graphic state.
  11695. * This function must be called after scaling, mirroring, translation, rotation and skewing.
  11696. * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
  11697. * @access public
  11698. * @since 2.1.000 (2008-01-07)
  11699. * @see StartTransform(), StopTransform()
  11700. */
  11701. public function StopTransform() {
  11702. $this->_out('Q');
  11703. if (isset($this->transfmatrix[$this->transfmatrix_key])) {
  11704. array_pop($this->transfmatrix[$this->transfmatrix_key]);
  11705. --$this->transfmatrix_key;
  11706. }
  11707. if ($this->inxobj) {
  11708. // we are inside an XObject template
  11709. array_pop($this->xobjects[$this->xobjid]['transfmrk']);
  11710. } else {
  11711. array_pop($this->transfmrk[$this->page]);
  11712. }
  11713. }
  11714. /**
  11715. * Horizontal Scaling.
  11716. * @param float $s_x scaling factor for width as percent. 0 is not allowed.
  11717. * @param int $x abscissa of the scaling center. Default is current x position
  11718. * @param int $y ordinate of the scaling center. Default is current y position
  11719. * @access public
  11720. * @since 2.1.000 (2008-01-07)
  11721. * @see StartTransform(), StopTransform()
  11722. */
  11723. public function ScaleX($s_x, $x='', $y='') {
  11724. $this->Scale($s_x, 100, $x, $y);
  11725. }
  11726. /**
  11727. * Vertical Scaling.
  11728. * @param float $s_y scaling factor for height as percent. 0 is not allowed.
  11729. * @param int $x abscissa of the scaling center. Default is current x position
  11730. * @param int $y ordinate of the scaling center. Default is current y position
  11731. * @access public
  11732. * @since 2.1.000 (2008-01-07)
  11733. * @see StartTransform(), StopTransform()
  11734. */
  11735. public function ScaleY($s_y, $x='', $y='') {
  11736. $this->Scale(100, $s_y, $x, $y);
  11737. }
  11738. /**
  11739. * Vertical and horizontal proportional Scaling.
  11740. * @param float $s scaling factor for width and height as percent. 0 is not allowed.
  11741. * @param int $x abscissa of the scaling center. Default is current x position
  11742. * @param int $y ordinate of the scaling center. Default is current y position
  11743. * @access public
  11744. * @since 2.1.000 (2008-01-07)
  11745. * @see StartTransform(), StopTransform()
  11746. */
  11747. public function ScaleXY($s, $x='', $y='') {
  11748. $this->Scale($s, $s, $x, $y);
  11749. }
  11750. /**
  11751. * Vertical and horizontal non-proportional Scaling.
  11752. * @param float $s_x scaling factor for width as percent. 0 is not allowed.
  11753. * @param float $s_y scaling factor for height as percent. 0 is not allowed.
  11754. * @param int $x abscissa of the scaling center. Default is current x position
  11755. * @param int $y ordinate of the scaling center. Default is current y position
  11756. * @access public
  11757. * @since 2.1.000 (2008-01-07)
  11758. * @see StartTransform(), StopTransform()
  11759. */
  11760. public function Scale($s_x, $s_y, $x='', $y='') {
  11761. if ($x === '') {
  11762. $x = $this->x;
  11763. }
  11764. if ($y === '') {
  11765. $y = $this->y;
  11766. }
  11767. if (($s_x == 0) OR ($s_y == 0)) {
  11768. $this->Error('Please do not use values equal to zero for scaling');
  11769. }
  11770. $y = ($this->h - $y) * $this->k;
  11771. $x *= $this->k;
  11772. //calculate elements of transformation matrix
  11773. $s_x /= 100;
  11774. $s_y /= 100;
  11775. $tm = array();
  11776. $tm[0] = $s_x;
  11777. $tm[1] = 0;
  11778. $tm[2] = 0;
  11779. $tm[3] = $s_y;
  11780. $tm[4] = $x * (1 - $s_x);
  11781. $tm[5] = $y * (1 - $s_y);
  11782. //scale the coordinate system
  11783. $this->Transform($tm);
  11784. }
  11785. /**
  11786. * Horizontal Mirroring.
  11787. * @param int $x abscissa of the point. Default is current x position
  11788. * @access public
  11789. * @since 2.1.000 (2008-01-07)
  11790. * @see StartTransform(), StopTransform()
  11791. */
  11792. public function MirrorH($x='') {
  11793. $this->Scale(-100, 100, $x);
  11794. }
  11795. /**
  11796. * Verical Mirroring.
  11797. * @param int $y ordinate of the point. Default is current y position
  11798. * @access public
  11799. * @since 2.1.000 (2008-01-07)
  11800. * @see StartTransform(), StopTransform()
  11801. */
  11802. public function MirrorV($y='') {
  11803. $this->Scale(100, -100, '', $y);
  11804. }
  11805. /**
  11806. * Point reflection mirroring.
  11807. * @param int $x abscissa of the point. Default is current x position
  11808. * @param int $y ordinate of the point. Default is current y position
  11809. * @access public
  11810. * @since 2.1.000 (2008-01-07)
  11811. * @see StartTransform(), StopTransform()
  11812. */
  11813. public function MirrorP($x='',$y='') {
  11814. $this->Scale(-100, -100, $x, $y);
  11815. }
  11816. /**
  11817. * Reflection against a straight line through point (x, y) with the gradient angle (angle).
  11818. * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
  11819. * @param int $x abscissa of the point. Default is current x position
  11820. * @param int $y ordinate of the point. Default is current y position
  11821. * @access public
  11822. * @since 2.1.000 (2008-01-07)
  11823. * @see StartTransform(), StopTransform()
  11824. */
  11825. public function MirrorL($angle=0, $x='',$y='') {
  11826. $this->Scale(-100, 100, $x, $y);
  11827. $this->Rotate(-2*($angle-90), $x, $y);
  11828. }
  11829. /**
  11830. * Translate graphic object horizontally.
  11831. * @param int $t_x movement to the right (or left for RTL)
  11832. * @access public
  11833. * @since 2.1.000 (2008-01-07)
  11834. * @see StartTransform(), StopTransform()
  11835. */
  11836. public function TranslateX($t_x) {
  11837. $this->Translate($t_x, 0);
  11838. }
  11839. /**
  11840. * Translate graphic object vertically.
  11841. * @param int $t_y movement to the bottom
  11842. * @access public
  11843. * @since 2.1.000 (2008-01-07)
  11844. * @see StartTransform(), StopTransform()
  11845. */
  11846. public function TranslateY($t_y) {
  11847. $this->Translate(0, $t_y);
  11848. }
  11849. /**
  11850. * Translate graphic object horizontally and vertically.
  11851. * @param int $t_x movement to the right
  11852. * @param int $t_y movement to the bottom
  11853. * @access public
  11854. * @since 2.1.000 (2008-01-07)
  11855. * @see StartTransform(), StopTransform()
  11856. */
  11857. public function Translate($t_x, $t_y) {
  11858. //calculate elements of transformation matrix
  11859. $tm = array();
  11860. $tm[0] = 1;
  11861. $tm[1] = 0;
  11862. $tm[2] = 0;
  11863. $tm[3] = 1;
  11864. $tm[4] = $t_x * $this->k;
  11865. $tm[5] = -$t_y * $this->k;
  11866. //translate the coordinate system
  11867. $this->Transform($tm);
  11868. }
  11869. /**
  11870. * Rotate object.
  11871. * @param float $angle angle in degrees for counter-clockwise rotation
  11872. * @param int $x abscissa of the rotation center. Default is current x position
  11873. * @param int $y ordinate of the rotation center. Default is current y position
  11874. * @access public
  11875. * @since 2.1.000 (2008-01-07)
  11876. * @see StartTransform(), StopTransform()
  11877. */
  11878. public function Rotate($angle, $x='', $y='') {
  11879. if ($x === '') {
  11880. $x = $this->x;
  11881. }
  11882. if ($y === '') {
  11883. $y = $this->y;
  11884. }
  11885. $y = ($this->h - $y) * $this->k;
  11886. $x *= $this->k;
  11887. //calculate elements of transformation matrix
  11888. $tm = array();
  11889. $tm[0] = cos(deg2rad($angle));
  11890. $tm[1] = sin(deg2rad($angle));
  11891. $tm[2] = -$tm[1];
  11892. $tm[3] = $tm[0];
  11893. $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
  11894. $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
  11895. //rotate the coordinate system around ($x,$y)
  11896. $this->Transform($tm);
  11897. }
  11898. /**
  11899. * Skew horizontally.
  11900. * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
  11901. * @param int $x abscissa of the skewing center. default is current x position
  11902. * @param int $y ordinate of the skewing center. default is current y position
  11903. * @access public
  11904. * @since 2.1.000 (2008-01-07)
  11905. * @see StartTransform(), StopTransform()
  11906. */
  11907. public function SkewX($angle_x, $x='', $y='') {
  11908. $this->Skew($angle_x, 0, $x, $y);
  11909. }
  11910. /**
  11911. * Skew vertically.
  11912. * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
  11913. * @param int $x abscissa of the skewing center. default is current x position
  11914. * @param int $y ordinate of the skewing center. default is current y position
  11915. * @access public
  11916. * @since 2.1.000 (2008-01-07)
  11917. * @see StartTransform(), StopTransform()
  11918. */
  11919. public function SkewY($angle_y, $x='', $y='') {
  11920. $this->Skew(0, $angle_y, $x, $y);
  11921. }
  11922. /**
  11923. * Skew.
  11924. * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
  11925. * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
  11926. * @param int $x abscissa of the skewing center. default is current x position
  11927. * @param int $y ordinate of the skewing center. default is current y position
  11928. * @access public
  11929. * @since 2.1.000 (2008-01-07)
  11930. * @see StartTransform(), StopTransform()
  11931. */
  11932. public function Skew($angle_x, $angle_y, $x='', $y='') {
  11933. if ($x === '') {
  11934. $x = $this->x;
  11935. }
  11936. if ($y === '') {
  11937. $y = $this->y;
  11938. }
  11939. if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
  11940. $this->Error('Please use values between -90 and +90 degrees for Skewing.');
  11941. }
  11942. $x *= $this->k;
  11943. $y = ($this->h - $y) * $this->k;
  11944. //calculate elements of transformation matrix
  11945. $tm = array();
  11946. $tm[0] = 1;
  11947. $tm[1] = tan(deg2rad($angle_y));
  11948. $tm[2] = tan(deg2rad($angle_x));
  11949. $tm[3] = 1;
  11950. $tm[4] = -$tm[2] * $y;
  11951. $tm[5] = -$tm[1] * $x;
  11952. //skew the coordinate system
  11953. $this->Transform($tm);
  11954. }
  11955. /**
  11956. * Apply graphic transformations.
  11957. * @param array $tm transformation matrix
  11958. * @access protected
  11959. * @since 2.1.000 (2008-01-07)
  11960. * @see StartTransform(), StopTransform()
  11961. */
  11962. protected function Transform($tm) {
  11963. $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
  11964. // add tranformation matrix
  11965. $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
  11966. // update transformation mark
  11967. if ($this->inxobj) {
  11968. // we are inside an XObject template
  11969. if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
  11970. $key = key($this->xobjects[$this->xobjid]['transfmrk']);
  11971. $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
  11972. }
  11973. } elseif (end($this->transfmrk[$this->page]) !== false) {
  11974. $key = key($this->transfmrk[$this->page]);
  11975. $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
  11976. }
  11977. }
  11978. // END TRANSFORMATIONS SECTION -------------------------
  11979. // START GRAPHIC FUNCTIONS SECTION ---------------------
  11980. // The following section is based on the code provided by David Hernandez Sanz
  11981. /**
  11982. * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page.
  11983. * @param float $width The width.
  11984. * @access public
  11985. * @since 1.0
  11986. * @see Line(), Rect(), Cell(), MultiCell()
  11987. */
  11988. public function SetLineWidth($width) {
  11989. //Set line width
  11990. $this->LineWidth = $width;
  11991. $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
  11992. if ($this->page > 0) {
  11993. $this->_out($this->linestyleWidth);
  11994. }
  11995. }
  11996. /**
  11997. * Returns the current the line width.
  11998. * @return int Line width
  11999. * @access public
  12000. * @since 2.1.000 (2008-01-07)
  12001. * @see Line(), SetLineWidth()
  12002. */
  12003. public function GetLineWidth() {
  12004. return $this->LineWidth;
  12005. }
  12006. /**
  12007. * Set line style.
  12008. * @param array $style Line style. Array with keys among the following:
  12009. * <ul>
  12010. * <li>width (float): Width of the line in user units.</li>
  12011. * <li>cap (string): Type of cap to put on the line. Possible values are:
  12012. * butt, round, square. The difference between "square" and "butt" is that
  12013. * "square" projects a flat end past the end of the line.</li>
  12014. * <li>join (string): Type of join. Possible values are: miter, round,
  12015. * bevel.</li>
  12016. * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
  12017. * series of length values, which are the lengths of the on and off dashes.
  12018. * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
  12019. * 1 off, 2 on, 1 off, ...</li>
  12020. * <li>phase (integer): Modifier on the dash pattern which is used to shift
  12021. * the point at which the pattern starts.</li>
  12022. * <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
  12023. * </ul>
  12024. * @param boolean $ret if true do not send the command.
  12025. * @return string the PDF command
  12026. * @access public
  12027. * @since 2.1.000 (2008-01-08)
  12028. */
  12029. public function SetLineStyle($style, $ret=false) {
  12030. $s = ''; // string to be returned
  12031. if (!is_array($style)) {
  12032. return;
  12033. }
  12034. extract($style);
  12035. if (isset($width)) {
  12036. $this->LineWidth = $width;
  12037. $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
  12038. $s .= $this->linestyleWidth.' ';
  12039. }
  12040. if (isset($cap)) {
  12041. $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
  12042. if (isset($ca[$cap])) {
  12043. $this->linestyleCap = $ca[$cap].' J';
  12044. $s .= $this->linestyleCap.' ';
  12045. }
  12046. }
  12047. if (isset($join)) {
  12048. $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
  12049. if (isset($ja[$join])) {
  12050. $this->linestyleJoin = $ja[$join].' j';
  12051. $s .= $this->linestyleJoin.' ';
  12052. }
  12053. }
  12054. if (isset($dash)) {
  12055. $dash_string = '';
  12056. if ($dash) {
  12057. if (preg_match('/^.+,/', $dash) > 0) {
  12058. $tab = explode(',', $dash);
  12059. } else {
  12060. $tab = array($dash);
  12061. }
  12062. $dash_string = '';
  12063. foreach ($tab as $i => $v) {
  12064. if ($i) {
  12065. $dash_string .= ' ';
  12066. }
  12067. $dash_string .= sprintf('%.2F', $v);
  12068. }
  12069. }
  12070. if (!isset($phase) OR !$dash) {
  12071. $phase = 0;
  12072. }
  12073. $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $phase);
  12074. $s .= $this->linestyleDash.' ';
  12075. }
  12076. if (isset($color)) {
  12077. $s .= $this->SetDrawColorArray($color, true).' ';
  12078. }
  12079. if (!$ret) {
  12080. $this->_out($s);
  12081. }
  12082. return $s;
  12083. }
  12084. /**
  12085. * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
  12086. * @param float $x Abscissa of point.
  12087. * @param float $y Ordinate of point.
  12088. * @access protected
  12089. * @since 2.1.000 (2008-01-08)
  12090. */
  12091. protected function _outPoint($x, $y) {
  12092. $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k));
  12093. }
  12094. /**
  12095. * Append a straight line segment from the current point to the point (x, y).
  12096. * The new current point shall be (x, y).
  12097. * @param float $x Abscissa of end point.
  12098. * @param float $y Ordinate of end point.
  12099. * @access protected
  12100. * @since 2.1.000 (2008-01-08)
  12101. */
  12102. protected function _outLine($x, $y) {
  12103. $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k));
  12104. }
  12105. /**
  12106. * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
  12107. * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
  12108. * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
  12109. * @param float $w Width.
  12110. * @param float $h Height.
  12111. * @param string $op options
  12112. * @access protected
  12113. * @since 2.1.000 (2008-01-08)
  12114. */
  12115. protected function _outRect($x, $y, $w, $h, $op) {
  12116. $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
  12117. }
  12118. /**
  12119. * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bézier control points.
  12120. * The new current point shall be (x3, y3).
  12121. * @param float $x1 Abscissa of control point 1.
  12122. * @param float $y1 Ordinate of control point 1.
  12123. * @param float $x2 Abscissa of control point 2.
  12124. * @param float $y2 Ordinate of control point 2.
  12125. * @param float $x3 Abscissa of end point.
  12126. * @param float $y3 Ordinate of end point.
  12127. * @access protected
  12128. * @since 2.1.000 (2008-01-08)
  12129. */
  12130. protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
  12131. $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));
  12132. }
  12133. /**
  12134. * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bézier control points.
  12135. * The new current point shall be (x3, y3).
  12136. * @param float $x2 Abscissa of control point 2.
  12137. * @param float $y2 Ordinate of control point 2.
  12138. * @param float $x3 Abscissa of end point.
  12139. * @param float $y3 Ordinate of end point.
  12140. * @access protected
  12141. * @since 4.9.019 (2010-04-26)
  12142. */
  12143. protected function _outCurveV($x2, $y2, $x3, $y3) {
  12144. $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
  12145. }
  12146. /**
  12147. * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bézier control points.
  12148. * The new current point shall be (x3, y3).
  12149. * @param float $x1 Abscissa of control point 1.
  12150. * @param float $y1 Ordinate of control point 1.
  12151. * @param float $x2 Abscissa of control point 2.
  12152. * @param float $y2 Ordinate of control point 2.
  12153. * @param float $x3 Abscissa of end point.
  12154. * @param float $y3 Ordinate of end point.
  12155. * @access protected
  12156. * @since 2.1.000 (2008-01-08)
  12157. */
  12158. protected function _outCurveY($x1, $y1, $x3, $y3) {
  12159. $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
  12160. }
  12161. /**
  12162. * Draws a line between two points.
  12163. * @param float $x1 Abscissa of first point.
  12164. * @param float $y1 Ordinate of first point.
  12165. * @param float $x2 Abscissa of second point.
  12166. * @param float $y2 Ordinate of second point.
  12167. * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12168. * @access public
  12169. * @since 1.0
  12170. * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
  12171. */
  12172. public function Line($x1, $y1, $x2, $y2, $style=array()) {
  12173. if (is_array($style)) {
  12174. $this->SetLineStyle($style);
  12175. }
  12176. $this->_outPoint($x1, $y1);
  12177. $this->_outLine($x2, $y2);
  12178. $this->_out('S');
  12179. }
  12180. /**
  12181. * Draws a rectangle.
  12182. * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
  12183. * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
  12184. * @param float $w Width.
  12185. * @param float $h Height.
  12186. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12187. * @param array $border_style Border style of rectangle. Array with keys among the following:
  12188. * <ul>
  12189. * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12190. * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12191. * </ul>
  12192. * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
  12193. * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12194. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12195. * @access public
  12196. * @since 1.0
  12197. * @see SetLineStyle()
  12198. */
  12199. public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
  12200. if (!(false === strpos($style, 'F')) AND !empty($fill_color)) {
  12201. $this->SetFillColorArray($fill_color);
  12202. }
  12203. $op = $this->getPathPaintOperator($style);
  12204. if ((!$border_style) OR (isset($border_style['all']))) {
  12205. if (isset($border_style['all']) AND $border_style['all']) {
  12206. $this->SetLineStyle($border_style['all']);
  12207. $border_style = array();
  12208. }
  12209. }
  12210. $this->_outRect($x, $y, $w, $h, $op);
  12211. if ($border_style) {
  12212. $border_style2 = array();
  12213. foreach ($border_style as $line => $value) {
  12214. $length = strlen($line);
  12215. for ($i = 0; $i < $length; ++$i) {
  12216. $border_style2[$line[$i]] = $value;
  12217. }
  12218. }
  12219. $border_style = $border_style2;
  12220. if (isset($border_style['L']) AND $border_style['L']) {
  12221. $this->Line($x, $y, $x, $y + $h, $border_style['L']);
  12222. }
  12223. if (isset($border_style['T']) AND $border_style['T']) {
  12224. $this->Line($x, $y, $x + $w, $y, $border_style['T']);
  12225. }
  12226. if (isset($border_style['R']) AND $border_style['R']) {
  12227. $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
  12228. }
  12229. if (isset($border_style['B']) AND $border_style['B']) {
  12230. $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
  12231. }
  12232. }
  12233. }
  12234. /**
  12235. * Draws a Bezier curve.
  12236. * The Bezier curve is a tangent to the line between the control points at
  12237. * either end of the curve.
  12238. * @param float $x0 Abscissa of start point.
  12239. * @param float $y0 Ordinate of start point.
  12240. * @param float $x1 Abscissa of control point 1.
  12241. * @param float $y1 Ordinate of control point 1.
  12242. * @param float $x2 Abscissa of control point 2.
  12243. * @param float $y2 Ordinate of control point 2.
  12244. * @param float $x3 Abscissa of end point.
  12245. * @param float $y3 Ordinate of end point.
  12246. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12247. * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12248. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12249. * @access public
  12250. * @see SetLineStyle()
  12251. * @since 2.1.000 (2008-01-08)
  12252. */
  12253. public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
  12254. if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
  12255. $this->SetFillColorArray($fill_color);
  12256. }
  12257. $op = $this->getPathPaintOperator($style);
  12258. if ($line_style) {
  12259. $this->SetLineStyle($line_style);
  12260. }
  12261. $this->_outPoint($x0, $y0);
  12262. $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
  12263. $this->_out($op);
  12264. }
  12265. /**
  12266. * Draws a poly-Bezier curve.
  12267. * Each Bezier curve segment is a tangent to the line between the control points at
  12268. * either end of the curve.
  12269. * @param float $x0 Abscissa of start point.
  12270. * @param float $y0 Ordinate of start point.
  12271. * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
  12272. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12273. * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12274. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12275. * @access public
  12276. * @see SetLineStyle()
  12277. * @since 3.0008 (2008-05-12)
  12278. */
  12279. public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
  12280. if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
  12281. $this->SetFillColorArray($fill_color);
  12282. }
  12283. $op = $this->getPathPaintOperator($style);
  12284. if ($op == 'f') {
  12285. $line_style = array();
  12286. }
  12287. if ($line_style) {
  12288. $this->SetLineStyle($line_style);
  12289. }
  12290. $this->_outPoint($x0, $y0);
  12291. foreach ($segments as $segment) {
  12292. list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
  12293. $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
  12294. }
  12295. $this->_out($op);
  12296. }
  12297. /**
  12298. * Draws an ellipse.
  12299. * An ellipse is formed from n Bezier curves.
  12300. * @param float $x0 Abscissa of center point.
  12301. * @param float $y0 Ordinate of center point.
  12302. * @param float $rx Horizontal radius.
  12303. * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
  12304. * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
  12305. * @param float $astart: Angle start of draw line. Default value: 0.
  12306. * @param float $afinish: Angle finish of draw line. Default value: 360.
  12307. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12308. * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12309. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12310. * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse.
  12311. * @author Nicola Asuni
  12312. * @access public
  12313. * @since 2.1.000 (2008-01-08)
  12314. */
  12315. public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
  12316. if ($this->empty_string($ry) OR ($ry == 0)) {
  12317. $ry = $rx;
  12318. }
  12319. if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
  12320. $this->SetFillColorArray($fill_color);
  12321. }
  12322. $op = $this->getPathPaintOperator($style);
  12323. if ($op == 'f') {
  12324. $line_style = array();
  12325. }
  12326. if ($line_style) {
  12327. $this->SetLineStyle($line_style);
  12328. }
  12329. $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc);
  12330. $this->_out($op);
  12331. }
  12332. /**
  12333. * Append an elliptical arc to the current path.
  12334. * An ellipse is formed from n Bezier curves.
  12335. * @param float $xc Abscissa of center point.
  12336. * @param float $yc Ordinate of center point.
  12337. * @param float $rx Horizontal radius.
  12338. * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
  12339. * @param float $xang: Angle between the X-axis and the major axis of the ellipse. Default value: 0.
  12340. * @param float $angs: Angle start of draw line. Default value: 0.
  12341. * @param float $angf: Angle finish of draw line. Default value: 360.
  12342. * @param boolean $pie if true do not mark the border point (used to draw pie sectors).
  12343. * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse.
  12344. * @author Nicola Asuni
  12345. * @access protected
  12346. * @since 4.9.019 (2010-04-26)
  12347. */
  12348. protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2) {
  12349. $k = $this->k;
  12350. if ($nc < 2) {
  12351. $nc = 2;
  12352. }
  12353. if ($pie) {
  12354. // center of the arc
  12355. $this->_outPoint($xc, $yc);
  12356. }
  12357. $xang = deg2rad((float) $xang);
  12358. $angs = deg2rad((float) $angs);
  12359. $angf = deg2rad((float) $angf);
  12360. $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
  12361. $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
  12362. if ($as < 0) {
  12363. $as += (2 * M_PI);
  12364. }
  12365. if ($af < 0) {
  12366. $af += (2 * M_PI);
  12367. }
  12368. if ($as > $af) {
  12369. // reverse rotation go clockwise
  12370. $as -= (2 * M_PI);
  12371. }
  12372. $total_angle = ($af - $as);
  12373. if ($nc < 2) {
  12374. $nc = 2;
  12375. }
  12376. // total arcs to draw
  12377. $nc *= (2 * abs($total_angle) / M_PI);
  12378. $nc = round($nc) + 1;
  12379. // angle of each arc
  12380. $arcang = $total_angle / $nc;
  12381. // center point in PDF coordiantes
  12382. $x0 = $xc;
  12383. $y0 = ($this->h - $yc);
  12384. // starting angle
  12385. $ang = $as;
  12386. $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
  12387. $cos_xang = cos($xang);
  12388. $sin_xang = sin($xang);
  12389. $cos_ang = cos($ang);
  12390. $sin_ang = sin($ang);
  12391. // first arc point
  12392. $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
  12393. $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
  12394. // first Bezier control point
  12395. $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
  12396. $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
  12397. if ($pie) {
  12398. $this->_outLine($px1, $this->h - $py1);
  12399. } else {
  12400. $this->_outPoint($px1, $this->h - $py1);
  12401. }
  12402. // draw arcs
  12403. for ($i = 1; $i <= $nc; ++$i) {
  12404. // starting angle
  12405. $ang = $as + ($i * $arcang);
  12406. $cos_xang = cos($xang);
  12407. $sin_xang = sin($xang);
  12408. $cos_ang = cos($ang);
  12409. $sin_ang = sin($ang);
  12410. // second arc point
  12411. $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
  12412. $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
  12413. // second Bezier control point
  12414. $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
  12415. $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
  12416. // draw arc
  12417. $this->_outCurve(($px1 + $qx1), ($this->h - ($py1 + $qy1)), ($px2 - $qx2), ($this->h - ($py2 - $qy2)), $px2, ($this->h - $py2));
  12418. // move to next point
  12419. $px1 = $px2;
  12420. $py1 = $py2;
  12421. $qx1 = $qx2;
  12422. $qy1 = $qy2;
  12423. }
  12424. if ($pie) {
  12425. $this->_outLine($xc, $yc);
  12426. }
  12427. }
  12428. /**
  12429. * Draws a circle.
  12430. * A circle is formed from n Bezier curves.
  12431. * @param float $x0 Abscissa of center point.
  12432. * @param float $y0 Ordinate of center point.
  12433. * @param float $r Radius.
  12434. * @param float $angstr: Angle start of draw line. Default value: 0.
  12435. * @param float $angend: Angle finish of draw line. Default value: 360.
  12436. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12437. * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12438. * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
  12439. * @param integer $nc Number of curves used to draw a 90 degrees portion of circle.
  12440. * @access public
  12441. * @since 2.1.000 (2008-01-08)
  12442. */
  12443. public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
  12444. $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
  12445. }
  12446. /**
  12447. * Draws a polygonal line
  12448. * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
  12449. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12450. * @param array $line_style Line style of polygon. Array with keys among the following:
  12451. * <ul>
  12452. * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12453. * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12454. * </ul>
  12455. * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
  12456. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12457. * @param boolean $closed if true the polygon is closes, otherwise will remain open
  12458. * @access public
  12459. * @since 4.8.003 (2009-09-15)
  12460. */
  12461. public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
  12462. $this->Polygon($p, $style, $line_style, $fill_color, false);
  12463. }
  12464. /**
  12465. * Draws a polygon.
  12466. * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
  12467. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12468. * @param array $line_style Line style of polygon. Array with keys among the following:
  12469. * <ul>
  12470. * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12471. * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12472. * </ul>
  12473. * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
  12474. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12475. * @param boolean $closed if true the polygon is closes, otherwise will remain open
  12476. * @access public
  12477. * @since 2.1.000 (2008-01-08)
  12478. */
  12479. public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
  12480. $nc = count($p); // number of coordinates
  12481. $np = $nc / 2; // number of points
  12482. if ($closed) {
  12483. // close polygon by adding the first 2 points at the end (one line)
  12484. for ($i = 0; $i < 4; ++$i) {
  12485. $p[$nc + $i] = $p[$i];
  12486. }
  12487. // copy style for the last added line
  12488. if (isset($line_style[0])) {
  12489. $line_style[$np] = $line_style[0];
  12490. }
  12491. $nc += 4;
  12492. }
  12493. if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
  12494. $this->SetFillColorArray($fill_color);
  12495. }
  12496. $op = $this->getPathPaintOperator($style);
  12497. if ($op == 'f') {
  12498. $line_style = array();
  12499. }
  12500. $draw = true;
  12501. if ($line_style) {
  12502. if (isset($line_style['all'])) {
  12503. $this->SetLineStyle($line_style['all']);
  12504. } else {
  12505. $draw = false;
  12506. if ($op == 'B') {
  12507. // draw fill
  12508. $op = 'f';
  12509. $this->_outPoint($p[0], $p[1]);
  12510. for ($i = 2; $i < $nc; $i = $i + 2) {
  12511. $this->_outLine($p[$i], $p[$i + 1]);
  12512. }
  12513. $this->_out($op);
  12514. }
  12515. // draw outline
  12516. $this->_outPoint($p[0], $p[1]);
  12517. for ($i = 2; $i < $nc; $i = $i + 2) {
  12518. $line_num = ($i / 2) - 1;
  12519. if (isset($line_style[$line_num])) {
  12520. if ($line_style[$line_num] != 0) {
  12521. if (is_array($line_style[$line_num])) {
  12522. $this->_out('S');
  12523. $this->SetLineStyle($line_style[$line_num]);
  12524. $this->_outPoint($p[$i - 2], $p[$i - 1]);
  12525. $this->_outLine($p[$i], $p[$i + 1]);
  12526. $this->_out('S');
  12527. $this->_outPoint($p[$i], $p[$i + 1]);
  12528. } else {
  12529. $this->_outLine($p[$i], $p[$i + 1]);
  12530. }
  12531. }
  12532. } else {
  12533. $this->_outLine($p[$i], $p[$i + 1]);
  12534. }
  12535. }
  12536. $this->_out($op);
  12537. }
  12538. }
  12539. if ($draw) {
  12540. $this->_outPoint($p[0], $p[1]);
  12541. for ($i = 2; $i < $nc; $i = $i + 2) {
  12542. $this->_outLine($p[$i], $p[$i + 1]);
  12543. }
  12544. $this->_out($op);
  12545. }
  12546. }
  12547. /**
  12548. * Draws a regular polygon.
  12549. * @param float $x0 Abscissa of center point.
  12550. * @param float $y0 Ordinate of center point.
  12551. * @param float $r: Radius of inscribed circle.
  12552. * @param integer $ns Number of sides.
  12553. * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
  12554. * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
  12555. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12556. * @param array $line_style Line style of polygon sides. Array with keys among the following:
  12557. * <ul>
  12558. * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12559. * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12560. * </ul>
  12561. * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
  12562. * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
  12563. * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
  12564. * <ul>
  12565. * <li>D or empty string: Draw (default).</li>
  12566. * <li>F: Fill.</li>
  12567. * <li>DF or FD: Draw and fill.</li>
  12568. * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
  12569. * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
  12570. * </ul>
  12571. * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12572. * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
  12573. * @access public
  12574. * @since 2.1.000 (2008-01-08)
  12575. */
  12576. 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()) {
  12577. if (3 > $ns) {
  12578. $ns = 3;
  12579. }
  12580. if ($draw_circle) {
  12581. $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
  12582. }
  12583. $p = array();
  12584. for ($i = 0; $i < $ns; ++$i) {
  12585. $a = $angle + ($i * 360 / $ns);
  12586. $a_rad = deg2rad((float) $a);
  12587. $p[] = $x0 + ($r * sin($a_rad));
  12588. $p[] = $y0 + ($r * cos($a_rad));
  12589. }
  12590. $this->Polygon($p, $style, $line_style, $fill_color);
  12591. }
  12592. /**
  12593. * Draws a star polygon
  12594. * @param float $x0 Abscissa of center point.
  12595. * @param float $y0 Ordinate of center point.
  12596. * @param float $r Radius of inscribed circle.
  12597. * @param integer $nv Number of vertices.
  12598. * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
  12599. * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
  12600. * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
  12601. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12602. * @param array $line_style Line style of polygon sides. Array with keys among the following:
  12603. * <ul>
  12604. * <li>all: Line style of all sides. Array like for
  12605. * {@link SetLineStyle SetLineStyle}.</li>
  12606. * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
  12607. * </ul>
  12608. * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
  12609. * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
  12610. * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
  12611. * <ul>
  12612. * <li>D or empty string: Draw (default).</li>
  12613. * <li>F: Fill.</li>
  12614. * <li>DF or FD: Draw and fill.</li>
  12615. * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
  12616. * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
  12617. * </ul>
  12618. * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12619. * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
  12620. * @access public
  12621. * @since 2.1.000 (2008-01-08)
  12622. */
  12623. 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()) {
  12624. if ($nv < 2) {
  12625. $nv = 2;
  12626. }
  12627. if ($draw_circle) {
  12628. $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
  12629. }
  12630. $p2 = array();
  12631. $visited = array();
  12632. for ($i = 0; $i < $nv; ++$i) {
  12633. $a = $angle + ($i * 360 / $nv);
  12634. $a_rad = deg2rad((float) $a);
  12635. $p2[] = $x0 + ($r * sin($a_rad));
  12636. $p2[] = $y0 + ($r * cos($a_rad));
  12637. $visited[] = false;
  12638. }
  12639. $p = array();
  12640. $i = 0;
  12641. do {
  12642. $p[] = $p2[$i * 2];
  12643. $p[] = $p2[($i * 2) + 1];
  12644. $visited[$i] = true;
  12645. $i += $ng;
  12646. $i %= $nv;
  12647. } while (!$visited[$i]);
  12648. $this->Polygon($p, $style, $line_style, $fill_color);
  12649. }
  12650. /**
  12651. * Draws a rounded rectangle.
  12652. * @param float $x Abscissa of upper-left corner.
  12653. * @param float $y Ordinate of upper-left corner.
  12654. * @param float $w Width.
  12655. * @param float $h Height.
  12656. * @param float $r the radius of the circle used to round off the corners of the rectangle.
  12657. * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
  12658. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12659. * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12660. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12661. * @access public
  12662. * @since 2.1.000 (2008-01-08)
  12663. */
  12664. public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
  12665. $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
  12666. }
  12667. /**
  12668. * Draws a rounded rectangle.
  12669. * @param float $x Abscissa of upper-left corner.
  12670. * @param float $y Ordinate of upper-left corner.
  12671. * @param float $w Width.
  12672. * @param float $h Height.
  12673. * @param float $rx the x-axis radius of the ellipse used to round off the corners of the rectangle.
  12674. * @param float $ry the y-axis radius of the ellipse used to round off the corners of the rectangle.
  12675. * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
  12676. * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
  12677. * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
  12678. * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
  12679. * @access public
  12680. * @since 4.9.019 (2010-04-22)
  12681. */
  12682. public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
  12683. if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
  12684. // Not rounded
  12685. $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
  12686. return;
  12687. }
  12688. // Rounded
  12689. if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
  12690. $this->SetFillColorArray($fill_color);
  12691. }
  12692. $op = $this->getPathPaintOperator($style);
  12693. if ($op == 'f') {
  12694. $border_style = array();
  12695. }
  12696. if ($border_style) {
  12697. $this->SetLineStyle($border_style);
  12698. }
  12699. $MyArc = 4 / 3 * (sqrt(2) - 1);
  12700. $this->_outPoint($x + $rx, $y);
  12701. $xc = $x + $w - $rx;
  12702. $yc = $y + $ry;
  12703. $this->_outLine($xc, $y);
  12704. if ($round_corner[0]) {
  12705. $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
  12706. } else {
  12707. $this->_outLine($x + $w, $y);
  12708. }
  12709. $xc = $x + $w - $rx;
  12710. $yc = $y + $h - $ry;
  12711. $this->_outLine($x + $w, $yc);
  12712. if ($round_corner[1]) {
  12713. $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
  12714. } else {
  12715. $this->_outLine($x + $w, $y + $h);
  12716. }
  12717. $xc = $x + $rx;
  12718. $yc = $y + $h - $ry;
  12719. $this->_outLine($xc, $y + $h);
  12720. if ($round_corner[2]) {
  12721. $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
  12722. } else {
  12723. $this->_outLine($x, $y + $h);
  12724. }
  12725. $xc = $x + $rx;
  12726. $yc = $y + $ry;
  12727. $this->_outLine($x, $yc);
  12728. if ($round_corner[3]) {
  12729. $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
  12730. } else {
  12731. $this->_outLine($x, $y);
  12732. $this->_outLine($x + $rx, $y);
  12733. }
  12734. $this->_out($op);
  12735. }
  12736. /**
  12737. * Draws a grahic arrow.
  12738. * @param float $x0 Abscissa of first point.
  12739. * @param float $y0 Ordinate of first point.
  12740. * @param float $x0 Abscissa of second point.
  12741. * @param float $y1 Ordinate of second point.
  12742. * @param int $head_style (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
  12743. * @param float $arm_size length of arrowhead arms
  12744. * @param int $arm_angle angle between an arm and the shaft
  12745. * @author Piotr Galecki, Nicola Asuni, Andy Meier
  12746. * @since 4.6.018 (2009-07-10)
  12747. */
  12748. public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
  12749. // getting arrow direction angle
  12750. // 0 deg angle is when both arms go along X axis. angle grows clockwise.
  12751. $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
  12752. if ($dir_angle < 0) {
  12753. $dir_angle += (2 * M_PI);
  12754. }
  12755. $arm_angle = deg2rad($arm_angle);
  12756. $sx1 = $x1;
  12757. $sy1 = $y1;
  12758. if ($head_style > 0) {
  12759. // calculate the stopping point for the arrow shaft
  12760. $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
  12761. $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
  12762. }
  12763. // main arrow line / shaft
  12764. $this->Line($x0, $y0, $sx1, $sy1);
  12765. // left arrowhead arm tip
  12766. $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
  12767. $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
  12768. // right arrowhead arm tip
  12769. $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
  12770. $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
  12771. $mode = 'D';
  12772. $style = array();
  12773. switch ($head_style) {
  12774. case 0: {
  12775. // draw only arrowhead arms
  12776. $mode = 'D';
  12777. $style = array(1, 1, 0);
  12778. break;
  12779. }
  12780. case 1: {
  12781. // draw closed arrowhead, but no fill
  12782. $mode = 'D';
  12783. break;
  12784. }
  12785. case 2: {
  12786. // closed and filled arrowhead
  12787. $mode = 'DF';
  12788. break;
  12789. }
  12790. case 3: {
  12791. // filled arrowhead
  12792. $mode = 'F';
  12793. break;
  12794. }
  12795. }
  12796. $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
  12797. }
  12798. // END GRAPHIC FUNCTIONS SECTION -----------------------
  12799. // BIDIRECTIONAL TEXT SECTION --------------------------
  12800. /**
  12801. * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
  12802. * @param string $str string to manipulate.
  12803. * @param bool $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
  12804. * @param bool $forcertl if true forces RTL text direction
  12805. * @return string
  12806. * @access protected
  12807. * @author Nicola Asuni
  12808. * @since 2.1.000 (2008-01-08)
  12809. */
  12810. protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
  12811. return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl);
  12812. }
  12813. /**
  12814. * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
  12815. * @param array $arr array of unicode values.
  12816. * @param string $str string to manipulate (or empty value).
  12817. * @param bool $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
  12818. * @param bool $forcertl if true forces RTL text direction
  12819. * @return string
  12820. * @access protected
  12821. * @author Nicola Asuni
  12822. * @since 4.9.000 (2010-03-27)
  12823. */
  12824. protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) {
  12825. return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom);
  12826. }
  12827. /**
  12828. * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
  12829. * @param array $ta array of characters composing the string.
  12830. * @param string $str string to process
  12831. * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
  12832. * @return array of unicode chars
  12833. * @author Nicola Asuni
  12834. * @access protected
  12835. * @since 2.4.000 (2008-03-06)
  12836. */
  12837. protected function utf8Bidi($ta, $str='', $forcertl=false) {
  12838. // paragraph embedding level
  12839. $pel = 0;
  12840. // max level
  12841. $maxlevel = 0;
  12842. if ($this->empty_string($str)) {
  12843. // create string from array
  12844. $str = $this->UTF8ArrSubString($ta);
  12845. }
  12846. // check if string contains arabic text
  12847. if (preg_match($this->unicode->uni_RE_PATTERN_ARABIC, $str)) {
  12848. $arabic = true;
  12849. } else {
  12850. $arabic = false;
  12851. }
  12852. // check if string contains RTL text
  12853. if (!($forcertl OR $arabic OR preg_match($this->unicode->uni_RE_PATTERN_RTL, $str))) {
  12854. return $ta;
  12855. }
  12856. // get number of chars
  12857. $numchars = count($ta);
  12858. if ($forcertl == 'R') {
  12859. $pel = 1;
  12860. } elseif ($forcertl == 'L') {
  12861. $pel = 0;
  12862. } else {
  12863. // P2. In each paragraph, find the first character of type L, AL, or R.
  12864. // 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.
  12865. for ($i=0; $i < $numchars; ++$i) {
  12866. $type = $this->unicode->uni_type[$ta[$i]];
  12867. if ($type == 'L') {
  12868. $pel = 0;
  12869. break;
  12870. } elseif (($type == 'AL') OR ($type == 'R')) {
  12871. $pel = 1;
  12872. break;
  12873. }
  12874. }
  12875. }
  12876. // Current Embedding Level
  12877. $cel = $pel;
  12878. // directional override status
  12879. $dos = 'N';
  12880. $remember = array();
  12881. // start-of-level-run
  12882. $sor = $pel % 2 ? 'R' : 'L';
  12883. $eor = $sor;
  12884. // Array of characters data
  12885. $chardata = Array();
  12886. // 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.
  12887. // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
  12888. for ($i=0; $i < $numchars; ++$i) {
  12889. if ($ta[$i] == $this->unicode->uni_RLE) {
  12890. // X2. With each RLE, compute the least greater odd embedding level.
  12891. // 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.
  12892. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
  12893. $next_level = $cel + ($cel % 2) + 1;
  12894. if ($next_level < 62) {
  12895. $remember[] = array('num' => $this->unicode->uni_RLE, 'cel' => $cel, 'dos' => $dos);
  12896. $cel = $next_level;
  12897. $dos = 'N';
  12898. $sor = $eor;
  12899. $eor = $cel % 2 ? 'R' : 'L';
  12900. }
  12901. } elseif ($ta[$i] == $this->unicode->uni_LRE) {
  12902. // X3. With each LRE, compute the least greater even embedding level.
  12903. // 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.
  12904. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
  12905. $next_level = $cel + 2 - ($cel % 2);
  12906. if ( $next_level < 62 ) {
  12907. $remember[] = array('num' => $this->unicode->uni_LRE, 'cel' => $cel, 'dos' => $dos);
  12908. $cel = $next_level;
  12909. $dos = 'N';
  12910. $sor = $eor;
  12911. $eor = $cel % 2 ? 'R' : 'L';
  12912. }
  12913. } elseif ($ta[$i] == $this->unicode->uni_RLO) {
  12914. // X4. With each RLO, compute the least greater odd embedding level.
  12915. // 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.
  12916. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
  12917. $next_level = $cel + ($cel % 2) + 1;
  12918. if ($next_level < 62) {
  12919. $remember[] = array('num' => $this->unicode->uni_RLO, 'cel' => $cel, 'dos' => $dos);
  12920. $cel = $next_level;
  12921. $dos = 'R';
  12922. $sor = $eor;
  12923. $eor = $cel % 2 ? 'R' : 'L';
  12924. }
  12925. } elseif ($ta[$i] == $this->unicode->uni_LRO) {
  12926. // X5. With each LRO, compute the least greater even embedding level.
  12927. // 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.
  12928. // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
  12929. $next_level = $cel + 2 - ($cel % 2);
  12930. if ( $next_level < 62 ) {
  12931. $remember[] = array('num' => $this->unicode->uni_LRO, 'cel' => $cel, 'dos' => $dos);
  12932. $cel = $next_level;
  12933. $dos = 'L';
  12934. $sor = $eor;
  12935. $eor = $cel % 2 ? 'R' : 'L';
  12936. }
  12937. } elseif ($ta[$i] == $this->unicode->uni_PDF) {
  12938. // 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.
  12939. if (count($remember)) {
  12940. $last = count($remember ) - 1;
  12941. if (($remember[$last]['num'] == $this->unicode->uni_RLE) OR
  12942. ($remember[$last]['num'] == $this->unicode->uni_LRE) OR
  12943. ($remember[$last]['num'] == $this->unicode->uni_RLO) OR
  12944. ($remember[$last]['num'] == $this->unicode->uni_LRO)) {
  12945. $match = array_pop($remember);
  12946. $cel = $match['cel'];
  12947. $dos = $match['dos'];
  12948. $sor = $eor;
  12949. $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
  12950. }
  12951. }
  12952. } elseif (($ta[$i] != $this->unicode->uni_RLE) AND
  12953. ($ta[$i] != $this->unicode->uni_LRE) AND
  12954. ($ta[$i] != $this->unicode->uni_RLO) AND
  12955. ($ta[$i] != $this->unicode->uni_LRO) AND
  12956. ($ta[$i] != $this->unicode->uni_PDF)) {
  12957. // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
  12958. // a. Set the level of the current character to the current embedding level.
  12959. // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
  12960. if ($dos != 'N') {
  12961. $chardir = $dos;
  12962. } else {
  12963. if (isset($this->unicode->uni_type[$ta[$i]])) {
  12964. $chardir = $this->unicode->uni_type[$ta[$i]];
  12965. } else {
  12966. $chardir = 'L';
  12967. }
  12968. }
  12969. // stores string characters and other information
  12970. $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
  12971. }
  12972. } // end for each char
  12973. // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
  12974. // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
  12975. // 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.
  12976. // 3.3.3 Resolving Weak Types
  12977. // 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.
  12978. // Nonspacing marks are now resolved based on the previous characters.
  12979. $numchars = count($chardata);
  12980. // 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.
  12981. $prevlevel = -1; // track level changes
  12982. $levcount = 0; // counts consecutive chars at the same level
  12983. for ($i=0; $i < $numchars; ++$i) {
  12984. if ($chardata[$i]['type'] == 'NSM') {
  12985. if ($levcount) {
  12986. $chardata[$i]['type'] = $chardata[$i]['sor'];
  12987. } elseif ($i > 0) {
  12988. $chardata[$i]['type'] = $chardata[($i-1)]['type'];
  12989. }
  12990. }
  12991. if ($chardata[$i]['level'] != $prevlevel) {
  12992. $levcount = 0;
  12993. } else {
  12994. ++$levcount;
  12995. }
  12996. $prevlevel = $chardata[$i]['level'];
  12997. }
  12998. // 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.
  12999. $prevlevel = -1;
  13000. $levcount = 0;
  13001. for ($i=0; $i < $numchars; ++$i) {
  13002. if ($chardata[$i]['char'] == 'EN') {
  13003. for ($j=$levcount; $j >= 0; $j--) {
  13004. if ($chardata[$j]['type'] == 'AL') {
  13005. $chardata[$i]['type'] = 'AN';
  13006. } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
  13007. break;
  13008. }
  13009. }
  13010. }
  13011. if ($chardata[$i]['level'] != $prevlevel) {
  13012. $levcount = 0;
  13013. } else {
  13014. ++$levcount;
  13015. }
  13016. $prevlevel = $chardata[$i]['level'];
  13017. }
  13018. // W3. Change all ALs to R.
  13019. for ($i=0; $i < $numchars; ++$i) {
  13020. if ($chardata[$i]['type'] == 'AL') {
  13021. $chardata[$i]['type'] = 'R';
  13022. }
  13023. }
  13024. // 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.
  13025. $prevlevel = -1;
  13026. $levcount = 0;
  13027. for ($i=0; $i < $numchars; ++$i) {
  13028. if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
  13029. if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
  13030. $chardata[$i]['type'] = 'EN';
  13031. } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
  13032. $chardata[$i]['type'] = 'EN';
  13033. } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
  13034. $chardata[$i]['type'] = 'AN';
  13035. }
  13036. }
  13037. if ($chardata[$i]['level'] != $prevlevel) {
  13038. $levcount = 0;
  13039. } else {
  13040. ++$levcount;
  13041. }
  13042. $prevlevel = $chardata[$i]['level'];
  13043. }
  13044. // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
  13045. $prevlevel = -1;
  13046. $levcount = 0;
  13047. for ($i=0; $i < $numchars; ++$i) {
  13048. if ($chardata[$i]['type'] == 'ET') {
  13049. if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
  13050. $chardata[$i]['type'] = 'EN';
  13051. } else {
  13052. $j = $i+1;
  13053. while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
  13054. if ($chardata[$j]['type'] == 'EN') {
  13055. $chardata[$i]['type'] = 'EN';
  13056. break;
  13057. } elseif ($chardata[$j]['type'] != 'ET') {
  13058. break;
  13059. }
  13060. ++$j;
  13061. }
  13062. }
  13063. }
  13064. if ($chardata[$i]['level'] != $prevlevel) {
  13065. $levcount = 0;
  13066. } else {
  13067. ++$levcount;
  13068. }
  13069. $prevlevel = $chardata[$i]['level'];
  13070. }
  13071. // W6. Otherwise, separators and terminators change to Other Neutral.
  13072. $prevlevel = -1;
  13073. $levcount = 0;
  13074. for ($i=0; $i < $numchars; ++$i) {
  13075. if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
  13076. $chardata[$i]['type'] = 'ON';
  13077. }
  13078. if ($chardata[$i]['level'] != $prevlevel) {
  13079. $levcount = 0;
  13080. } else {
  13081. ++$levcount;
  13082. }
  13083. $prevlevel = $chardata[$i]['level'];
  13084. }
  13085. //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.
  13086. $prevlevel = -1;
  13087. $levcount = 0;
  13088. for ($i=0; $i < $numchars; ++$i) {
  13089. if ($chardata[$i]['char'] == 'EN') {
  13090. for ($j=$levcount; $j >= 0; $j--) {
  13091. if ($chardata[$j]['type'] == 'L') {
  13092. $chardata[$i]['type'] = 'L';
  13093. } elseif ($chardata[$j]['type'] == 'R') {
  13094. break;
  13095. }
  13096. }
  13097. }
  13098. if ($chardata[$i]['level'] != $prevlevel) {
  13099. $levcount = 0;
  13100. } else {
  13101. ++$levcount;
  13102. }
  13103. $prevlevel = $chardata[$i]['level'];
  13104. }
  13105. // 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.
  13106. $prevlevel = -1;
  13107. $levcount = 0;
  13108. for ($i=0; $i < $numchars; ++$i) {
  13109. if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
  13110. if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
  13111. $chardata[$i]['type'] = 'L';
  13112. } elseif (($chardata[$i]['type'] == 'N') AND
  13113. (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
  13114. (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
  13115. $chardata[$i]['type'] = 'R';
  13116. } elseif ($chardata[$i]['type'] == 'N') {
  13117. // N2. Any remaining neutrals take the embedding direction
  13118. $chardata[$i]['type'] = $chardata[$i]['sor'];
  13119. }
  13120. } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
  13121. // first char
  13122. if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
  13123. $chardata[$i]['type'] = 'L';
  13124. } elseif (($chardata[$i]['type'] == 'N') AND
  13125. (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
  13126. (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
  13127. $chardata[$i]['type'] = 'R';
  13128. } elseif ($chardata[$i]['type'] == 'N') {
  13129. // N2. Any remaining neutrals take the embedding direction
  13130. $chardata[$i]['type'] = $chardata[$i]['sor'];
  13131. }
  13132. } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
  13133. //last char
  13134. if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
  13135. $chardata[$i]['type'] = 'L';
  13136. } elseif (($chardata[$i]['type'] == 'N') AND
  13137. (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
  13138. (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
  13139. $chardata[$i]['type'] = 'R';
  13140. } elseif ($chardata[$i]['type'] == 'N') {
  13141. // N2. Any remaining neutrals take the embedding direction
  13142. $chardata[$i]['type'] = $chardata[$i]['sor'];
  13143. }
  13144. } elseif ($chardata[$i]['type'] == 'N') {
  13145. // N2. Any remaining neutrals take the embedding direction
  13146. $chardata[$i]['type'] = $chardata[$i]['sor'];
  13147. }
  13148. if ($chardata[$i]['level'] != $prevlevel) {
  13149. $levcount = 0;
  13150. } else {
  13151. ++$levcount;
  13152. }
  13153. $prevlevel = $chardata[$i]['level'];
  13154. }
  13155. // 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.
  13156. // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
  13157. for ($i=0; $i < $numchars; ++$i) {
  13158. $odd = $chardata[$i]['level'] % 2;
  13159. if ($odd) {
  13160. if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR (