Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

31552 lines
1.1MB

  1. <?php
  2. // ******************************************************************************
  3. // Software: mPDF, Unicode-HTML Free PDF generator *
  4. // Version: 6.1 based on *
  5. // FPDF by Olivier PLATHEY *
  6. // HTML2FPDF by Renato Coelho *
  7. // Date: 2016-03-25 *
  8. // Author: Ian Back <ianb@bpm1.com> *
  9. // License: GPL *
  10. // *
  11. // Changes: See changelog.txt *
  12. // ******************************************************************************
  13. define('mPDF_VERSION', '6.1');
  14. //Scale factor
  15. define('_MPDFK', (72 / 25.4));
  16. // Specify which font metrics to use:
  17. // 'winTypo' uses sTypoAscender etc from the OS/2 table and is the one usually recommended - BUT
  18. // 'win' use WinAscent etc from OS/2 and inpractice seems to be used more commonly in Windows environment
  19. // 'mac' uses Ascender etc from hhea table, and is used on Mac/OSX environment
  20. if (!defined('_FONT_DESCRIPTOR')) {
  21. define("_FONT_DESCRIPTOR", 'win'); // Values: '' [BLANK] or 'win', 'mac', 'winTypo'
  22. }
  23. /* -- HTML-CSS -- */
  24. define('_BORDER_ALL', 15);
  25. define('_BORDER_TOP', 8);
  26. define('_BORDER_RIGHT', 4);
  27. define('_BORDER_BOTTOM', 2);
  28. define('_BORDER_LEFT', 1);
  29. /* -- END HTML-CSS -- */
  30. // mPDF 6.0
  31. // Used for $textvars - user settings via CSS
  32. define('FD_UNDERLINE', 1); // font-decoration
  33. define('FD_LINETHROUGH', 2);
  34. define('FD_OVERLINE', 4);
  35. define('FA_SUPERSCRIPT', 8); // font-(vertical)-align
  36. define('FA_SUBSCRIPT', 16);
  37. define('FT_UPPERCASE', 32); // font-transform
  38. define('FT_LOWERCASE', 64);
  39. define('FT_CAPITALIZE', 128);
  40. define('FC_KERNING', 256); // font-(other)-controls
  41. define('FC_SMALLCAPS', 512);
  42. if (!defined('_MPDF_PATH')) {
  43. define('_MPDF_PATH', dirname(preg_replace('/\\\\/', '/', __FILE__)) . '/');
  44. }
  45. if (!defined('_MPDF_URI')) {
  46. define('_MPDF_URI', _MPDF_PATH);
  47. }
  48. require_once _MPDF_PATH . 'includes/functions.php';
  49. require_once _MPDF_PATH . 'config_lang2fonts.php';
  50. require_once _MPDF_PATH . 'classes/ucdn.php'; // mPDF 6.0
  51. /* -- OTL -- */
  52. require_once _MPDF_PATH . 'classes/indic.php'; // mPDF 6.0
  53. require_once _MPDF_PATH . 'classes/myanmar.php'; // mPDF 6.0
  54. require_once _MPDF_PATH . 'classes/sea.php'; // mPDF 6.0
  55. /* -- END OTL -- */
  56. require_once _MPDF_PATH . 'Tag.php';
  57. require_once _MPDF_PATH . 'MpdfException.php';
  58. if (!defined('_JPGRAPH_PATH')) {
  59. define("_JPGRAPH_PATH", _MPDF_PATH . 'jpgraph/');
  60. }
  61. if (!defined('_MPDF_TEMP_PATH')) {
  62. define("_MPDF_TEMP_PATH", _MPDF_PATH . 'tmp/');
  63. }
  64. if (!defined('_MPDF_TTFONTPATH')) {
  65. define('_MPDF_TTFONTPATH', _MPDF_PATH . 'ttfonts/');
  66. }
  67. if (!defined('_MPDF_TTFONTDATAPATH')) {
  68. define('_MPDF_TTFONTDATAPATH', _MPDF_PATH . 'ttfontdata/');
  69. }
  70. $errorlevel = error_reporting();
  71. $errorlevel = error_reporting($errorlevel & ~E_NOTICE);
  72. //error_reporting(E_ALL);
  73. if (function_exists("date_default_timezone_set")) {
  74. if (ini_get("date.timezone") == "") {
  75. date_default_timezone_set("Europe/London");
  76. }
  77. }
  78. if (!function_exists('mb_strlen')) {
  79. throw new MpdfException('mPDF requires mb_string functions. Ensure that mb_string extension is loaded.');
  80. }
  81. if (!defined('PHP_VERSION_ID')) {
  82. $version = explode('.', PHP_VERSION);
  83. define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
  84. }
  85. class mPDF
  86. {
  87. ///////////////////////////////
  88. // EXTERNAL (PUBLIC) VARIABLES
  89. // Define these in config.php
  90. ///////////////////////////////
  91. var $useFixedNormalLineHeight; // mPDF 6
  92. var $useFixedTextBaseline; // mPDF 6
  93. var $adjustFontDescLineheight; // mPDF 6
  94. var $interpolateImages; // mPDF 6
  95. var $defaultPagebreakType; // mPDF 6 pagebreaktype
  96. var $indexUseSubentries; // mPDF 6
  97. var $autoScriptToLang; // mPDF 6
  98. var $baseScript; // mPDF 6
  99. var $autoVietnamese; // mPDF 6
  100. var $autoArabic; // mPDF 6
  101. var $CJKforceend;
  102. var $h2bookmarks;
  103. var $h2toc;
  104. var $decimal_align;
  105. var $margBuffer;
  106. var $splitTableBorderWidth;
  107. var $bookmarkStyles;
  108. var $useActiveForms;
  109. var $repackageTTF;
  110. var $allowCJKorphans;
  111. var $allowCJKoverflow;
  112. var $useKerning;
  113. var $restrictColorSpace;
  114. var $bleedMargin;
  115. var $crossMarkMargin;
  116. var $cropMarkMargin;
  117. var $cropMarkLength;
  118. var $nonPrintMargin;
  119. var $PDFX;
  120. var $PDFXauto;
  121. var $PDFA;
  122. var $PDFAauto;
  123. var $ICCProfile;
  124. var $printers_info;
  125. var $iterationCounter;
  126. var $smCapsScale;
  127. var $smCapsStretch;
  128. var $backupSubsFont;
  129. var $backupSIPFont;
  130. var $debugfonts;
  131. var $useAdobeCJK;
  132. var $percentSubset;
  133. var $maxTTFFilesize;
  134. var $BMPonly;
  135. var $tableMinSizePriority;
  136. var $dpi;
  137. var $watermarkImgAlphaBlend;
  138. var $watermarkImgBehind;
  139. var $justifyB4br;
  140. var $packTableData;
  141. var $pgsIns;
  142. var $simpleTables;
  143. var $enableImports;
  144. var $debug;
  145. var $showStats;
  146. var $setAutoTopMargin;
  147. var $setAutoBottomMargin;
  148. var $autoMarginPadding;
  149. var $collapseBlockMargins;
  150. var $falseBoldWeight;
  151. var $normalLineheight;
  152. var $progressBar;
  153. var $incrementFPR1;
  154. var $incrementFPR2;
  155. var $incrementFPR3;
  156. var $incrementFPR4;
  157. var $SHYlang;
  158. var $SHYleftmin;
  159. var $SHYrightmin;
  160. var $SHYcharmin;
  161. var $SHYcharmax;
  162. var $SHYlanguages;
  163. // PageNumber Conditional Text
  164. var $pagenumPrefix;
  165. var $pagenumSuffix;
  166. var $nbpgPrefix;
  167. var $nbpgSuffix;
  168. var $showImageErrors;
  169. var $allow_output_buffering;
  170. var $autoPadding;
  171. var $useGraphs;
  172. var $tabSpaces;
  173. var $autoLangToFont;
  174. var $watermarkTextAlpha;
  175. var $watermarkImageAlpha;
  176. var $watermark_size;
  177. var $watermark_pos;
  178. var $annotSize;
  179. var $annotMargin;
  180. var $annotOpacity;
  181. var $title2annots;
  182. var $keepColumns;
  183. var $keep_table_proportions;
  184. var $ignore_table_widths;
  185. var $ignore_table_percents;
  186. var $list_number_suffix;
  187. var $list_auto_mode; // mPDF 6
  188. var $list_indent_first_level; // mPDF 6
  189. var $list_indent_default; // mPDF 6
  190. var $list_marker_offset; // mPDF 6
  191. var $useSubstitutions;
  192. var $CSSselectMedia;
  193. var $forcePortraitHeaders;
  194. var $forcePortraitMargins;
  195. var $displayDefaultOrientation;
  196. var $ignore_invalid_utf8;
  197. var $allowedCSStags;
  198. var $onlyCoreFonts;
  199. var $allow_charset_conversion;
  200. var $jSWord;
  201. var $jSmaxChar;
  202. var $jSmaxCharLast;
  203. var $jSmaxWordLast;
  204. var $max_colH_correction;
  205. var $table_error_report;
  206. var $table_error_report_param;
  207. var $biDirectional;
  208. var $text_input_as_HTML;
  209. var $anchor2Bookmark;
  210. var $shrink_tables_to_fit;
  211. var $allow_html_optional_endtags;
  212. var $img_dpi;
  213. var $defaultheaderfontsize;
  214. var $defaultheaderfontstyle;
  215. var $defaultheaderline;
  216. var $defaultfooterfontsize;
  217. var $defaultfooterfontstyle;
  218. var $defaultfooterline;
  219. var $header_line_spacing;
  220. var $footer_line_spacing;
  221. var $pregCJKchars;
  222. var $pregRTLchars;
  223. var $pregCURSchars; // mPDF 6
  224. var $mirrorMargins;
  225. var $watermarkText;
  226. var $watermarkImage;
  227. var $showWatermarkText;
  228. var $showWatermarkImage;
  229. var $fontsizes;
  230. var $defaultPageNumStyle; // mPDF 6
  231. //////////////////////
  232. // CLASS OBJECTS
  233. //////////////////////
  234. var $otl; // mPDF 5.7.1
  235. var $cssmgr;
  236. var $grad;
  237. var $bmp;
  238. var $wmf;
  239. var $tocontents;
  240. var $mpdfform;
  241. var $directw;
  242. //////////////////////
  243. // INTERNAL VARIABLES
  244. //////////////////////
  245. var $script2lang;
  246. var $viet;
  247. var $pashto;
  248. var $urdu;
  249. var $persian;
  250. var $sindhi;
  251. var $extrapagebreak; // mPDF 6 pagebreaktype
  252. var $uniqstr; // mPDF 5.7.2
  253. var $hasOC;
  254. var $textvar; // mPDF 5.7.1
  255. var $fontLanguageOverride; // mPDF 5.7.1
  256. var $OTLtags; // mPDF 5.7.1
  257. var $OTLdata; // mPDF 5.7.1
  258. var $writingToC;
  259. var $layers;
  260. var $current_layer;
  261. var $open_layer_pane;
  262. var $decimal_offset;
  263. var $inMeter;
  264. var $CJKleading;
  265. var $CJKfollowing;
  266. var $CJKoverflow;
  267. var $textshadow;
  268. var $colsums;
  269. var $spanborder;
  270. var $spanborddet;
  271. var $visibility;
  272. var $useRC128encryption;
  273. var $uniqid;
  274. var $kerning;
  275. var $fixedlSpacing;
  276. var $minwSpacing;
  277. var $lSpacingCSS;
  278. var $wSpacingCSS;
  279. var $spotColorIDs;
  280. var $SVGcolors;
  281. var $spotColors;
  282. var $defTextColor;
  283. var $defDrawColor;
  284. var $defFillColor;
  285. var $tableBackgrounds;
  286. var $inlineDisplayOff;
  287. var $kt_y00;
  288. var $kt_p00;
  289. var $upperCase;
  290. var $checkSIP;
  291. var $checkSMP;
  292. var $checkCJK;
  293. var $watermarkImgAlpha;
  294. var $PDFAXwarnings;
  295. var $MetadataRoot;
  296. var $OutputIntentRoot;
  297. var $InfoRoot;
  298. var $current_filename;
  299. var $parsers;
  300. var $current_parser;
  301. var $_obj_stack;
  302. var $_don_obj_stack;
  303. var $_current_obj_id;
  304. var $tpls;
  305. var $tpl;
  306. var $tplprefix;
  307. var $_res;
  308. var $pdf_version;
  309. var $noImageFile;
  310. var $lastblockbottommargin;
  311. var $baselineC;
  312. // mPDF 5.7.3 inline text-decoration parameters
  313. var $baselineSup;
  314. var $baselineSub;
  315. var $baselineS;
  316. var $subPos;
  317. var $subArrMB;
  318. var $ReqFontStyle;
  319. var $tableClipPath;
  320. var $fullImageHeight;
  321. var $inFixedPosBlock; // Internal flag for position:fixed block
  322. var $fixedPosBlock; // Buffer string for position:fixed block
  323. var $fixedPosBlockDepth;
  324. var $fixedPosBlockBBox;
  325. var $fixedPosBlockSave;
  326. var $maxPosL;
  327. var $maxPosR;
  328. var $loaded;
  329. var $extraFontSubsets;
  330. var $docTemplateStart; // Internal flag for page (page no. -1) that docTemplate starts on
  331. var $time0;
  332. // Classes
  333. var $indic;
  334. var $barcode;
  335. var $SHYpatterns;
  336. var $loadedSHYpatterns;
  337. var $loadedSHYdictionary;
  338. var $SHYdictionary;
  339. var $SHYdictionaryWords;
  340. var $spanbgcolorarray;
  341. var $default_font;
  342. var $headerbuffer;
  343. var $lastblocklevelchange;
  344. var $nestedtablejustfinished;
  345. var $linebreakjustfinished;
  346. var $cell_border_dominance_L;
  347. var $cell_border_dominance_R;
  348. var $cell_border_dominance_T;
  349. var $cell_border_dominance_B;
  350. var $table_keep_together;
  351. var $plainCell_properties;
  352. var $shrin_k1;
  353. var $outerfilled;
  354. var $blockContext;
  355. var $floatDivs;
  356. var $patterns;
  357. var $pageBackgrounds;
  358. var $bodyBackgroundGradient;
  359. var $bodyBackgroundImage;
  360. var $bodyBackgroundColor;
  361. var $writingHTMLheader; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
  362. var $writingHTMLfooter;
  363. var $angle;
  364. var $gradients;
  365. var $kwt_Reference;
  366. var $kwt_BMoutlines;
  367. var $kwt_toc;
  368. var $tbrot_BMoutlines;
  369. var $tbrot_toc;
  370. var $col_BMoutlines;
  371. var $col_toc;
  372. var $currentGraphId;
  373. var $graphs;
  374. var $floatbuffer;
  375. var $floatmargins;
  376. var $bullet;
  377. var $bulletarray;
  378. var $currentLang;
  379. var $default_lang;
  380. var $default_available_fonts;
  381. var $pageTemplate;
  382. var $docTemplate;
  383. var $docTemplateContinue;
  384. var $arabGlyphs;
  385. var $arabHex;
  386. var $persianGlyphs;
  387. var $persianHex;
  388. var $arabVowels;
  389. var $arabPrevLink;
  390. var $arabNextLink;
  391. var $formobjects; // array of Form Objects for WMF
  392. var $InlineProperties;
  393. var $InlineAnnots;
  394. var $InlineBDF; // mPDF 6 Bidirectional formatting
  395. var $InlineBDFctr; // mPDF 6
  396. var $ktAnnots;
  397. var $tbrot_Annots;
  398. var $kwt_Annots;
  399. var $columnAnnots;
  400. var $columnForms;
  401. var $PageAnnots;
  402. var $pageDim; // Keep track of page wxh for orientation changes - set in _beginpage, used in _putannots
  403. var $breakpoints;
  404. var $tableLevel;
  405. var $tbctr;
  406. var $innermostTableLevel;
  407. var $saveTableCounter;
  408. var $cellBorderBuffer;
  409. var $saveHTMLFooter_height;
  410. var $saveHTMLFooterE_height;
  411. var $firstPageBoxHeader;
  412. var $firstPageBoxHeaderEven;
  413. var $firstPageBoxFooter;
  414. var $firstPageBoxFooterEven;
  415. var $page_box;
  416. var $show_marks; // crop or cross marks
  417. var $basepathIsLocal;
  418. var $use_kwt;
  419. var $kwt;
  420. var $kwt_height;
  421. var $kwt_y0;
  422. var $kwt_x0;
  423. var $kwt_buffer;
  424. var $kwt_Links;
  425. var $kwt_moved;
  426. var $kwt_saved;
  427. var $PageNumSubstitutions;
  428. var $table_borders_separate;
  429. var $base_table_properties;
  430. var $borderstyles;
  431. var $blockjustfinished;
  432. var $orig_bMargin;
  433. var $orig_tMargin;
  434. var $orig_lMargin;
  435. var $orig_rMargin;
  436. var $orig_hMargin;
  437. var $orig_fMargin;
  438. var $pageHTMLheaders;
  439. var $pageHTMLfooters;
  440. var $saveHTMLHeader;
  441. var $saveHTMLFooter;
  442. var $HTMLheaderPageLinks;
  443. var $HTMLheaderPageAnnots;
  444. var $HTMLheaderPageForms;
  445. // See config_fonts.php for these next 5 values
  446. var $available_unifonts;
  447. var $sans_fonts;
  448. var $serif_fonts;
  449. var $mono_fonts;
  450. var $defaultSubsFont;
  451. // List of ALL available CJK fonts (incl. styles) (Adobe add-ons) hw removed
  452. var $available_CJK_fonts;
  453. var $HTMLHeader;
  454. var $HTMLFooter;
  455. var $HTMLHeaderE;
  456. var $HTMLFooterE;
  457. var $bufferoutput;
  458. // CJK fonts
  459. var $Big5_widths;
  460. var $GB_widths;
  461. var $SJIS_widths;
  462. var $UHC_widths;
  463. // SetProtection
  464. var $encrypted; // whether document is protected
  465. var $Uvalue; // U entry in pdf document
  466. var $Ovalue; // O entry in pdf document
  467. var $Pvalue; // P entry in pdf document
  468. var $enc_obj_id; //encryption object id
  469. var $last_rc4_key; //last RC4 key encrypted (cached for optimisation)
  470. var $last_rc4_key_c; //last RC4 computed key
  471. var $encryption_key;
  472. var $padding; //used for encryption
  473. // Bookmark
  474. var $BMoutlines;
  475. var $OutlineRoot;
  476. // INDEX
  477. var $ColActive;
  478. var $Reference;
  479. var $CurrCol;
  480. var $NbCol;
  481. var $y0; //Top ordinate of columns
  482. var $ColL;
  483. var $ColWidth;
  484. var $ColGap;
  485. // COLUMNS
  486. var $ColR;
  487. var $ChangeColumn;
  488. var $columnbuffer;
  489. var $ColDetails;
  490. var $columnLinks;
  491. var $colvAlign;
  492. // Substitutions
  493. var $substitute; // Array of substitution strings e.g. <ttz>112</ttz>
  494. var $entsearch; // Array of HTML entities (>ASCII 127) to substitute
  495. var $entsubstitute; // Array of substitution decimal unicode for the Hi entities
  496. // Default values if no style sheet offered (cf. http://www.w3.org/TR/CSS21/sample.html)
  497. var $defaultCSS;
  498. var $lastoptionaltag; // Save current block item which HTML specifies optionsl endtag
  499. var $pageoutput;
  500. var $charset_in;
  501. var $blk;
  502. var $blklvl;
  503. var $ColumnAdjust;
  504. var $ws; // Word spacing
  505. var $HREF;
  506. var $pgwidth;
  507. var $fontlist;
  508. var $oldx;
  509. var $oldy;
  510. var $B;
  511. var $I;
  512. var $tdbegin;
  513. var $table;
  514. var $cell;
  515. var $col;
  516. var $row;
  517. var $divbegin;
  518. var $divwidth;
  519. var $divheight;
  520. var $spanbgcolor;
  521. // mPDF 6 Used for table cell (block-type) properties
  522. var $cellTextAlign;
  523. var $cellLineHeight;
  524. var $cellLineStackingStrategy;
  525. var $cellLineStackingShift;
  526. // mPDF 6 Lists
  527. var $listcounter;
  528. var $listlvl;
  529. var $listtype;
  530. var $listitem;
  531. var $pjustfinished;
  532. var $ignorefollowingspaces;
  533. var $SMALL;
  534. var $BIG;
  535. var $dash_on;
  536. var $dotted_on;
  537. var $textbuffer;
  538. var $currentfontstyle;
  539. var $currentfontfamily;
  540. var $currentfontsize;
  541. var $colorarray;
  542. var $bgcolorarray;
  543. var $internallink;
  544. var $enabledtags;
  545. var $lineheight;
  546. var $basepath;
  547. var $textparam;
  548. var $specialcontent;
  549. var $selectoption;
  550. var $objectbuffer;
  551. // Table Rotation
  552. var $table_rotate;
  553. var $tbrot_maxw;
  554. var $tbrot_maxh;
  555. var $tablebuffer;
  556. var $tbrot_align;
  557. var $tbrot_Links;
  558. var $keep_block_together; // Keep a Block from page-break-inside: avoid
  559. var $tbrot_y0;
  560. var $tbrot_x0;
  561. var $tbrot_w;
  562. var $tbrot_h;
  563. var $mb_enc;
  564. var $directionality;
  565. var $extgstates; // Used for alpha channel - Transparency (Watermark)
  566. var $mgl;
  567. var $mgt;
  568. var $mgr;
  569. var $mgb;
  570. var $tts;
  571. var $ttz;
  572. var $tta;
  573. // Best to alter the below variables using default stylesheet above
  574. var $page_break_after_avoid;
  575. var $margin_bottom_collapse;
  576. var $default_font_size; // in pts
  577. var $original_default_font_size; // used to save default sizes when using table default
  578. var $original_default_font;
  579. var $watermark_font;
  580. var $defaultAlign;
  581. // TABLE
  582. var $defaultTableAlign;
  583. var $tablethead;
  584. var $thead_font_weight;
  585. var $thead_font_style;
  586. var $thead_font_smCaps;
  587. var $thead_valign_default;
  588. var $thead_textalign_default;
  589. var $tabletfoot;
  590. var $tfoot_font_weight;
  591. var $tfoot_font_style;
  592. var $tfoot_font_smCaps;
  593. var $tfoot_valign_default;
  594. var $tfoot_textalign_default;
  595. var $trow_text_rotate;
  596. var $cellPaddingL;
  597. var $cellPaddingR;
  598. var $cellPaddingT;
  599. var $cellPaddingB;
  600. var $table_border_attr_set;
  601. var $table_border_css_set;
  602. var $shrin_k; // factor with which to shrink tables - used internally - do not change
  603. var $shrink_this_table_to_fit; // 0 or false to disable; value (if set) gives maximum factor to reduce fontsize
  604. var $MarginCorrection; // corrects for OddEven Margins
  605. var $margin_footer;
  606. var $margin_header;
  607. var $tabletheadjustfinished;
  608. var $usingCoreFont;
  609. var $charspacing;
  610. //Private properties FROM FPDF
  611. var $DisplayPreferences;
  612. var $flowingBlockAttr;
  613. var $page; //current page number
  614. var $n; //current object number
  615. var $offsets; //array of object offsets
  616. var $buffer; //buffer holding in-memory PDF
  617. var $pages; //array containing pages
  618. var $state; //current document state
  619. var $compress; //compression flag
  620. var $DefOrientation; //default orientation
  621. var $CurOrientation; //current orientation
  622. var $OrientationChanges; //array indicating orientation changes
  623. var $k; //scale factor (number of points in user unit)
  624. var $fwPt;
  625. var $fhPt; //dimensions of page format in points
  626. var $fw;
  627. var $fh; //dimensions of page format in user unit
  628. var $wPt;
  629. var $hPt; //current dimensions of page in points
  630. var $w;
  631. var $h; //current dimensions of page in user unit
  632. var $lMargin; //left margin
  633. var $tMargin; //top margin
  634. var $rMargin; //right margin
  635. var $bMargin; //page break margin
  636. var $cMarginL; //cell margin Left
  637. var $cMarginR; //cell margin Right
  638. var $cMarginT; //cell margin Left
  639. var $cMarginB; //cell margin Right
  640. var $DeflMargin; //Default left margin
  641. var $DefrMargin; //Default right margin
  642. var $x;
  643. var $y; //current position in user unit for cell positioning
  644. var $lasth; //height of last cell printed
  645. var $LineWidth; //line width in user unit
  646. var $CoreFonts; //array of standard font names
  647. var $fonts; //array of used fonts
  648. var $FontFiles; //array of font files
  649. var $images; //array of used images
  650. var $PageLinks; //array of links in pages
  651. var $links; //array of internal links
  652. var $FontFamily; //current font family
  653. var $FontStyle; //current font style
  654. var $CurrentFont; //current font info
  655. var $FontSizePt; //current font size in points
  656. var $FontSize; //current font size in user unit
  657. var $DrawColor; //commands for drawing color
  658. var $FillColor; //commands for filling color
  659. var $TextColor; //commands for text color
  660. var $ColorFlag; //indicates whether fill and text colors are different
  661. var $autoPageBreak; //automatic page breaking
  662. var $PageBreakTrigger; //threshold used to trigger page breaks
  663. var $InFooter; //flag set when processing footer
  664. var $InHTMLFooter;
  665. var $processingFooter; //flag set when processing footer - added for columns
  666. var $processingHeader; //flag set when processing header - added for columns
  667. var $ZoomMode; //zoom display mode
  668. var $LayoutMode; //layout display mode
  669. var $title; //title
  670. var $subject; //subject
  671. var $author; //author
  672. var $keywords; //keywords
  673. var $creator; //creator
  674. var $aliasNbPg; //alias for total number of pages
  675. var $aliasNbPgGp; //alias for total number of pages in page group
  676. //var $aliasNbPgHex; // mPDF 6 deleted
  677. //var $aliasNbPgGpHex; // mPDF 6 deleted
  678. var $ispre;
  679. var $outerblocktags;
  680. var $innerblocktags;
  681. // **********************************
  682. // **********************************
  683. // **********************************
  684. // **********************************
  685. // **********************************
  686. // **********************************
  687. // **********************************
  688. // **********************************
  689. // **********************************
  690. private $tag;
  691. public function __construct($mode = '', $format = 'A4', $default_font_size = 0, $default_font = '', $mgl = 15, $mgr = 15, $mgt = 16, $mgb = 16, $mgh = 9, $mgf = 9, $orientation = 'P')
  692. {
  693. /* -- BACKGROUNDS -- */
  694. if (!class_exists('grad', false)) {
  695. include(_MPDF_PATH . 'classes/grad.php');
  696. }
  697. if (empty($this->grad)) {
  698. $this->grad = new grad($this);
  699. }
  700. /* -- END BACKGROUNDS -- */
  701. /* -- FORMS -- */
  702. if (!class_exists('mpdfform', false)) {
  703. include(_MPDF_PATH . 'classes/mpdfform.php');
  704. }
  705. if (empty($this->mpdfform)) {
  706. $this->mpdfform = new mpdfform($this);
  707. }
  708. /* -- END FORMS -- */
  709. $this->time0 = microtime(true);
  710. //Some checks
  711. $this->_dochecks();
  712. $this->writingToC = false;
  713. $this->layers = array();
  714. $this->current_layer = 0;
  715. $this->open_layer_pane = false;
  716. $this->visibility = 'visible';
  717. //Initialization of properties
  718. $this->spotColors = array();
  719. $this->spotColorIDs = array();
  720. $this->tableBackgrounds = array();
  721. $this->uniqstr = '20110230'; // mPDF 5.7.2
  722. $this->kt_y00 = '';
  723. $this->kt_p00 = '';
  724. $this->iterationCounter = false;
  725. $this->BMPonly = array();
  726. $this->page = 0;
  727. $this->n = 2;
  728. $this->buffer = '';
  729. $this->objectbuffer = array();
  730. $this->pages = array();
  731. $this->OrientationChanges = array();
  732. $this->state = 0;
  733. $this->fonts = array();
  734. $this->FontFiles = array();
  735. $this->images = array();
  736. $this->links = array();
  737. $this->InFooter = false;
  738. $this->processingFooter = false;
  739. $this->processingHeader = false;
  740. $this->lasth = 0;
  741. $this->FontFamily = '';
  742. $this->FontStyle = '';
  743. $this->FontSizePt = 9;
  744. $this->U = false;
  745. // Small Caps
  746. $this->upperCase = array();
  747. $this->smCapsScale = 1;
  748. $this->smCapsStretch = 100;
  749. $this->margBuffer = 0;
  750. $this->inMeter = false;
  751. $this->decimal_offset = 0;
  752. $this->defTextColor = $this->TextColor = $this->SetTColor($this->ConvertColor(0), true);
  753. $this->defDrawColor = $this->DrawColor = $this->SetDColor($this->ConvertColor(0), true);
  754. $this->defFillColor = $this->FillColor = $this->SetFColor($this->ConvertColor(255), true);
  755. //SVG color names array
  756. //http://www.w3schools.com/css/css_colornames.asp
  757. $this->SVGcolors = array('antiquewhite' => '#FAEBD7', 'aqua' => '#00FFFF', 'aquamarine' => '#7FFFD4', 'beige' => '#F5F5DC', 'black' => '#000000',
  758. 'blue' => '#0000FF', 'brown' => '#A52A2A', 'cadetblue' => '#5F9EA0', 'chocolate' => '#D2691E', 'cornflowerblue' => '#6495ED', 'crimson' => '#DC143C',
  759. 'darkblue' => '#00008B', 'darkgoldenrod' => '#B8860B', 'darkgreen' => '#006400', 'darkmagenta' => '#8B008B', 'darkorange' => '#FF8C00',
  760. 'darkred' => '#8B0000', 'darkseagreen' => '#8FBC8F', 'darkslategray' => '#2F4F4F', 'darkviolet' => '#9400D3', 'deepskyblue' => '#00BFFF',
  761. 'dodgerblue' => '#1E90FF', 'firebrick' => '#B22222', 'forestgreen' => '#228B22', 'fuchsia' => '#FF00FF', 'gainsboro' => '#DCDCDC', 'gold' => '#FFD700',
  762. 'gray' => '#808080', 'green' => '#008000', 'greenyellow' => '#ADFF2F', 'hotpink' => '#FF69B4', 'indigo' => '#4B0082', 'khaki' => '#F0E68C',
  763. 'lavenderblush' => '#FFF0F5', 'lemonchiffon' => '#FFFACD', 'lightcoral' => '#F08080', 'lightgoldenrodyellow' => '#FAFAD2', 'lightgreen' => '#90EE90',
  764. 'lightsalmon' => '#FFA07A', 'lightskyblue' => '#87CEFA', 'lightslategray' => '#778899', 'lightyellow' => '#FFFFE0', 'lime' => '#00FF00', 'limegreen' => '#32CD32',
  765. 'magenta' => '#FF00FF', 'maroon' => '#800000', 'mediumaquamarine' => '#66CDAA', 'mediumorchid' => '#BA55D3', 'mediumseagreen' => '#3CB371',
  766. 'mediumspringgreen' => '#00FA9A', 'mediumvioletred' => '#C71585', 'midnightblue' => '#191970', 'mintcream' => '#F5FFFA', 'moccasin' => '#FFE4B5', 'navy' => '#000080',
  767. 'olive' => '#808000', 'orange' => '#FFA500', 'orchid' => '#DA70D6', 'palegreen' => '#98FB98',
  768. 'palevioletred' => '#D87093', 'peachpuff' => '#FFDAB9', 'pink' => '#FFC0CB', 'powderblue' => '#B0E0E6', 'purple' => '#800080',
  769. 'red' => '#FF0000', 'royalblue' => '#4169E1', 'salmon' => '#FA8072', 'seagreen' => '#2E8B57', 'sienna' => '#A0522D', 'silver' => '#C0C0C0', 'skyblue' => '#87CEEB',
  770. 'slategray' => '#708090', 'springgreen' => '#00FF7F', 'steelblue' => '#4682B4', 'tan' => '#D2B48C', 'teal' => '#008080', 'thistle' => '#D8BFD8', 'turquoise' => '#40E0D0',
  771. 'violetred' => '#D02090', 'white' => '#FFFFFF', 'yellow' => '#FFFF00',
  772. 'aliceblue' => '#f0f8ff', 'azure' => '#f0ffff', 'bisque' => '#ffe4c4', 'blanchedalmond' => '#ffebcd', 'blueviolet' => '#8a2be2', 'burlywood' => '#deb887',
  773. 'chartreuse' => '#7fff00', 'coral' => '#ff7f50', 'cornsilk' => '#fff8dc', 'cyan' => '#00ffff', 'darkcyan' => '#008b8b', 'darkgray' => '#a9a9a9',
  774. 'darkgrey' => '#a9a9a9', 'darkkhaki' => '#bdb76b', 'darkolivegreen' => '#556b2f', 'darkorchid' => '#9932cc', 'darksalmon' => '#e9967a',
  775. 'darkslateblue' => '#483d8b', 'darkslategrey' => '#2f4f4f', 'darkturquoise' => '#00ced1', 'deeppink' => '#ff1493', 'dimgray' => '#696969',
  776. 'dimgrey' => '#696969', 'floralwhite' => '#fffaf0', 'ghostwhite' => '#f8f8ff', 'goldenrod' => '#daa520', 'grey' => '#808080', 'honeydew' => '#f0fff0',
  777. 'indianred' => '#cd5c5c', 'ivory' => '#fffff0', 'lavender' => '#e6e6fa', 'lawngreen' => '#7cfc00', 'lightblue' => '#add8e6', 'lightcyan' => '#e0ffff',
  778. 'lightgray' => '#d3d3d3', 'lightgrey' => '#d3d3d3', 'lightpink' => '#ffb6c1', 'lightseagreen' => '#20b2aa', 'lightslategrey' => '#778899',
  779. 'lightsteelblue' => '#b0c4de', 'linen' => '#faf0e6', 'mediumblue' => '#0000cd', 'mediumpurple' => '#9370db', 'mediumslateblue' => '#7b68ee',
  780. 'mediumturquoise' => '#48d1cc', 'mistyrose' => '#ffe4e1', 'navajowhite' => '#ffdead', 'oldlace' => '#fdf5e6', 'olivedrab' => '#6b8e23', 'orangered' => '#ff4500',
  781. 'palegoldenrod' => '#eee8aa', 'paleturquoise' => '#afeeee', 'papayawhip' => '#ffefd5', 'peru' => '#cd853f', 'plum' => '#dda0dd', 'rosybrown' => '#bc8f8f',
  782. 'saddlebrown' => '#8b4513', 'sandybrown' => '#f4a460', 'seashell' => '#fff5ee', 'slateblue' => '#6a5acd', 'slategrey' => '#708090', 'snow' => '#fffafa',
  783. 'tomato' => '#ff6347', 'violet' => '#ee82ee', 'wheat' => '#f5deb3', 'whitesmoke' => '#f5f5f5', 'yellowgreen' => '#9acd32');
  784. // Uppercase alternatives (for Small Caps)
  785. if (empty($this->upperCase)) {
  786. @include(_MPDF_PATH . 'includes/upperCase.php');
  787. }
  788. $this->extrapagebreak = true; // mPDF 6 pagebreaktype
  789. $this->ColorFlag = false;
  790. $this->extgstates = array();
  791. $this->mb_enc = 'windows-1252';
  792. $this->directionality = 'ltr';
  793. $this->defaultAlign = 'L';
  794. $this->defaultTableAlign = 'L';
  795. $this->fixedPosBlockSave = array();
  796. $this->extraFontSubsets = 0;
  797. $this->SHYpatterns = array();
  798. $this->loadedSHYdictionary = false;
  799. $this->SHYdictionary = array();
  800. $this->SHYdictionaryWords = array();
  801. $this->blockContext = 1;
  802. $this->floatDivs = array();
  803. $this->DisplayPreferences = '';
  804. $this->patterns = array(); // Tiling patterns used for backgrounds
  805. $this->pageBackgrounds = array();
  806. $this->writingHTMLheader = false; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
  807. $this->writingHTMLfooter = false; // internal flag - used both for writing HTMLHeaders/Footers and FixedPos block
  808. $this->gradients = array();
  809. $this->kwt_Reference = array();
  810. $this->kwt_BMoutlines = array();
  811. $this->kwt_toc = array();
  812. $this->tbrot_BMoutlines = array();
  813. $this->tbrot_toc = array();
  814. $this->col_BMoutlines = array();
  815. $this->col_toc = array();
  816. $this->graphs = array();
  817. $this->pgsIns = array();
  818. $this->PDFAXwarnings = array();
  819. $this->inlineDisplayOff = false;
  820. $this->lSpacingCSS = '';
  821. $this->wSpacingCSS = '';
  822. $this->fixedlSpacing = false;
  823. $this->minwSpacing = 0;
  824. $this->baselineC = 0.35; // Baseline for text
  825. // mPDF 5.7.3 inline text-decoration parameters
  826. $this->baselineSup = 0.5; // Sets default change in baseline for <sup> text as factor of preceeding fontsize
  827. // 0.35 has been recommended; 0.5 matches applications like MS Word
  828. $this->baselineSub = -0.2; // Sets default change in baseline for <sub> text as factor of preceeding fontsize
  829. $this->baselineS = 0.3; // Sets default height for <strike> text as factor of fontsize
  830. $this->baselineO = 1.1; // Sets default height for overline text as factor of fontsize
  831. $this->noImageFile = str_replace("\\", "/", dirname(__FILE__)) . '/includes/no_image.jpg';
  832. $this->subPos = 0;
  833. $this->normalLineheight = 1.3; // This should be overridden in config.php - but it is so important a default value is put here
  834. // These are intended as configuration variables, and should be set in config.php - which will override these values;
  835. // set here as failsafe as will cause an error if not defined
  836. $this->incrementFPR1 = 10;
  837. $this->incrementFPR2 = 10;
  838. $this->incrementFPR3 = 10;
  839. $this->incrementFPR4 = 10;
  840. $this->fullImageHeight = false;
  841. $this->floatbuffer = array();
  842. $this->floatmargins = array();
  843. $this->formobjects = array(); // array of Form Objects for WMF
  844. $this->InlineProperties = array();
  845. $this->InlineAnnots = array();
  846. $this->InlineBDF = array(); // mPDF 6
  847. $this->InlineBDFctr = 0; // mPDF 6
  848. $this->tbrot_Annots = array();
  849. $this->kwt_Annots = array();
  850. $this->columnAnnots = array();
  851. $this->pageDim = array();
  852. $this->breakpoints = array(); // used in columnbuffer
  853. $this->tableLevel = 0;
  854. $this->tbctr = array(); // counter for nested tables at each level
  855. $this->page_box = array();
  856. $this->show_marks = ''; // crop or cross marks
  857. $this->kwt = false;
  858. $this->kwt_height = 0;
  859. $this->kwt_y0 = 0;
  860. $this->kwt_x0 = 0;
  861. $this->kwt_buffer = array();
  862. $this->kwt_Links = array();
  863. $this->kwt_moved = false;
  864. $this->kwt_saved = false;
  865. $this->PageNumSubstitutions = array();
  866. $this->base_table_properties = array();
  867. $this->borderstyles = array('inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double');
  868. $this->tbrot_align = 'C';
  869. $this->pageHTMLheaders = array();
  870. $this->pageHTMLfooters = array();
  871. $this->HTMLheaderPageLinks = array();
  872. $this->HTMLheaderPageAnnots = array();
  873. $this->HTMLheaderPageForms = array();
  874. $this->columnForms = array();
  875. $this->tbrotForms = array();
  876. $this->useRC128encryption = false;
  877. $this->uniqid = '';
  878. $this->pageoutput = array();
  879. $this->bufferoutput = false;
  880. $this->encrypted = false; //whether document is protected
  881. $this->BMoutlines = array();
  882. $this->ColActive = 0; //Flag indicating that columns are on (the index is being processed)
  883. $this->Reference = array(); //Array containing the references
  884. $this->CurrCol = 0; //Current column number
  885. $this->ColL = array(0); // Array of Left pos of columns - absolute - needs Margin correction for Odd-Even
  886. $this->ColR = array(0); // Array of Right pos of columns - absolute pos - needs Margin correction for Odd-Even
  887. $this->ChangeColumn = 0;
  888. $this->columnbuffer = array();
  889. $this->ColDetails = array(); // Keeps track of some column details
  890. $this->columnLinks = array(); // Cross references PageLinks
  891. $this->substitute = array(); // Array of substitution strings e.g. <ttz>112</ttz>
  892. $this->entsearch = array(); // Array of HTML entities (>ASCII 127) to substitute
  893. $this->entsubstitute = array(); // Array of substitution decimal unicode for the Hi entities
  894. $this->lastoptionaltag = '';
  895. $this->charset_in = '';
  896. $this->blk = array();
  897. $this->blklvl = 0;
  898. $this->tts = false;
  899. $this->ttz = false;
  900. $this->tta = false;
  901. $this->ispre = false;
  902. $this->checkSIP = false;
  903. $this->checkSMP = false;
  904. $this->checkCJK = false;
  905. $this->page_break_after_avoid = false;
  906. $this->margin_bottom_collapse = false;
  907. $this->tablethead = 0;
  908. $this->tabletfoot = 0;
  909. $this->table_border_attr_set = 0;
  910. $this->table_border_css_set = 0;
  911. $this->shrin_k = 1.0;
  912. $this->shrink_this_table_to_fit = 0;
  913. $this->MarginCorrection = 0;
  914. $this->tabletheadjustfinished = false;
  915. $this->usingCoreFont = false;
  916. $this->charspacing = 0;
  917. $this->autoPageBreak = true;
  918. require(_MPDF_PATH . 'config.php'); // config data
  919. $this->_setPageSize($format, $orientation);
  920. $this->DefOrientation = $orientation;
  921. $this->margin_header = $mgh;
  922. $this->margin_footer = $mgf;
  923. $bmargin = $mgb;
  924. $this->DeflMargin = $mgl;
  925. $this->DefrMargin = $mgr;
  926. $this->orig_tMargin = $mgt;
  927. $this->orig_bMargin = $bmargin;
  928. $this->orig_lMargin = $this->DeflMargin;
  929. $this->orig_rMargin = $this->DefrMargin;
  930. $this->orig_hMargin = $this->margin_header;
  931. $this->orig_fMargin = $this->margin_footer;
  932. if ($this->setAutoTopMargin == 'pad') {
  933. $mgt += $this->margin_header;
  934. }
  935. if ($this->setAutoBottomMargin == 'pad') {
  936. $mgb += $this->margin_footer;
  937. }
  938. $this->SetMargins($this->DeflMargin, $this->DefrMargin, $mgt); // sets l r t margin
  939. //Automatic page break
  940. $this->SetAutoPageBreak($this->autoPageBreak, $bmargin); // sets $this->bMargin & PageBreakTrigger
  941. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  942. //Interior cell margin (1 mm) ? not used
  943. $this->cMarginL = 1;
  944. $this->cMarginR = 1;
  945. //Line width (0.2 mm)
  946. $this->LineWidth = .567 / _MPDFK;
  947. //To make the function Footer() work - replaces {nb} with page number
  948. $this->AliasNbPages();
  949. $this->AliasNbPageGroups();
  950. //$this->aliasNbPgHex = '{nbHEXmarker}'; // mPDF 6 deleted
  951. //$this->aliasNbPgGpHex = '{nbpgHEXmarker}'; // mPDF 6 deleted
  952. //Enable all tags as default
  953. $this->DisableTags();
  954. //Full width display mode
  955. $this->SetDisplayMode(100); // fullwidth? 'fullpage'
  956. //Compression
  957. $this->SetCompression(true);
  958. //Set default display preferences
  959. $this->SetDisplayPreferences('');
  960. // Font data
  961. require(_MPDF_PATH . 'config_fonts.php');
  962. // check for a custom config file that can add/overwrite the default config
  963. if (defined('_MPDF_SYSTEM_TTFONTS_CONFIG') && file_exists(_MPDF_SYSTEM_TTFONTS_CONFIG)) {
  964. require(_MPDF_SYSTEM_TTFONTS_CONFIG);
  965. }
  966. // Available fonts
  967. $this->available_unifonts = array();
  968. foreach ($this->fontdata AS $f => $fs) {
  969. if (isset($fs['R']) && $fs['R']) {
  970. $this->available_unifonts[] = $f;
  971. }
  972. if (isset($fs['B']) && $fs['B']) {
  973. $this->available_unifonts[] = $f . 'B';
  974. }
  975. if (isset($fs['I']) && $fs['I']) {
  976. $this->available_unifonts[] = $f . 'I';
  977. }
  978. if (isset($fs['BI']) && $fs['BI']) {
  979. $this->available_unifonts[] = $f . 'BI';
  980. }
  981. }
  982. $this->default_available_fonts = $this->available_unifonts;
  983. $optcore = false;
  984. $onlyCoreFonts = false;
  985. if (preg_match('/([\-+])aCJK/i', $mode, $m)) {
  986. $mode = preg_replace('/([\-+])aCJK/i', '', $mode); // mPDF 6
  987. if ($m[1] == '+') {
  988. $this->useAdobeCJK = true;
  989. } else {
  990. $this->useAdobeCJK = false;
  991. }
  992. }
  993. if (strlen($mode) == 1) {
  994. if ($mode == 's') {
  995. $this->percentSubset = 100;
  996. $mode = '';
  997. } elseif ($mode == 'c') {
  998. $onlyCoreFonts = true;
  999. $mode = '';
  1000. }
  1001. } elseif (substr($mode, -2) == '-s') {
  1002. $this->percentSubset = 100;
  1003. $mode = substr($mode, 0, strlen($mode) - 2);
  1004. } elseif (substr($mode, -2) == '-c') {
  1005. $onlyCoreFonts = true;
  1006. $mode = substr($mode, 0, strlen($mode) - 2);
  1007. } elseif (substr($mode, -2) == '-x') {
  1008. $optcore = true;
  1009. $mode = substr($mode, 0, strlen($mode) - 2);
  1010. }
  1011. // Autodetect if mode is a language_country string (en-GB or en_GB or en)
  1012. if ($mode && $mode != 'UTF-8') { // mPDF 6
  1013. list ($coreSuitable, $mpdf_pdf_unifont) = GetLangOpts($mode, $this->useAdobeCJK, $this->fontdata);
  1014. if ($coreSuitable && $optcore) {
  1015. $onlyCoreFonts = true;
  1016. }
  1017. if ($mpdf_pdf_unifont) { // mPDF 6
  1018. $default_font = $mpdf_pdf_unifont;
  1019. }
  1020. $this->currentLang = $mode;
  1021. $this->default_lang = $mode;
  1022. }
  1023. $this->onlyCoreFonts = $onlyCoreFonts;
  1024. if ($this->onlyCoreFonts) {
  1025. $this->setMBencoding('windows-1252'); // sets $this->mb_enc
  1026. } else {
  1027. $this->setMBencoding('UTF-8'); // sets $this->mb_enc
  1028. }
  1029. @mb_regex_encoding('UTF-8'); // required only for mb_ereg... and mb_split functions
  1030. // Adobe CJK fonts
  1031. $this->available_CJK_fonts = array('gb', 'big5', 'sjis', 'uhc', 'gbB', 'big5B', 'sjisB', 'uhcB', 'gbI', 'big5I', 'sjisI', 'uhcI',
  1032. 'gbBI', 'big5BI', 'sjisBI', 'uhcBI');
  1033. //Standard fonts
  1034. $this->CoreFonts = array('ccourier' => 'Courier', 'ccourierB' => 'Courier-Bold', 'ccourierI' => 'Courier-Oblique', 'ccourierBI' => 'Courier-BoldOblique',
  1035. 'chelvetica' => 'Helvetica', 'chelveticaB' => 'Helvetica-Bold', 'chelveticaI' => 'Helvetica-Oblique', 'chelveticaBI' => 'Helvetica-BoldOblique',
  1036. 'ctimes' => 'Times-Roman', 'ctimesB' => 'Times-Bold', 'ctimesI' => 'Times-Italic', 'ctimesBI' => 'Times-BoldItalic',
  1037. 'csymbol' => 'Symbol', 'czapfdingbats' => 'ZapfDingbats');
  1038. $this->fontlist = array("ctimes", "ccourier", "chelvetica", "csymbol", "czapfdingbats");
  1039. // Substitutions
  1040. $this->setHiEntitySubstitutions();
  1041. if ($this->onlyCoreFonts) {
  1042. $this->useSubstitutions = true;
  1043. $this->SetSubstitutions();
  1044. } else {
  1045. $this->useSubstitutions = false;
  1046. }
  1047. /* -- HTML-CSS -- */
  1048. if (!class_exists('cssmgr', false)) {
  1049. include(_MPDF_PATH . 'classes/cssmgr.php');
  1050. }
  1051. $this->cssmgr = new cssmgr($this);
  1052. // mPDF 6
  1053. if (file_exists(_MPDF_PATH . 'mpdf.css')) {
  1054. $css = file_get_contents(_MPDF_PATH . 'mpdf.css');
  1055. $this->cssmgr->ReadCSS('<style> ' . $css . ' </style>');
  1056. }
  1057. /* -- END HTML-CSS -- */
  1058. if ($default_font == '') {
  1059. if ($this->onlyCoreFonts) {
  1060. if (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->mono_fonts)) {
  1061. $default_font = 'ccourier';
  1062. } elseif (in_array(strtolower($this->defaultCSS['BODY']['FONT-FAMILY']), $this->sans_fonts)) {
  1063. $default_font = 'chelvetica';
  1064. } else {
  1065. $default_font = 'ctimes';
  1066. }
  1067. } else {
  1068. $default_font = $this->defaultCSS['BODY']['FONT-FAMILY'];
  1069. }
  1070. }
  1071. if (!$default_font_size) {
  1072. $mmsize = $this->ConvertSize($this->defaultCSS['BODY']['FONT-SIZE']);
  1073. $default_font_size = $mmsize * (_MPDFK);
  1074. }
  1075. if ($default_font) {
  1076. $this->SetDefaultFont($default_font);
  1077. }
  1078. if ($default_font_size) {
  1079. $this->SetDefaultFontSize($default_font_size);
  1080. }
  1081. $this->SetLineHeight(); // lineheight is in mm
  1082. $this->SetFColor($this->ConvertColor(255));
  1083. $this->HREF = '';
  1084. $this->oldy = -1;
  1085. $this->B = 0;
  1086. $this->I = 0;
  1087. // mPDF 6 Lists
  1088. $this->listlvl = 0;
  1089. $this->listtype = array();
  1090. $this->listitem = array();
  1091. $this->listcounter = array();
  1092. $this->tdbegin = false;
  1093. $this->table = array();
  1094. $this->cell = array();
  1095. $this->col = -1;
  1096. $this->row = -1;
  1097. $this->cellBorderBuffer = array();
  1098. $this->divbegin = false;
  1099. // mPDF 6
  1100. $this->cellTextAlign = '';
  1101. $this->cellLineHeight = '';
  1102. $this->cellLineStackingStrategy = '';
  1103. $this->cellLineStackingShift = '';
  1104. $this->divwidth = 0;
  1105. $this->divheight = 0;
  1106. $this->spanbgcolor = false;
  1107. $this->spanborder = false;
  1108. $this->spanborddet = array();
  1109. $this->blockjustfinished = false;
  1110. $this->ignorefollowingspaces = true; //in order to eliminate exceeding left-side spaces
  1111. $this->dash_on = false;
  1112. $this->dotted_on = false;
  1113. $this->textshadow = '';
  1114. $this->currentfontfamily = '';
  1115. $this->currentfontsize = '';
  1116. $this->currentfontstyle = '';
  1117. $this->colorarray = ''; // mPDF 6
  1118. $this->spanbgcolorarray = ''; // mPDF 6
  1119. $this->textbuffer = array();
  1120. $this->internallink = array();
  1121. $this->basepath = "";
  1122. $this->SetBasePath('');
  1123. $this->textparam = array();
  1124. $this->specialcontent = '';
  1125. $this->selectoption = array();
  1126. /* -- IMPORTS -- */
  1127. $this->tpls = array();
  1128. $this->tpl = 0;
  1129. $this->tplprefix = "/TPL";
  1130. $this->res = array();
  1131. if ($this->enableImports) {
  1132. $this->SetImportUse();
  1133. }
  1134. /* -- END IMPORTS -- */
  1135. if ($this->progressBar) {
  1136. $this->StartProgressBarOutput($this->progressBar);
  1137. } // *PROGRESS-BAR*
  1138. $this->tag = new Tag($this);
  1139. }
  1140. function _setPageSize($format, &$orientation)
  1141. {
  1142. //Page format
  1143. if (is_string($format)) {
  1144. if ($format == '') {
  1145. $format = 'A4';
  1146. }
  1147. $pfo = 'P';
  1148. if (preg_match('/([0-9a-zA-Z]*)-L/i', $format, $m)) { // e.g. A4-L = A4 landscape
  1149. $format = $m[1];
  1150. $pfo = 'L';
  1151. }
  1152. $format = $this->_getPageFormat($format);
  1153. if (!$format) {
  1154. throw new MpdfException('Unknown page format: ' . $format);
  1155. } else {
  1156. $orientation = $pfo;
  1157. }
  1158. $this->fwPt = $format[0];
  1159. $this->fhPt = $format[1];
  1160. } else {
  1161. if (!$format[0] || !$format[1]) {
  1162. throw new MpdfException('Invalid page format: ' . $format[0] . ' ' . $format[1]);
  1163. }
  1164. $this->fwPt = $format[0] * _MPDFK;
  1165. $this->fhPt = $format[1] * _MPDFK;
  1166. }
  1167. $this->fw = $this->fwPt / _MPDFK;
  1168. $this->fh = $this->fhPt / _MPDFK;
  1169. //Page orientation
  1170. $orientation = strtolower($orientation);
  1171. if ($orientation == 'p' or $orientation == 'portrait') {
  1172. $orientation = 'P';
  1173. $this->wPt = $this->fwPt;
  1174. $this->hPt = $this->fhPt;
  1175. } elseif ($orientation == 'l' or $orientation == 'landscape') {
  1176. $orientation = 'L';
  1177. $this->wPt = $this->fhPt;
  1178. $this->hPt = $this->fwPt;
  1179. } else
  1180. throw new MpdfException('Incorrect orientation: ' . $orientation);
  1181. $this->CurOrientation = $orientation;
  1182. $this->w = $this->wPt / _MPDFK;
  1183. $this->h = $this->hPt / _MPDFK;
  1184. }
  1185. function _getPageFormat($format)
  1186. {
  1187. switch (strtoupper($format)) {
  1188. case '4A0': {
  1189. $format = array(4767.87, 6740.79);
  1190. break;
  1191. }
  1192. case '2A0': {
  1193. $format = array(3370.39, 4767.87);
  1194. break;
  1195. }
  1196. case 'A0': {
  1197. $format = array(2383.94, 3370.39);
  1198. break;
  1199. }
  1200. case 'A1': {
  1201. $format = array(1683.78, 2383.94);
  1202. break;
  1203. }
  1204. case 'A2': {
  1205. $format = array(1190.55, 1683.78);
  1206. break;
  1207. }
  1208. case 'A3': {
  1209. $format = array(841.89, 1190.55);
  1210. break;
  1211. }
  1212. case 'A4': {
  1213. $format = array(595.28, 841.89);
  1214. break;
  1215. }
  1216. case 'A5': {
  1217. $format = array(419.53, 595.28);
  1218. break;
  1219. }
  1220. case 'A6': {
  1221. $format = array(297.64, 419.53);
  1222. break;
  1223. }
  1224. case 'A7': {
  1225. $format = array(209.76, 297.64);
  1226. break;
  1227. }
  1228. case 'A8': {
  1229. $format = array(147.40, 209.76);
  1230. break;
  1231. }
  1232. case 'A9': {
  1233. $format = array(104.88, 147.40);
  1234. break;
  1235. }
  1236. case 'A10': {
  1237. $format = array(73.70, 104.88);
  1238. break;
  1239. }
  1240. case 'B0': {
  1241. $format = array(2834.65, 4008.19);
  1242. break;
  1243. }
  1244. case 'B1': {
  1245. $format = array(2004.09, 2834.65);
  1246. break;
  1247. }
  1248. case 'B2': {
  1249. $format = array(1417.32, 2004.09);
  1250. break;
  1251. }
  1252. case 'B3': {
  1253. $format = array(1000.63, 1417.32);
  1254. break;
  1255. }
  1256. case 'B4': {
  1257. $format = array(708.66, 1000.63);
  1258. break;
  1259. }
  1260. case 'B5': {
  1261. $format = array(498.90, 708.66);
  1262. break;
  1263. }
  1264. case 'B6': {
  1265. $format = array(354.33, 498.90);
  1266. break;
  1267. }
  1268. case 'B7': {
  1269. $format = array(249.45, 354.33);
  1270. break;
  1271. }
  1272. case 'B8': {
  1273. $format = array(175.75, 249.45);
  1274. break;
  1275. }
  1276. case 'B9': {
  1277. $format = array(124.72, 175.75);
  1278. break;
  1279. }
  1280. case 'B10': {
  1281. $format = array(87.87, 124.72);
  1282. break;
  1283. }
  1284. case 'C0': {
  1285. $format = array(2599.37, 3676.54);
  1286. break;
  1287. }
  1288. case 'C1': {
  1289. $format = array(1836.85, 2599.37);
  1290. break;
  1291. }
  1292. case 'C2': {
  1293. $format = array(1298.27, 1836.85);
  1294. break;
  1295. }
  1296. case 'C3': {
  1297. $format = array(918.43, 1298.27);
  1298. break;
  1299. }
  1300. case 'C4': {
  1301. $format = array(649.13, 918.43);
  1302. break;
  1303. }
  1304. case 'C5': {
  1305. $format = array(459.21, 649.13);
  1306. break;
  1307. }
  1308. case 'C6': {
  1309. $format = array(323.15, 459.21);
  1310. break;
  1311. }
  1312. case 'C7': {
  1313. $format = array(229.61, 323.15);
  1314. break;
  1315. }
  1316. case 'C8': {
  1317. $format = array(161.57, 229.61);
  1318. break;
  1319. }
  1320. case 'C9': {
  1321. $format = array(113.39, 161.57);
  1322. break;
  1323. }
  1324. case 'C10': {
  1325. $format = array(79.37, 113.39);
  1326. break;
  1327. }
  1328. case 'RA0': {
  1329. $format = array(2437.80, 3458.27);
  1330. break;
  1331. }
  1332. case 'RA1': {
  1333. $format = array(1729.13, 2437.80);
  1334. break;
  1335. }
  1336. case 'RA2': {
  1337. $format = array(1218.90, 1729.13);
  1338. break;
  1339. }
  1340. case 'RA3': {
  1341. $format = array(864.57, 1218.90);
  1342. break;
  1343. }
  1344. case 'RA4': {
  1345. $format = array(609.45, 864.57);
  1346. break;
  1347. }
  1348. case 'SRA0': {
  1349. $format = array(2551.18, 3628.35);
  1350. break;
  1351. }
  1352. case 'SRA1': {
  1353. $format = array(1814.17, 2551.18);
  1354. break;
  1355. }
  1356. case 'SRA2': {
  1357. $format = array(1275.59, 1814.17);
  1358. break;
  1359. }
  1360. case 'SRA3': {
  1361. $format = array(907.09, 1275.59);
  1362. break;
  1363. }
  1364. case 'SRA4': {
  1365. $format = array(637.80, 907.09);
  1366. break;
  1367. }
  1368. case 'LETTER': {
  1369. $format = array(612.00, 792.00);
  1370. break;
  1371. }
  1372. case 'LEGAL': {
  1373. $format = array(612.00, 1008.00);
  1374. break;
  1375. }
  1376. case 'LEDGER': {
  1377. $format = array(1224.00, 792.00);
  1378. break;
  1379. }
  1380. case 'TABLOID': {
  1381. $format = array(792.00, 1224.00);
  1382. break;
  1383. }
  1384. case 'EXECUTIVE': {
  1385. $format = array(521.86, 756.00);
  1386. break;
  1387. }
  1388. case 'FOLIO': {
  1389. $format = array(612.00, 936.00);
  1390. break;
  1391. }
  1392. case 'B': {
  1393. $format = array(362.83, 561.26);
  1394. break;
  1395. } // 'B' format paperback size 128x198mm
  1396. case 'A': {
  1397. $format = array(314.65, 504.57);
  1398. break;
  1399. } // 'A' format paperback size 111x178mm
  1400. case 'DEMY': {
  1401. $format = array(382.68, 612.28);
  1402. break;
  1403. } // 'Demy' format paperback size 135x216mm
  1404. case 'ROYAL': {
  1405. $format = array(433.70, 663.30);
  1406. break;
  1407. } // 'Royal' format paperback size 153x234mm
  1408. default: {
  1409. $format = array(595.28, 841.89);
  1410. break;
  1411. }
  1412. }
  1413. return $format;
  1414. }
  1415. /* -- PROGRESS-BAR -- */
  1416. function StartProgressBarOutput($mode = 1)
  1417. {
  1418. // must be relative path, or URI (not a file system path)
  1419. if (!defined('_MPDF_URI')) {
  1420. $this->progressBar = false;
  1421. if ($this->debug) {
  1422. throw new MpdfException("You need to define _MPDF_URI to use the progress bar!");
  1423. } else
  1424. return false;
  1425. }
  1426. $this->progressBar = $mode;
  1427. if ($this->progbar_altHTML) {
  1428. echo $this->progbar_altHTML;
  1429. } else {
  1430. echo '<html>
  1431. <head>
  1432. <title>mPDF File Progress</title>
  1433. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  1434. <link rel="stylesheet" type="text/css" href="' . _MPDF_URI . 'progbar.css" />
  1435. </head>
  1436. <body>
  1437. <div class="main">
  1438. <div class="heading">' . $this->progbar_heading . '</div>
  1439. <div class="demo">
  1440. ';
  1441. if ($this->progressBar == 2) {
  1442. echo ' <table width="100%"><tr><td style="width: 50%;">
  1443. <span class="barheading">Writing HTML code</span> <br/>
  1444. <div class="progressBar">
  1445. <div id="element1" class="innerBar">&nbsp;</div>
  1446. </div>
  1447. <span class="code" id="box1"></span>
  1448. </td><td style="width: 50%;">
  1449. <span class="barheading">Autosizing elements</span> <br/>
  1450. <div class="progressBar">
  1451. <div id="element4" class="innerBar">&nbsp;</div>
  1452. </div>
  1453. <span class="code" id="box4"></span>
  1454. <br/><br/>
  1455. <span class="barheading">Writing Tables</span> <br/>
  1456. <div class="progressBar">
  1457. <div id="element7" class="innerBar">&nbsp;</div>
  1458. </div>
  1459. <span class="code" id="box7"></span>
  1460. </td></tr>
  1461. <tr><td><br /><br /></td><td></td></tr>
  1462. <tr><td style="width: 50%;">
  1463. ';
  1464. }
  1465. echo ' <span class="barheading">Writing PDF file</span> <br/>
  1466. <div class="progressBar">
  1467. <div id="element2" class="innerBar">&nbsp;</div>
  1468. </div>
  1469. <span class="code" id="box2"></span>
  1470. ';
  1471. if ($this->progressBar == 2) {
  1472. echo '
  1473. </td><td style="width: 50%;">
  1474. <span class="barheading">Memory usage</span> <br/>
  1475. <div class="progressBar">
  1476. <div id="element5" class="innerBar">&nbsp;</div>
  1477. </div>
  1478. <span id="box5">0</span> ' . ini_get("memory_limit") . '<br />
  1479. <br/><br/>
  1480. <span class="barheading">Memory usage (peak)</span> <br/>
  1481. <div class="progressBar">
  1482. <div id="element6" class="innerBar">&nbsp;</div>
  1483. </div>
  1484. <span id="box6">0</span> ' . ini_get("memory_limit") . '<br />
  1485. </td></tr>
  1486. </table>
  1487. ';
  1488. }
  1489. echo ' <br/><br/>
  1490. <span id="box3"></span>
  1491. </div>
  1492. ';
  1493. }
  1494. ob_flush();
  1495. flush();
  1496. }
  1497. function UpdateProgressBar($el, $val, $txt = '')
  1498. {
  1499. // $val should be a string - 5 = actual value, +15 = increment
  1500. if ($this->progressBar < 2) {
  1501. if ($el > 3) {
  1502. return;
  1503. } elseif ($el == 1) {
  1504. $el = 2;
  1505. }
  1506. }
  1507. echo '<script type="text/javascript">';
  1508. if ($val) {
  1509. echo ' document.getElementById(\'element' . $el . '\').style.width=\'' . $val . '%\'; ';
  1510. }
  1511. if ($txt) {
  1512. echo ' document.getElementById(\'box' . $el . '\').innerHTML=\'' . $txt . '\'; ';
  1513. }
  1514. if ($this->progressBar == 2) {
  1515. $m = round(memory_get_usage(true) / 1048576);
  1516. $m2 = round(memory_get_peak_usage(true) / 1048576);
  1517. $mem = $m * 100 / (ini_get("memory_limit") + 0);
  1518. $mem2 = $m2 * 100 / (ini_get("memory_limit") + 0);
  1519. echo ' document.getElementById(\'element5\').style.width=\'' . $mem . '%\'; ';
  1520. echo ' document.getElementById(\'element6\').style.width=\'' . $mem2 . '%\'; ';
  1521. echo ' document.getElementById(\'box5\').innerHTML=\'' . $m . 'MB / \'; ';
  1522. echo ' document.getElementById(\'box6\').innerHTML=\'' . $m2 . 'MB / \'; ';
  1523. }
  1524. echo '</script>' . "\n";
  1525. ob_flush();
  1526. flush();
  1527. }
  1528. /* -- END PROGRESS-BAR -- */
  1529. function RestrictUnicodeFonts($res)
  1530. {
  1531. // $res = array of (Unicode) fonts to restrict to: e.g. norasi|norasiB - language specific
  1532. if (count($res)) { // Leave full list of available fonts if passed blank array
  1533. $this->available_unifonts = $res;
  1534. } else {
  1535. $this->available_unifonts = $this->default_available_fonts;
  1536. }
  1537. if (count($this->available_unifonts) == 0) {
  1538. $this->available_unifonts[] = $this->default_available_fonts[0];
  1539. }
  1540. $this->available_unifonts = array_values($this->available_unifonts);
  1541. }
  1542. function setMBencoding($enc)
  1543. {
  1544. if ($this->mb_enc != $enc) {
  1545. $this->mb_enc = $enc;
  1546. mb_internal_encoding($this->mb_enc);
  1547. }
  1548. }
  1549. function SetMargins($left, $right, $top)
  1550. {
  1551. //Set left, top and right margins
  1552. $this->lMargin = $left;
  1553. $this->rMargin = $right;
  1554. $this->tMargin = $top;
  1555. }
  1556. function ResetMargins()
  1557. {
  1558. //ReSet left, top margins
  1559. if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P' && $this->CurOrientation == 'L') {
  1560. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  1561. $this->tMargin = $this->orig_rMargin;
  1562. $this->bMargin = $this->orig_lMargin;
  1563. } else { // ODD // OR NOT MIRRORING MARGINS/FOOTERS
  1564. $this->tMargin = $this->orig_lMargin;
  1565. $this->bMargin = $this->orig_rMargin;
  1566. }
  1567. $this->lMargin = $this->DeflMargin;
  1568. $this->rMargin = $this->DefrMargin;
  1569. $this->MarginCorrection = 0;
  1570. $this->PageBreakTrigger = $this->h - $this->bMargin;
  1571. } elseif (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  1572. $this->lMargin = $this->DefrMargin;
  1573. $this->rMargin = $this->DeflMargin;
  1574. $this->MarginCorrection = $this->DefrMargin - $this->DeflMargin;
  1575. } else { // ODD // OR NOT MIRRORING MARGINS/FOOTERS
  1576. $this->lMargin = $this->DeflMargin;
  1577. $this->rMargin = $this->DefrMargin;
  1578. if ($this->mirrorMargins) {
  1579. $this->MarginCorrection = $this->DeflMargin - $this->DefrMargin;
  1580. }
  1581. }
  1582. $this->x = $this->lMargin;
  1583. }
  1584. function SetLeftMargin($margin)
  1585. {
  1586. //Set left margin
  1587. $this->lMargin = $margin;
  1588. if ($this->page > 0 and $this->x < $margin)
  1589. $this->x = $margin;
  1590. }
  1591. function SetTopMargin($margin)
  1592. {
  1593. //Set top margin
  1594. $this->tMargin = $margin;
  1595. }
  1596. function SetRightMargin($margin)
  1597. {
  1598. //Set right margin
  1599. $this->rMargin = $margin;
  1600. }
  1601. function SetAutoPageBreak($auto, $margin = 0)
  1602. {
  1603. //Set auto page break mode and triggering margin
  1604. $this->autoPageBreak = $auto;
  1605. $this->bMargin = $margin;
  1606. $this->PageBreakTrigger = $this->h - $margin;
  1607. }
  1608. function SetDisplayMode($zoom, $layout = 'continuous')
  1609. {
  1610. //Set display mode in viewer
  1611. if ($zoom == 'fullpage' or $zoom == 'fullwidth' or $zoom == 'real' or $zoom == 'default' or ! is_string($zoom))
  1612. $this->ZoomMode = $zoom;
  1613. else
  1614. throw new MpdfException('Incorrect zoom display mode: ' . $zoom);
  1615. if ($layout == 'single' or $layout == 'continuous' or $layout == 'two' or $layout == 'twoleft' or $layout == 'tworight' or $layout == 'default')
  1616. $this->LayoutMode = $layout;
  1617. else
  1618. throw new MpdfException('Incorrect layout display mode: ' . $layout);
  1619. }
  1620. function SetCompression($compress)
  1621. {
  1622. //Set page compression
  1623. if (function_exists('gzcompress'))
  1624. $this->compress = $compress;
  1625. else
  1626. $this->compress = false;
  1627. }
  1628. function SetTitle($title)
  1629. {
  1630. //Title of document // Arrives as UTF-8
  1631. $this->title = $title;
  1632. }
  1633. function SetSubject($subject)
  1634. {
  1635. //Subject of document
  1636. $this->subject = $subject;
  1637. }
  1638. function SetAuthor($author)
  1639. {
  1640. //Author of document
  1641. $this->author = $author;
  1642. }
  1643. function SetKeywords($keywords)
  1644. {
  1645. //Keywords of document
  1646. $this->keywords = $keywords;
  1647. }
  1648. function SetCreator($creator)
  1649. {
  1650. //Creator of document
  1651. $this->creator = $creator;
  1652. }
  1653. function SetAnchor2Bookmark($x)
  1654. {
  1655. $this->anchor2Bookmark = $x;
  1656. }
  1657. function AliasNbPages($alias = '{nb}')
  1658. {
  1659. //Define an alias for total number of pages
  1660. $this->aliasNbPg = $alias;
  1661. }
  1662. function AliasNbPageGroups($alias = '{nbpg}')
  1663. {
  1664. //Define an alias for total number of pages in a group
  1665. $this->aliasNbPgGp = $alias;
  1666. }
  1667. function SetAlpha($alpha, $bm = 'Normal', $return = false, $mode = 'B')
  1668. {
  1669. // alpha: real value from 0 (transparent) to 1 (opaque)
  1670. // bm: blend mode, one of the following:
  1671. // Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn,
  1672. // HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
  1673. // set alpha for stroking (CA) and non-stroking (ca) operations
  1674. // mode determines F (fill) S (stroke) B (both)
  1675. if (($this->PDFA || $this->PDFX) && $alpha != 1) {
  1676. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  1677. $this->PDFAXwarnings[] = "Image opacity must be 100% (Opacity changed to 100%)";
  1678. }
  1679. $alpha = 1;
  1680. }
  1681. $a = array('BM' => '/' . $bm);
  1682. if ($mode == 'F' || $mode == 'B')
  1683. $a['ca'] = $alpha; // mPDF 5.7.2
  1684. if ($mode == 'S' || $mode == 'B')
  1685. $a['CA'] = $alpha; // mPDF 5.7.2
  1686. $gs = $this->AddExtGState($a);
  1687. if ($return) {
  1688. return sprintf('/GS%d gs', $gs);
  1689. } else {
  1690. $this->_out(sprintf('/GS%d gs', $gs));
  1691. }
  1692. }
  1693. function AddExtGState($parms)
  1694. {
  1695. $n = count($this->extgstates);
  1696. // check if graphics state already exists
  1697. for ($i = 1; $i <= $n; $i++) {
  1698. if (count($this->extgstates[$i]['parms']) == count($parms)) {
  1699. $same = true;
  1700. foreach ($this->extgstates[$i]['parms'] AS $k => $v) {
  1701. if (!isset($parms[$k]) || $parms[$k] != $v) {
  1702. $same = false;
  1703. break;
  1704. }
  1705. }
  1706. if ($same) {
  1707. return $i;
  1708. }
  1709. }
  1710. }
  1711. $n++;
  1712. $this->extgstates[$n]['parms'] = $parms;
  1713. return $n;
  1714. }
  1715. function SetVisibility($v)
  1716. {
  1717. if (($this->PDFA || $this->PDFX) && $this->visibility != 'visible') {
  1718. $this->PDFAXwarnings[] = "Cannot set visibility to anything other than full when using PDFA or PDFX";
  1719. return '';
  1720. } elseif (!$this->PDFA && !$this->PDFX)
  1721. $this->pdf_version = '1.5';
  1722. if ($this->visibility != 'visible') {
  1723. $this->_out('EMC');
  1724. $this->hasOC = intval($this->hasOC);
  1725. }
  1726. if ($v == 'printonly') {
  1727. $this->_out('/OC /OC1 BDC');
  1728. $this->hasOC = ($this->hasOC | 1);
  1729. } elseif ($v == 'screenonly') {
  1730. $this->_out('/OC /OC2 BDC');
  1731. $this->hasOC = ($this->hasOC | 2);
  1732. } elseif ($v == 'hidden') {
  1733. $this->_out('/OC /OC3 BDC');
  1734. $this->hasOC = ($this->hasOC | 4);
  1735. } elseif ($v != 'visible')
  1736. throw new MpdfException('Incorrect visibility: ' . $v);
  1737. $this->visibility = $v;
  1738. }
  1739. function Open()
  1740. {
  1741. //Begin document
  1742. if ($this->state == 0) {
  1743. // Was is function _begindoc()
  1744. // Start document
  1745. $this->state = 1;
  1746. $this->_out('%PDF-' . $this->pdf_version);
  1747. $this->_out('%' . chr(226) . chr(227) . chr(207) . chr(211)); // 4 chars > 128 to show binary file
  1748. }
  1749. }
  1750. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1751. // DEPRACATED but included for backwards compatability
  1752. // Depracated - can use AddPage for all
  1753. function AddPages($a = '', $b = '', $c = '', $d = '', $e = '', $f = '', $g = '', $h = '', $i = '', $j = '', $k = '', $l = '', $m = '', $n = '', $o = '', $p = 0, $q = 0, $r = 0, $s = 0, $t = '', $u = '')
  1754. {
  1755. throw new MpdfException('function AddPages is depracated as of mPDF 6. Please use AddPage or HTML code methods instead.');
  1756. }
  1757. function startPageNums()
  1758. {
  1759. throw new MpdfException('function startPageNums is depracated as of mPDF 6.');
  1760. }
  1761. function setUnvalidatedText($a = '', $b = -1)
  1762. {
  1763. throw new MpdfException('function setUnvalidatedText is depracated as of mPDF 6. Please use SetWatermarkText instead.');
  1764. }
  1765. function SetAutoFont($a)
  1766. {
  1767. throw new MpdfException('function SetAutoFont is depracated as of mPDF 6. Please use autoScriptToLang instead. See config.php');
  1768. }
  1769. function Reference($a)
  1770. {
  1771. throw new MpdfException('function Reference is depracated as of mPDF 6. Please use IndexEntry instead.');
  1772. }
  1773. function ReferenceSee($a, $b)
  1774. {
  1775. throw new MpdfException('function ReferenceSee is depracated as of mPDF 6. Please use IndexEntrySee instead.');
  1776. }
  1777. function CreateReference($a = 1, $b = '', $c = '', $d = 3, $e = 1, $f = '', $g = 5, $h = '', $i = '', $j = false)
  1778. {
  1779. throw new MpdfException('function CreateReference is depracated as of mPDF 6. Please use InsertIndex instead.');
  1780. }
  1781. function CreateIndex($a = 1, $b = '', $c = '', $d = 3, $e = 1, $f = '', $g = 5, $h = '', $i = '', $j = false)
  1782. {
  1783. throw new MpdfException('function CreateIndex is depracated as of mPDF 6. Please use InsertIndex instead.');
  1784. }
  1785. // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1786. function Close()
  1787. {
  1788. // Check old Aliases - now depracated mPDF 6
  1789. if (isset($this->UnvalidatedText)) {
  1790. throw new MpdfException('$mpdf->UnvalidatedText is depracated as of mPDF 6. Please use $mpdf->watermarkText instead.');
  1791. }
  1792. if (isset($this->TopicIsUnvalidated)) {
  1793. throw new MpdfException('$mpdf->TopicIsUnvalidated is depracated as of mPDF 6. Please use $mpdf->showWatermarkText instead.');
  1794. }
  1795. if (isset($this->AliasNbPg)) {
  1796. throw new MpdfException('$mpdf->AliasNbPg is depracated as of mPDF 6. Please use $mpdf->aliasNbPg instead.');
  1797. }
  1798. if (isset($this->AliasNbPgGp)) {
  1799. throw new MpdfException('$mpdf->AliasNbPgGp is depracated as of mPDF 6. Please use $mpdf->aliasNbPgGp instead.');
  1800. }
  1801. if (isset($this->BiDirectional)) {
  1802. throw new MpdfException('$mpdf->BiDirectional is depracated as of mPDF 6. Please use $mpdf->biDirectional instead.');
  1803. }
  1804. if (isset($this->Anchor2Bookmark)) {
  1805. throw new MpdfException('$mpdf->Anchor2Bookmark is depracated as of mPDF 6. Please use $mpdf->anchor2Bookmark instead.');
  1806. }
  1807. if (isset($this->KeepColumns)) {
  1808. throw new MpdfException('$mpdf->KeepColumns is depracated as of mPDF 6. Please use $mpdf->keepColumns instead.');
  1809. }
  1810. if (isset($this->useOddEven)) {
  1811. throw new MpdfException('$mpdf->useOddEven is depracated as of mPDF 6. Please use $mpdf->mirrorMargins instead.');
  1812. }
  1813. if (isset($this->useSubstitutionsMB)) {
  1814. throw new MpdfException('$mpdf->useSubstitutionsMB is depracated as of mPDF 6. Please use $mpdf->useSubstitutions instead.');
  1815. }
  1816. if (isset($this->useLang)) {
  1817. throw new MpdfException('$mpdf->useLang is depracated as of mPDF 6. Please use $mpdf->autoLangToFont instead.');
  1818. }
  1819. if (isset($this->useAutoFont)) {
  1820. throw new MpdfException('$mpdf->useAutoFont is depracated. Please use $mpdf->autoScriptToLang instead.');
  1821. }
  1822. if ($this->progressBar) {
  1823. $this->UpdateProgressBar(2, '2', 'Closing last page');
  1824. } // *PROGRESS-BAR*
  1825. //Terminate document
  1826. if ($this->state == 3)
  1827. return;
  1828. if ($this->page == 0)
  1829. $this->AddPage($this->CurOrientation);
  1830. if (count($this->cellBorderBuffer)) {
  1831. $this->printcellbuffer();
  1832. } // *TABLES*
  1833. if ($this->tablebuffer) {
  1834. $this->printtablebuffer();
  1835. } // *TABLES*
  1836. /* -- COLUMNS -- */
  1837. if ($this->ColActive) {
  1838. $this->SetColumns(0);
  1839. $this->ColActive = 0;
  1840. if (count($this->columnbuffer)) {
  1841. $this->printcolumnbuffer();
  1842. }
  1843. }
  1844. /* -- END COLUMNS -- */
  1845. // BODY Backgrounds
  1846. $s = '';
  1847. $s .= $this->PrintBodyBackgrounds();
  1848. $s .= $this->PrintPageBackgrounds();
  1849. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $s . "\n" . '\\1', $this->pages[$this->page]);
  1850. $this->pageBackgrounds = array();
  1851. if ($this->visibility != 'visible')
  1852. $this->SetVisibility('visible');
  1853. $this->EndLayer();
  1854. if (!$this->tocontents || !$this->tocontents->TOCmark) { //Page footer
  1855. $this->InFooter = true;
  1856. $this->Footer();
  1857. $this->InFooter = false;
  1858. }
  1859. if ($this->tocontents && ($this->tocontents->TOCmark || count($this->tocontents->m_TOC))) {
  1860. $this->tocontents->insertTOC();
  1861. } // *TOC*
  1862. //Close page
  1863. $this->_endpage();
  1864. //Close document
  1865. $this->_enddoc();
  1866. }
  1867. /* -- BACKGROUNDS -- */
  1868. function _resizeBackgroundImage($imw, $imh, $cw, $ch, $resize = 0, $repx, $repy, $pba = array(), $size = array())
  1869. {
  1870. // pba is background positioning area (from CSS background-origin) may not always be set [x,y,w,h]
  1871. // size is from CSS3 background-size - takes precendence over old resize
  1872. // $w - absolute length or % or auto or cover | contain
  1873. // $h - absolute length or % or auto or cover | contain
  1874. if (isset($pba['w']))
  1875. $cw = $pba['w'];
  1876. if (isset($pba['h']))
  1877. $ch = $pba['h'];
  1878. $cw = $cw * _MPDFK;
  1879. $ch = $ch * _MPDFK;
  1880. if (empty($size) && !$resize) {
  1881. return array($imw, $imh, $repx, $repy);
  1882. }
  1883. if (isset($size['w']) && $size['w']) {
  1884. if ($size['w'] == 'contain') {
  1885. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.
  1886. // Same as resize==3
  1887. $h = $imh * $cw / $imw;
  1888. $w = $cw;
  1889. if ($h > $ch) {
  1890. $w = $w * $ch / $h;
  1891. $h = $ch;
  1892. }
  1893. } elseif ($size['w'] == 'cover') {
  1894. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
  1895. $h = $imh * $cw / $imw;
  1896. $w = $cw;
  1897. if ($h < $ch) {
  1898. $w = $w * $h / $ch;
  1899. $h = $ch;
  1900. }
  1901. } else {
  1902. if (stristr($size['w'], '%')) {
  1903. $size['w'] += 0;
  1904. $size['w'] /= 100;
  1905. $size['w'] = ($cw * $size['w']);
  1906. }
  1907. if (stristr($size['h'], '%')) {
  1908. $size['h'] += 0;
  1909. $size['h'] /= 100;
  1910. $size['h'] = ($ch * $size['h']);
  1911. }
  1912. if ($size['w'] == 'auto' && $size['h'] == 'auto') {
  1913. $w = $imw;
  1914. $h = $imh;
  1915. } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
  1916. $w = $imw * $size['h'] / $imh;
  1917. $h = $size['h'];
  1918. } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
  1919. $h = $imh * $size['w'] / $imw;
  1920. $w = $size['w'];
  1921. } else {
  1922. $w = $size['w'];
  1923. $h = $size['h'];
  1924. }
  1925. }
  1926. return array($w, $h, $repx, $repy);
  1927. } elseif ($resize == 1 && $imw > $cw) {
  1928. $h = $imh * $cw / $imw;
  1929. return array($cw, $h, $repx, $repy);
  1930. } elseif ($resize == 2 && $imh > $ch) {
  1931. $w = $imw * $ch / $imh;
  1932. return array($w, $ch, $repx, $repy);
  1933. } elseif ($resize == 3) {
  1934. $w = $imw;
  1935. $h = $imh;
  1936. if ($w > $cw) {
  1937. $h = $h * $cw / $w;
  1938. $w = $cw;
  1939. }
  1940. if ($h > $ch) {
  1941. $w = $w * $ch / $h;
  1942. $h = $ch;
  1943. }
  1944. return array($w, $h, $repx, $repy);
  1945. } elseif ($resize == 4) {
  1946. $h = $imh * $cw / $imw;
  1947. return array($cw, $h, $repx, $repy);
  1948. } elseif ($resize == 5) {
  1949. $w = $imw * $ch / $imh;
  1950. return array($w, $ch, $repx, $repy);
  1951. } elseif ($resize == 6) {
  1952. return array($cw, $ch, $repx, $repy);
  1953. }
  1954. return array($imw, $imh, $repx, $repy);
  1955. }
  1956. function SetBackground(&$properties, &$maxwidth)
  1957. {
  1958. if (isset($properties['BACKGROUND-ORIGIN']) && ($properties['BACKGROUND-ORIGIN'] == 'border-box' || $properties['BACKGROUND-ORIGIN'] == 'content-box')) {
  1959. $origin = $properties['BACKGROUND-ORIGIN'];
  1960. } else {
  1961. $origin = 'padding-box';
  1962. }
  1963. if (isset($properties['BACKGROUND-SIZE'])) {
  1964. if (stristr($properties['BACKGROUND-SIZE'], 'contain')) {
  1965. $bsw = $bsh = 'contain';
  1966. } elseif (stristr($properties['BACKGROUND-SIZE'], 'cover')) {
  1967. $bsw = $bsh = 'cover';
  1968. } else {
  1969. $bsw = $bsh = 'auto';
  1970. $sz = preg_split('/\s+/', trim($properties['BACKGROUND-SIZE']));
  1971. if (count($sz) == 2) {
  1972. $bsw = $sz[0];
  1973. $bsh = $sz[1];
  1974. } else {
  1975. $bsw = $sz[0];
  1976. }
  1977. if (!stristr($bsw, '%') && !stristr($bsw, 'auto')) {
  1978. $bsw = $this->ConvertSize($bsw, $maxwidth, $this->FontSize);
  1979. }
  1980. if (!stristr($bsh, '%') && !stristr($bsh, 'auto')) {
  1981. $bsh = $this->ConvertSize($bsh, $maxwidth, $this->FontSize);
  1982. }
  1983. }
  1984. $size = array('w' => $bsw, 'h' => $bsh);
  1985. } else {
  1986. $size = false;
  1987. } // mPDF 6
  1988. if (preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $properties['BACKGROUND-IMAGE'])) {
  1989. return array('gradient' => $properties['BACKGROUND-IMAGE'], 'origin' => $origin, 'size' => $size);
  1990. } else {
  1991. $file = $properties['BACKGROUND-IMAGE'];
  1992. $sizesarray = $this->Image($file, 0, 0, 0, 0, '', '', false, false, false, false, true);
  1993. if (isset($sizesarray['IMAGE_ID'])) {
  1994. $image_id = $sizesarray['IMAGE_ID'];
  1995. $orig_w = $sizesarray['WIDTH'] * _MPDFK; // in user units i.e. mm
  1996. $orig_h = $sizesarray['HEIGHT'] * _MPDFK; // (using $this->img_dpi)
  1997. if (isset($properties['BACKGROUND-IMAGE-RESOLUTION'])) {
  1998. if (preg_match('/from-image/i', $properties['BACKGROUND-IMAGE-RESOLUTION']) && isset($sizesarray['set-dpi']) && $sizesarray['set-dpi'] > 0) {
  1999. $orig_w *= $this->img_dpi / $sizesarray['set-dpi'];
  2000. $orig_h *= $this->img_dpi / $sizesarray['set-dpi'];
  2001. } elseif (preg_match('/(\d+)dpi/i', $properties['BACKGROUND-IMAGE-RESOLUTION'], $m)) {
  2002. $dpi = $m[1];
  2003. if ($dpi > 0) {
  2004. $orig_w *= $this->img_dpi / $dpi;
  2005. $orig_h *= $this->img_dpi / $dpi;
  2006. }
  2007. }
  2008. }
  2009. $x_repeat = true;
  2010. $y_repeat = true;
  2011. if (isset($properties['BACKGROUND-REPEAT'])) {
  2012. if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-x') {
  2013. $y_repeat = false;
  2014. }
  2015. if ($properties['BACKGROUND-REPEAT'] == 'no-repeat' || $properties['BACKGROUND-REPEAT'] == 'repeat-y') {
  2016. $x_repeat = false;
  2017. }
  2018. }
  2019. $x_pos = 0;
  2020. $y_pos = 0;
  2021. if (isset($properties['BACKGROUND-POSITION'])) {
  2022. $ppos = preg_split('/\s+/', $properties['BACKGROUND-POSITION']);
  2023. $x_pos = $ppos[0];
  2024. $y_pos = $ppos[1];
  2025. if (!stristr($x_pos, '%')) {
  2026. $x_pos = $this->ConvertSize($x_pos, $maxwidth, $this->FontSize);
  2027. }
  2028. if (!stristr($y_pos, '%')) {
  2029. $y_pos = $this->ConvertSize($y_pos, $maxwidth, $this->FontSize);
  2030. }
  2031. }
  2032. if (isset($properties['BACKGROUND-IMAGE-RESIZE'])) {
  2033. $resize = $properties['BACKGROUND-IMAGE-RESIZE'];
  2034. } else {
  2035. $resize = 0;
  2036. }
  2037. if (isset($properties['BACKGROUND-IMAGE-OPACITY'])) {
  2038. $opacity = $properties['BACKGROUND-IMAGE-OPACITY'];
  2039. } else {
  2040. $opacity = 1;
  2041. }
  2042. return array('image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $sizesarray['itype'], 'origin' => $origin, 'size' => $size);
  2043. }
  2044. }
  2045. return false;
  2046. }
  2047. /* -- END BACKGROUNDS -- */
  2048. function PrintBodyBackgrounds()
  2049. {
  2050. $s = '';
  2051. $clx = 0;
  2052. $cly = 0;
  2053. $clw = $this->w;
  2054. $clh = $this->h;
  2055. // If using bleed and trim margins in paged media
  2056. if ($this->pageDim[$this->page]['outer_width_LR'] || $this->pageDim[$this->page]['outer_width_TB']) {
  2057. $clx = $this->pageDim[$this->page]['outer_width_LR'] - $this->pageDim[$this->page]['bleedMargin'];
  2058. $cly = $this->pageDim[$this->page]['outer_width_TB'] - $this->pageDim[$this->page]['bleedMargin'];
  2059. $clw = $this->w - 2 * $clx;
  2060. $clh = $this->h - 2 * $cly;
  2061. }
  2062. if ($this->bodyBackgroundColor) {
  2063. $s .= 'q ' . $this->SetFColor($this->bodyBackgroundColor, true) . "\n";
  2064. if ($this->bodyBackgroundColor{0} == 5) { // RGBa
  2065. $s .= $this->SetAlpha(ord($this->bodyBackgroundColor{4}) / 100, 'Normal', true, 'F') . "\n";
  2066. } elseif ($this->bodyBackgroundColor{0} == 6) { // CMYKa
  2067. $s .= $this->SetAlpha(ord($this->bodyBackgroundColor{5}) / 100, 'Normal', true, 'F') . "\n";
  2068. }
  2069. $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', ($clx * _MPDFK), ($cly * _MPDFK), $clw * _MPDFK, $clh * _MPDFK) . "\n";
  2070. }
  2071. /* -- BACKGROUNDS -- */
  2072. if ($this->bodyBackgroundGradient) {
  2073. $g = $this->grad->parseBackgroundGradient($this->bodyBackgroundGradient);
  2074. if ($g) {
  2075. $s .= $this->grad->Gradient($clx, $cly, $clw, $clh, (isset($g['gradtype']) ? $g['gradtype'] : null), $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
  2076. }
  2077. }
  2078. if ($this->bodyBackgroundImage) {
  2079. if (isset($this->bodyBackgroundImage['gradient']) && $this->bodyBackgroundImage['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->bodyBackgroundImage['gradient'])) {
  2080. $g = $this->grad->parseMozGradient($this->bodyBackgroundImage['gradient']);
  2081. if ($g) {
  2082. $s .= $this->grad->Gradient($clx, $cly, $clw, $clh, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true);
  2083. }
  2084. } elseif ($this->bodyBackgroundImage['image_id']) { // Background pattern
  2085. $n = count($this->patterns) + 1;
  2086. // If using resize, uses TrimBox (not including the bleed)
  2087. list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($this->bodyBackgroundImage['orig_w'], $this->bodyBackgroundImage['orig_h'], $clw, $clh, $this->bodyBackgroundImage['resize'], $this->bodyBackgroundImage['x_repeat'], $this->bodyBackgroundImage['y_repeat']);
  2088. $this->patterns[$n] = array('x' => $clx, 'y' => $cly, 'w' => $clw, 'h' => $clh, 'pgh' => $this->h, 'image_id' => $this->bodyBackgroundImage['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $this->bodyBackgroundImage['x_pos'], 'y_pos' => $this->bodyBackgroundImage['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $this->bodyBackgroundImage['itype']);
  2089. if (($this->bodyBackgroundImage['opacity'] > 0 || $this->bodyBackgroundImage['opacity'] === '0') && $this->bodyBackgroundImage['opacity'] < 1) {
  2090. $opac = $this->SetAlpha($this->bodyBackgroundImage['opacity'], 'Normal', true);
  2091. } else {
  2092. $opac = '';
  2093. }
  2094. $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, ($clx * _MPDFK), ($cly * _MPDFK), $clw * _MPDFK, $clh * _MPDFK) . "\n";
  2095. }
  2096. }
  2097. /* -- END BACKGROUNDS -- */
  2098. return $s;
  2099. }
  2100. function _setClippingPath($clx, $cly, $clw, $clh)
  2101. {
  2102. $s = ' q 0 w '; // Line width=0
  2103. $s .= sprintf('%.3F %.3F m ', ($clx) * _MPDFK, ($this->h - ($cly)) * _MPDFK); // start point TL before the arc
  2104. $s .= sprintf('%.3F %.3F l ', ($clx) * _MPDFK, ($this->h - ($cly + $clh)) * _MPDFK); // line to BL
  2105. $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * _MPDFK, ($this->h - ($cly + $clh)) * _MPDFK); // line to BR
  2106. $s .= sprintf('%.3F %.3F l ', ($clx + $clw) * _MPDFK, ($this->h - ($cly)) * _MPDFK); // line to TR
  2107. $s .= sprintf('%.3F %.3F l ', ($clx) * _MPDFK, ($this->h - ($cly)) * _MPDFK); // line to TL
  2108. $s .= ' W n '; // Ends path no-op & Sets the clipping path
  2109. return $s;
  2110. }
  2111. function PrintPageBackgrounds($adjustmenty = 0)
  2112. {
  2113. $s = '';
  2114. ksort($this->pageBackgrounds);
  2115. foreach ($this->pageBackgrounds AS $bl => $pbs) {
  2116. foreach ($pbs AS $pb) {
  2117. if ((!isset($pb['image_id']) && !isset($pb['gradient'])) || isset($pb['shadowonly'])) { // Background colour or boxshadow
  2118. if ($pb['z-index'] > 0) {
  2119. $this->current_layer = $pb['z-index'];
  2120. $s .= "\n" . '/OCBZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
  2121. }
  2122. if ($pb['visibility'] != 'visible') {
  2123. if ($pb['visibility'] == 'printonly')
  2124. $s .= '/OC /OC1 BDC' . "\n";
  2125. elseif ($pb['visibility'] == 'screenonly')
  2126. $s .= '/OC /OC2 BDC' . "\n";
  2127. elseif ($pb['visibility'] == 'hidden')
  2128. $s .= '/OC /OC3 BDC' . "\n";
  2129. }
  2130. // Box shadow
  2131. if (isset($pb['shadow']) && $pb['shadow']) {
  2132. $s .= $pb['shadow'] . "\n";
  2133. }
  2134. if (isset($pb['clippath']) && $pb['clippath']) {
  2135. $s .= $pb['clippath'] . "\n";
  2136. }
  2137. $s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
  2138. if ($pb['col']{0} == 5) { // RGBa
  2139. $s .= $this->SetAlpha(ord($pb['col']{4}) / 100, 'Normal', true, 'F') . "\n";
  2140. } elseif ($pb['col']{0} == 6) { // CMYKa
  2141. $s .= $this->SetAlpha(ord($pb['col']{5}) / 100, 'Normal', true, 'F') . "\n";
  2142. }
  2143. $s .= sprintf('%.3F %.3F %.3F %.3F re f Q', $pb['x'] * _MPDFK, ($this->h - $pb['y']) * _MPDFK, $pb['w'] * _MPDFK, -$pb['h'] * _MPDFK) . "\n";
  2144. if (isset($pb['clippath']) && $pb['clippath']) {
  2145. $s .= 'Q' . "\n";
  2146. }
  2147. if ($pb['visibility'] != 'visible')
  2148. $s .= 'EMC' . "\n";
  2149. if ($pb['z-index'] > 0) {
  2150. $s .= "\n" . 'EMCBZ-index' . "\n";
  2151. $this->current_layer = 0;
  2152. }
  2153. }
  2154. }
  2155. /* -- BACKGROUNDS -- */
  2156. foreach ($pbs AS $pb) {
  2157. if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
  2158. if ($pb['z-index'] > 0) {
  2159. $this->current_layer = $pb['z-index'];
  2160. $s .= "\n" . '/OCGZ-index /ZI' . $pb['z-index'] . ' BDC' . "\n";
  2161. }
  2162. if ($pb['visibility'] != 'visible') {
  2163. if ($pb['visibility'] == 'printonly')
  2164. $s .= '/OC /OC1 BDC' . "\n";
  2165. elseif ($pb['visibility'] == 'screenonly')
  2166. $s .= '/OC /OC2 BDC' . "\n";
  2167. elseif ($pb['visibility'] == 'hidden')
  2168. $s .= '/OC /OC3 BDC' . "\n";
  2169. }
  2170. }
  2171. if (isset($pb['gradient']) && $pb['gradient']) {
  2172. if (isset($pb['clippath']) && $pb['clippath']) {
  2173. $s .= $pb['clippath'] . "\n";
  2174. }
  2175. $s .= $this->grad->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
  2176. if (isset($pb['clippath']) && $pb['clippath']) {
  2177. $s .= 'Q' . "\n";
  2178. }
  2179. } elseif (isset($pb['image_id']) && $pb['image_id']) { // Background Image
  2180. $pb['y'] -= $adjustmenty;
  2181. $pb['h'] += $adjustmenty;
  2182. $n = count($this->patterns) + 1;
  2183. list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat'], $pb['bpa'], $pb['size']);
  2184. $this->patterns[$n] = array('x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype'], 'bpa' => $pb['bpa']);
  2185. $x = $pb['x'] * _MPDFK;
  2186. $y = ($this->h - $pb['y']) * _MPDFK;
  2187. $w = $pb['w'] * _MPDFK;
  2188. $h = -$pb['h'] * _MPDFK;
  2189. if (isset($pb['clippath']) && $pb['clippath']) {
  2190. $s .= $pb['clippath'] . "\n";
  2191. }
  2192. if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
  2193. $iw = $pb['orig_w'] / _MPDFK;
  2194. $ih = $pb['orig_h'] / _MPDFK;
  2195. $w = $pb['w'];
  2196. $h = $pb['h'];
  2197. $x0 = $pb['x'];
  2198. $y0 = $pb['y'];
  2199. if (isset($pb['bpa']) && $pb['bpa']) {
  2200. $w = $pb['bpa']['w'];
  2201. $h = $pb['bpa']['h'];
  2202. $x0 = $pb['bpa']['x'];
  2203. $y0 = $pb['bpa']['y'];
  2204. }
  2205. if (isset($pb['size']['w']) && $pb['size']['w']) {
  2206. $size = $pb['size'];
  2207. if ($size['w'] == 'contain') {
  2208. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.
  2209. // Same as resize==3
  2210. $ih = $ih * $pb['bpa']['w'] / $iw;
  2211. $iw = $pb['bpa']['w'];
  2212. if ($ih > $pb['bpa']['h']) {
  2213. $iw = $iw * $pb['bpa']['h'] / $ih;
  2214. $ih = $pb['bpa']['h'];
  2215. }
  2216. } elseif ($size['w'] == 'cover') {
  2217. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
  2218. $ih = $ih * $pb['bpa']['w'] / $iw;
  2219. $iw = $pb['bpa']['w'];
  2220. if ($ih < $pb['bpa']['h']) {
  2221. $iw = $iw * $ih / $pb['bpa']['h'];
  2222. $ih = $pb['bpa']['h'];
  2223. }
  2224. } else {
  2225. if (stristr($size['w'], '%')) {
  2226. $size['w'] += 0;
  2227. $size['w'] /= 100;
  2228. $size['w'] = ($pb['bpa']['w'] * $size['w']);
  2229. }
  2230. if (stristr($size['h'], '%')) {
  2231. $size['h'] += 0;
  2232. $size['h'] /= 100;
  2233. $size['h'] = ($pb['bpa']['h'] * $size['h']);
  2234. }
  2235. if ($size['w'] == 'auto' && $size['h'] == 'auto') {
  2236. $iw = $iw;
  2237. $ih = $ih;
  2238. } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
  2239. $iw = $iw * $size['h'] / $ih;
  2240. $ih = $size['h'];
  2241. } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
  2242. $ih = $ih * $size['w'] / $iw;
  2243. $iw = $size['w'];
  2244. } else {
  2245. $iw = $size['w'];
  2246. $ih = $size['h'];
  2247. }
  2248. }
  2249. }
  2250. // Number to repeat
  2251. if ($pb['x_repeat']) {
  2252. $nx = ceil($pb['w'] / $iw) + 1;
  2253. } else {
  2254. $nx = 1;
  2255. }
  2256. if ($pb['y_repeat']) {
  2257. $ny = ceil($pb['h'] / $ih) + 1;
  2258. } else {
  2259. $ny = 1;
  2260. }
  2261. $x_pos = $pb['x_pos'];
  2262. if (stristr($x_pos, '%')) {
  2263. $x_pos += 0;
  2264. $x_pos /= 100;
  2265. $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
  2266. }
  2267. $y_pos = $pb['y_pos'];
  2268. if (stristr($y_pos, '%')) {
  2269. $y_pos += 0;
  2270. $y_pos /= 100;
  2271. $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
  2272. }
  2273. if ($nx > 1) {
  2274. while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
  2275. $x_pos -= $iw;
  2276. }
  2277. }
  2278. if ($ny > 1) {
  2279. while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
  2280. $y_pos -= $ih;
  2281. }
  2282. }
  2283. for ($xi = 0; $xi < $nx; $xi++) {
  2284. for ($yi = 0; $yi < $ny; $yi++) {
  2285. $x = $x0 + $x_pos + ($iw * $xi);
  2286. $y = $y0 + $y_pos + ($ih * $yi);
  2287. if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
  2288. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2289. } else {
  2290. $opac = '';
  2291. }
  2292. $s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac, $iw * _MPDFK, $ih * _MPDFK, $x * _MPDFK, ($this->h - ($y + $ih)) * _MPDFK, $pb['image_id']) . "\n";
  2293. }
  2294. }
  2295. } else {
  2296. if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
  2297. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2298. } else {
  2299. $opac = '';
  2300. }
  2301. $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
  2302. }
  2303. if (isset($pb['clippath']) && $pb['clippath']) {
  2304. $s .= 'Q' . "\n";
  2305. }
  2306. }
  2307. if ((isset($pb['gradient']) && $pb['gradient']) || (isset($pb['image_id']) && $pb['image_id'])) {
  2308. if ($pb['visibility'] != 'visible')
  2309. $s .= 'EMC' . "\n";
  2310. if ($pb['z-index'] > 0) {
  2311. $s .= "\n" . 'EMCGZ-index' . "\n";
  2312. $this->current_layer = 0;
  2313. }
  2314. }
  2315. }
  2316. /* -- END BACKGROUNDS -- */
  2317. }
  2318. return $s;
  2319. }
  2320. function PrintTableBackgrounds($adjustmenty = 0)
  2321. {
  2322. $s = '';
  2323. /* -- BACKGROUNDS -- */
  2324. ksort($this->tableBackgrounds);
  2325. foreach ($this->tableBackgrounds AS $bl => $pbs) {
  2326. foreach ($pbs AS $pb) {
  2327. if ((!isset($pb['gradient']) || !$pb['gradient']) && (!isset($pb['image_id']) || !$pb['image_id'])) {
  2328. $s .= 'q ' . $this->SetFColor($pb['col'], true) . "\n";
  2329. if ($pb['col']{0} == 5) { // RGBa
  2330. $s .= $this->SetAlpha(ord($pb['col']{4}) / 100, 'Normal', true, 'F') . "\n";
  2331. } elseif ($pb['col']{0} == 6) { // CMYKa
  2332. $s .= $this->SetAlpha(ord($pb['col']{5}) / 100, 'Normal', true, 'F') . "\n";
  2333. }
  2334. $s .= sprintf('%.3F %.3F %.3F %.3F re %s Q', $pb['x'] * _MPDFK, ($this->h - $pb['y']) * _MPDFK, $pb['w'] * _MPDFK, -$pb['h'] * _MPDFK, 'f') . "\n";
  2335. }
  2336. if (isset($pb['gradient']) && $pb['gradient']) {
  2337. if (isset($pb['clippath']) && $pb['clippath']) {
  2338. $s .= $pb['clippath'] . "\n";
  2339. }
  2340. $s .= $this->grad->Gradient($pb['x'], $pb['y'], $pb['w'], $pb['h'], $pb['gradtype'], $pb['stops'], $pb['colorspace'], $pb['coords'], $pb['extend'], true);
  2341. if (isset($pb['clippath']) && $pb['clippath']) {
  2342. $s .= 'Q' . "\n";
  2343. }
  2344. }
  2345. if (isset($pb['image_id']) && $pb['image_id']) { // Background pattern
  2346. $pb['y'] -= $adjustmenty;
  2347. $pb['h'] += $adjustmenty;
  2348. $n = count($this->patterns) + 1;
  2349. list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($pb['orig_w'], $pb['orig_h'], $pb['w'], $pb['h'], $pb['resize'], $pb['x_repeat'], $pb['y_repeat']);
  2350. $this->patterns[$n] = array('x' => $pb['x'], 'y' => $pb['y'], 'w' => $pb['w'], 'h' => $pb['h'], 'pgh' => $this->h, 'image_id' => $pb['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $pb['x_pos'], 'y_pos' => $pb['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $pb['itype']);
  2351. $x = $pb['x'] * _MPDFK;
  2352. $y = ($this->h - $pb['y']) * _MPDFK;
  2353. $w = $pb['w'] * _MPDFK;
  2354. $h = -$pb['h'] * _MPDFK;
  2355. // mPDF 5.7.3
  2356. if (($this->writingHTMLfooter || $this->writingHTMLheader) && (!isset($pb['clippath']) || $pb['clippath'] == '')) {
  2357. // Set clipping path
  2358. $pb['clippath'] = sprintf(' q 0 w %.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l W n ', $x, $y, $x, $y + $h, $x + $w, $y + $h, $x + $w, $y, $x, $y);
  2359. }
  2360. if (isset($pb['clippath']) && $pb['clippath']) {
  2361. $s .= $pb['clippath'] . "\n";
  2362. }
  2363. // mPDF 5.7.3
  2364. if ($this->writingHTMLfooter || $this->writingHTMLheader) { // Write each (tiles) image rather than use as a pattern
  2365. $iw = $pb['orig_w'] / _MPDFK;
  2366. $ih = $pb['orig_h'] / _MPDFK;
  2367. $w = $pb['w'];
  2368. $h = $pb['h'];
  2369. $x0 = $pb['x'];
  2370. $y0 = $pb['y'];
  2371. if (isset($pb['bpa']) && $pb['bpa']) {
  2372. $w = $pb['bpa']['w'];
  2373. $h = $pb['bpa']['h'];
  2374. $x0 = $pb['bpa']['x'];
  2375. $y0 = $pb['bpa']['y'];
  2376. }
  2377. // At present 'bpa' (background page area) is not set for tablebackgrounds - only pagebackgrounds
  2378. // For now, just set it as:
  2379. else {
  2380. $pb['bpa'] = array('x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h);
  2381. }
  2382. if (isset($pb['size']['w']) && $pb['size']['w']) {
  2383. $size = $pb['size'];
  2384. if ($size['w'] == 'contain') {
  2385. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.
  2386. // Same as resize==3
  2387. $ih = $ih * $pb['bpa']['w'] / $iw;
  2388. $iw = $pb['bpa']['w'];
  2389. if ($ih > $pb['bpa']['h']) {
  2390. $iw = $iw * $pb['bpa']['h'] / $ih;
  2391. $ih = $pb['bpa']['h'];
  2392. }
  2393. } elseif ($size['w'] == 'cover') {
  2394. // Scale the image, while preserving its intrinsic aspect ratio (if any), to the smallest size such that both its width and its height can completely cover the background positioning area.
  2395. $ih = $ih * $pb['bpa']['w'] / $iw;
  2396. $iw = $pb['bpa']['w'];
  2397. if ($ih < $pb['bpa']['h']) {
  2398. $iw = $iw * $ih / $pb['bpa']['h'];
  2399. $ih = $pb['bpa']['h'];
  2400. }
  2401. } else {
  2402. if (stristr($size['w'], '%')) {
  2403. $size['w'] += 0;
  2404. $size['w'] /= 100;
  2405. $size['w'] = ($pb['bpa']['w'] * $size['w']);
  2406. }
  2407. if (stristr($size['h'], '%')) {
  2408. $size['h'] += 0;
  2409. $size['h'] /= 100;
  2410. $size['h'] = ($pb['bpa']['h'] * $size['h']);
  2411. }
  2412. if ($size['w'] == 'auto' && $size['h'] == 'auto') {
  2413. $iw = $iw;
  2414. $ih = $ih;
  2415. } elseif ($size['w'] == 'auto' && $size['h'] != 'auto') {
  2416. $iw = $iw * $size['h'] / $ih;
  2417. $ih = $size['h'];
  2418. } elseif ($size['w'] != 'auto' && $size['h'] == 'auto') {
  2419. $ih = $ih * $size['w'] / $iw;
  2420. $iw = $size['w'];
  2421. } else {
  2422. $iw = $size['w'];
  2423. $ih = $size['h'];
  2424. }
  2425. }
  2426. }
  2427. // Number to repeat
  2428. if (isset($pb['x_repeat']) && $pb['x_repeat']) {
  2429. $nx = ceil($pb['w'] / $iw) + 1;
  2430. } else {
  2431. $nx = 1;
  2432. }
  2433. if (isset($pb['y_repeat']) && $pb['y_repeat']) {
  2434. $ny = ceil($pb['h'] / $ih) + 1;
  2435. } else {
  2436. $ny = 1;
  2437. }
  2438. $x_pos = $pb['x_pos'];
  2439. if (stristr($x_pos, '%')) {
  2440. $x_pos += 0;
  2441. $x_pos /= 100;
  2442. $x_pos = ($pb['bpa']['w'] * $x_pos) - ($iw * $x_pos);
  2443. }
  2444. $y_pos = $pb['y_pos'];
  2445. if (stristr($y_pos, '%')) {
  2446. $y_pos += 0;
  2447. $y_pos /= 100;
  2448. $y_pos = ($pb['bpa']['h'] * $y_pos) - ($ih * $y_pos);
  2449. }
  2450. if ($nx > 1) {
  2451. while ($x_pos > ($pb['x'] - $pb['bpa']['x'])) {
  2452. $x_pos -= $iw;
  2453. }
  2454. }
  2455. if ($ny > 1) {
  2456. while ($y_pos > ($pb['y'] - $pb['bpa']['y'])) {
  2457. $y_pos -= $ih;
  2458. }
  2459. }
  2460. for ($xi = 0; $xi < $nx; $xi++) {
  2461. for ($yi = 0; $yi < $ny; $yi++) {
  2462. $x = $x0 + $x_pos + ($iw * $xi);
  2463. $y = $y0 + $y_pos + ($ih * $yi);
  2464. if ($pb['opacity'] > 0 && $pb['opacity'] < 1) {
  2465. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2466. } else {
  2467. $opac = '';
  2468. }
  2469. $s .= sprintf("q %s %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $opac, $iw * _MPDFK, $ih * _MPDFK, $x * _MPDFK, ($this->h - ($y + $ih)) * _MPDFK, $pb['image_id']) . "\n";
  2470. }
  2471. }
  2472. } else {
  2473. if (($pb['opacity'] > 0 || $pb['opacity'] === '0') && $pb['opacity'] < 1) {
  2474. $opac = $this->SetAlpha($pb['opacity'], 'Normal', true);
  2475. } else {
  2476. $opac = '';
  2477. }
  2478. $s .= sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $x, $y, $w, $h) . "\n";
  2479. }
  2480. if (isset($pb['clippath']) && $pb['clippath']) {
  2481. $s .= 'Q' . "\n";
  2482. }
  2483. }
  2484. }
  2485. }
  2486. /* -- END BACKGROUNDS -- */
  2487. return $s;
  2488. }
  2489. function BeginLayer($id)
  2490. {
  2491. if ($this->current_layer > 0)
  2492. $this->EndLayer();
  2493. if ($id < 1) {
  2494. return false;
  2495. }
  2496. if (!isset($this->layers[$id])) {
  2497. $this->layers[$id] = array('name' => 'Layer ' . ($id));
  2498. if (($this->PDFA || $this->PDFX)) {
  2499. $this->PDFAXwarnings[] = "Cannot use layers when using PDFA or PDFX";
  2500. return '';
  2501. } elseif (!$this->PDFA && !$this->PDFX) {
  2502. $this->pdf_version = '1.5';
  2503. }
  2504. }
  2505. $this->current_layer = $id;
  2506. $this->_out('/OCZ-index /ZI' . $id . ' BDC');
  2507. $this->pageoutput[$this->page] = array();
  2508. }
  2509. function EndLayer()
  2510. {
  2511. if ($this->current_layer > 0) {
  2512. $this->_out('EMCZ-index');
  2513. $this->current_layer = 0;
  2514. }
  2515. }
  2516. function AddPageByArray($a)
  2517. {
  2518. if (!is_array($a)) {
  2519. $a = array();
  2520. }
  2521. $orientation = (isset($a['orientation']) ? $a['orientation'] : '');
  2522. $condition = (isset($a['condition']) ? $a['condition'] : (isset($a['type']) ? $a['type'] : ''));
  2523. $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
  2524. $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
  2525. $suppress = (isset($a['suppress']) ? $a['suppress'] : '');
  2526. $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
  2527. $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
  2528. $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
  2529. $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
  2530. $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
  2531. $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
  2532. $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
  2533. $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
  2534. $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
  2535. $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
  2536. $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
  2537. $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
  2538. $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
  2539. $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
  2540. $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
  2541. $newformat = (isset($a['newformat']) ? $a['newformat'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
  2542. $this->AddPage($orientation, $condition, $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
  2543. }
  2544. // mPDF 6 pagebreaktype
  2545. function _preForcedPagebreak($pagebreaktype)
  2546. {
  2547. if ($pagebreaktype == 'cloneall') {
  2548. // Close any open block tags
  2549. $arr = array();
  2550. $ai = 0;
  2551. for ($b = $this->blklvl; $b > 0; $b--) {
  2552. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  2553. }
  2554. if ($this->blklvl == 0 && !empty($this->textbuffer)) { //Output previously buffered content
  2555. $this->printbuffer($this->textbuffer, 1);
  2556. $this->textbuffer = array();
  2557. }
  2558. } elseif ($pagebreaktype == 'clonebycss') {
  2559. // Close open block tags whilst box-decoration-break==clone
  2560. $arr = array();
  2561. $ai = 0;
  2562. for ($b = $this->blklvl; $b > 0; $b--) {
  2563. if (isset($this->blk[$b]['box_decoration_break']) && $this->blk[$b]['box_decoration_break'] == 'clone') {
  2564. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  2565. } else {
  2566. if ($b == $this->blklvl && !empty($this->textbuffer)) { //Output previously buffered content
  2567. $this->printbuffer($this->textbuffer, 1);
  2568. $this->textbuffer = array();
  2569. }
  2570. break;
  2571. }
  2572. }
  2573. } elseif (!empty($this->textbuffer)) { //Output previously buffered content
  2574. $this->printbuffer($this->textbuffer, 1);
  2575. $this->textbuffer = array();
  2576. }
  2577. }
  2578. // mPDF 6 pagebreaktype
  2579. function _postForcedPagebreak($pagebreaktype, $startpage, $save_blk, $save_blklvl)
  2580. {
  2581. if ($pagebreaktype == 'cloneall') {
  2582. $this->blk = array();
  2583. $this->blk[0] = $save_blk[0];
  2584. // Re-open block tags
  2585. $this->blklvl = 0;
  2586. $arr = array();
  2587. $i = 0;
  2588. for ($b = 1; $b <= $save_blklvl; $b++) {
  2589. $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
  2590. }
  2591. } elseif ($pagebreaktype == 'clonebycss') {
  2592. $this->blk = array();
  2593. $this->blk[0] = $save_blk[0];
  2594. // Don't re-open tags for lowest level elements - so need to do some adjustments
  2595. for ($b = 1; $b <= $this->blklvl; $b++) {
  2596. $this->blk[$b] = $save_blk[$b];
  2597. $this->blk[$b]['startpage'] = 0;
  2598. $this->blk[$b]['y0'] = $this->y; // ?? $this->tMargin
  2599. if (($this->page - $startpage) % 2) {
  2600. if (isset($this->blk[$b]['x0'])) {
  2601. $this->blk[$b]['x0'] += $this->MarginCorrection;
  2602. } else {
  2603. $this->blk[$b]['x0'] = $this->MarginCorrection;
  2604. }
  2605. }
  2606. //for Float DIV
  2607. $this->blk[$b]['marginCorrected'][$this->page] = true;
  2608. }
  2609. // Re-open block tags for any that have box_decoration_break==clone
  2610. $arr = array();
  2611. $i = 0;
  2612. for ($b = $this->blklvl + 1; $b <= $save_blklvl; $b++) {
  2613. if ($b < $this->blklvl) {
  2614. $this->lastblocklevelchange = -1;
  2615. }
  2616. $this->tag->OpenTag($save_blk[$b]['tag'], $save_blk[$b]['attr'], $arr, $i);
  2617. }
  2618. if ($this->blk[$this->blklvl]['box_decoration_break'] != 'clone') {
  2619. $this->lastblocklevelchange = -1;
  2620. }
  2621. } else {
  2622. $this->lastblocklevelchange = -1;
  2623. }
  2624. }
  2625. function AddPage($orientation = '', $condition = '', $resetpagenum = '', $pagenumstyle = '', $suppress = '', $mgl = '', $mgr = '', $mgt = '', $mgb = '', $mgh = '', $mgf = '', $ohname = '', $ehname = '', $ofname = '', $efname = '', $ohvalue = 0, $ehvalue = 0, $ofvalue = 0, $efvalue = 0, $pagesel = '', $newformat = '')
  2626. {
  2627. /* -- CSS-FLOAT -- */
  2628. // Float DIV
  2629. // Cannot do with columns on, or if any change in page orientation/margins etc.
  2630. // If next page already exists - i.e background /headers and footers already written
  2631. if ($this->state > 0 && $this->page < count($this->pages)) {
  2632. $bak_cml = $this->cMarginL;
  2633. $bak_cmr = $this->cMarginR;
  2634. $bak_dw = $this->divwidth;
  2635. // Paint Div Border if necessary
  2636. if ($this->blklvl > 0) {
  2637. $save_tr = $this->table_rotate; // *TABLES*
  2638. $this->table_rotate = 0; // *TABLES*
  2639. if ($this->y == $this->blk[$this->blklvl]['y0']) {
  2640. $this->blk[$this->blklvl]['startpage'] ++;
  2641. }
  2642. if (($this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table']) {
  2643. $toplvl = $this->blklvl;
  2644. } else {
  2645. $toplvl = $this->blklvl - 1;
  2646. }
  2647. $sy = $this->y;
  2648. for ($bl = 1; $bl <= $toplvl; $bl++) {
  2649. $this->PaintDivBB('pagebottom', 0, $bl);
  2650. }
  2651. $this->y = $sy;
  2652. $this->table_rotate = $save_tr; // *TABLES*
  2653. }
  2654. $s = $this->PrintPageBackgrounds();
  2655. // Writes after the marker so not overwritten later by page background etc.
  2656. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  2657. $this->pageBackgrounds = array();
  2658. $family = $this->FontFamily;
  2659. $style = $this->FontStyle;
  2660. $size = $this->FontSizePt;
  2661. $lw = $this->LineWidth;
  2662. $dc = $this->DrawColor;
  2663. $fc = $this->FillColor;
  2664. $tc = $this->TextColor;
  2665. $cf = $this->ColorFlag;
  2666. $this->printfloatbuffer();
  2667. //Move to next page
  2668. $this->page++;
  2669. $this->ResetMargins();
  2670. $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
  2671. $this->x = $this->lMargin;
  2672. $this->y = $this->tMargin;
  2673. $this->FontFamily = '';
  2674. $this->_out('2 J');
  2675. $this->LineWidth = $lw;
  2676. $this->_out(sprintf('%.3F w', $lw * _MPDFK));
  2677. if ($family)
  2678. $this->SetFont($family, $style, $size, true, true);
  2679. $this->DrawColor = $dc;
  2680. if ($dc != $this->defDrawColor)
  2681. $this->_out($dc);
  2682. $this->FillColor = $fc;
  2683. if ($fc != $this->defFillColor)
  2684. $this->_out($fc);
  2685. $this->TextColor = $tc;
  2686. $this->ColorFlag = $cf;
  2687. for ($bl = 1; $bl <= $this->blklvl; $bl++) {
  2688. $this->blk[$bl]['y0'] = $this->y;
  2689. // Don't correct more than once for background DIV containing a Float
  2690. if (!isset($this->blk[$bl]['marginCorrected'][$this->page])) {
  2691. $this->blk[$bl]['x0'] += $this->MarginCorrection;
  2692. }
  2693. $this->blk[$bl]['marginCorrected'][$this->page] = true;
  2694. }
  2695. $this->cMarginL = $bak_cml;
  2696. $this->cMarginR = $bak_cmr;
  2697. $this->divwidth = $bak_dw;
  2698. return '';
  2699. }
  2700. /* -- END CSS-FLOAT -- */
  2701. //Start a new page
  2702. if ($this->state == 0)
  2703. $this->Open();
  2704. $bak_cml = $this->cMarginL;
  2705. $bak_cmr = $this->cMarginR;
  2706. $bak_dw = $this->divwidth;
  2707. $bak_lh = $this->lineheight;
  2708. $orientation = substr(strtoupper($orientation), 0, 1);
  2709. $condition = strtoupper($condition);
  2710. if ($condition == 'E') { // only adds new page if needed to create an Even page
  2711. if (!$this->mirrorMargins || ($this->page) % 2 == 0) {
  2712. return false;
  2713. }
  2714. } elseif ($condition == 'O') { // only adds new page if needed to create an Odd page
  2715. if (!$this->mirrorMargins || ($this->page) % 2 == 1) {
  2716. return false;
  2717. }
  2718. } elseif ($condition == 'NEXT-EVEN') { // always adds at least one new page to create an Even page
  2719. if (!$this->mirrorMargins) {
  2720. $condition = '';
  2721. } else {
  2722. if ($pagesel) {
  2723. $pbch = $pagesel;
  2724. $pagesel = '';
  2725. } // *CSS-PAGE*
  2726. else {
  2727. $pbch = false;
  2728. } // *CSS-PAGE*
  2729. $this->AddPage($this->CurOrientation, 'O');
  2730. $this->extrapagebreak = true; // mPDF 6 pagebreaktype
  2731. if ($pbch) {
  2732. $pagesel = $pbch;
  2733. } // *CSS-PAGE*
  2734. $condition = '';
  2735. }
  2736. } elseif ($condition == 'NEXT-ODD') { // always adds at least one new page to create an Odd page
  2737. if (!$this->mirrorMargins) {
  2738. $condition = '';
  2739. } else {
  2740. if ($pagesel) {
  2741. $pbch = $pagesel;
  2742. $pagesel = '';
  2743. } // *CSS-PAGE*
  2744. else {
  2745. $pbch = false;
  2746. } // *CSS-PAGE*
  2747. $this->AddPage($this->CurOrientation, 'E');
  2748. $this->extrapagebreak = true; // mPDF 6 pagebreaktype
  2749. if ($pbch) {
  2750. $pagesel = $pbch;
  2751. } // *CSS-PAGE*
  2752. $condition = '';
  2753. }
  2754. }
  2755. if ($resetpagenum || $pagenumstyle || $suppress) {
  2756. $this->PageNumSubstitutions[] = array('from' => ($this->page + 1), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress);
  2757. }
  2758. $save_tr = $this->table_rotate; // *TABLES*
  2759. $this->table_rotate = 0; // *TABLES*
  2760. $save_kwt = $this->kwt;
  2761. $this->kwt = 0;
  2762. $save_layer = $this->current_layer;
  2763. $save_vis = $this->visibility;
  2764. if ($this->visibility != 'visible')
  2765. $this->SetVisibility('visible');
  2766. $this->EndLayer();
  2767. // Paint Div Border if necessary
  2768. //PAINTS BACKGROUND COLOUR OR BORDERS for DIV - DISABLED FOR COLUMNS (cf. AcceptPageBreak) AT PRESENT in ->PaintDivBB
  2769. if (!$this->ColActive && $this->blklvl > 0) {
  2770. if (isset($this->blk[$this->blklvl]['y0']) && $this->y == $this->blk[$this->blklvl]['y0'] && !$this->extrapagebreak) { // mPDF 6 pagebreaktype
  2771. if (isset($this->blk[$this->blklvl]['startpage'])) {
  2772. $this->blk[$this->blklvl]['startpage'] ++;
  2773. } else {
  2774. $this->blk[$this->blklvl]['startpage'] = 1;
  2775. }
  2776. }
  2777. if ((isset($this->blk[$this->blklvl]['y0']) && $this->y > $this->blk[$this->blklvl]['y0']) || $this->flowingBlockAttr['is_table'] || $this->extrapagebreak) {
  2778. $toplvl = $this->blklvl;
  2779. } // mPDF 6 pagebreaktype
  2780. else {
  2781. $toplvl = $this->blklvl - 1;
  2782. }
  2783. $sy = $this->y;
  2784. for ($bl = 1; $bl <= $toplvl; $bl++) {
  2785. if (isset($this->blk[$bl]['z-index']) && $this->blk[$bl]['z-index'] > 0) {
  2786. $this->BeginLayer($this->blk[$bl]['z-index']);
  2787. }
  2788. if (isset($this->blk[$bl]['visibility']) && $this->blk[$bl]['visibility'] && $this->blk[$bl]['visibility'] != 'visible') {
  2789. $this->SetVisibility($this->blk[$bl]['visibility']);
  2790. }
  2791. $this->PaintDivBB('pagebottom', 0, $bl);
  2792. }
  2793. $this->y = $sy;
  2794. // RESET block y0 and x0 - see below
  2795. }
  2796. $this->extrapagebreak = false; // mPDF 6 pagebreaktype
  2797. if ($this->visibility != 'visible')
  2798. $this->SetVisibility('visible');
  2799. $this->EndLayer();
  2800. // BODY Backgrounds
  2801. if ($this->page > 0) {
  2802. $s = '';
  2803. $s .= $this->PrintBodyBackgrounds();
  2804. $s .= $this->PrintPageBackgrounds();
  2805. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $s . "\n" . '\\1', $this->pages[$this->page]);
  2806. $this->pageBackgrounds = array();
  2807. }
  2808. $save_kt = $this->keep_block_together;
  2809. $this->keep_block_together = 0;
  2810. $save_cols = false;
  2811. /* -- COLUMNS -- */
  2812. if ($this->ColActive) {
  2813. $save_cols = true;
  2814. $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
  2815. $this->SetColumns(0);
  2816. }
  2817. /* -- END COLUMNS -- */
  2818. $family = $this->FontFamily;
  2819. $style = $this->FontStyle;
  2820. $size = $this->FontSizePt;
  2821. $this->ColumnAdjust = true; // enables column height adjustment for the page
  2822. $lw = $this->LineWidth;
  2823. $dc = $this->DrawColor;
  2824. $fc = $this->FillColor;
  2825. $tc = $this->TextColor;
  2826. $cf = $this->ColorFlag;
  2827. if ($this->page > 0) {
  2828. //Page footer
  2829. $this->InFooter = true;
  2830. $this->Reset();
  2831. $this->pageoutput[$this->page] = array();
  2832. $this->Footer();
  2833. //Close page
  2834. $this->_endpage();
  2835. }
  2836. //Start new page
  2837. $this->_beginpage($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $newformat);
  2838. if ($this->docTemplate) {
  2839. $pagecount = $this->SetSourceFile($this->docTemplate);
  2840. if (($this->page - $this->docTemplateStart) > $pagecount) {
  2841. if ($this->docTemplateContinue) {
  2842. $tplIdx = $this->ImportPage($pagecount);
  2843. $this->UseTemplate($tplIdx);
  2844. }
  2845. } else {
  2846. $tplIdx = $this->ImportPage(($this->page - $this->docTemplateStart));
  2847. $this->UseTemplate($tplIdx);
  2848. }
  2849. }
  2850. if ($this->pageTemplate) {
  2851. $this->UseTemplate($this->pageTemplate);
  2852. }
  2853. // Tiling Patterns
  2854. $this->_out('___PAGE___START' . $this->uniqstr);
  2855. $this->_out('___BACKGROUND___PATTERNS' . $this->uniqstr);
  2856. $this->_out('___HEADER___MARKER' . $this->uniqstr);
  2857. $this->pageBackgrounds = array();
  2858. //Set line cap style to square
  2859. $this->SetLineCap(2);
  2860. //Set line width
  2861. $this->LineWidth = $lw;
  2862. $this->_out(sprintf('%.3F w', $lw * _MPDFK));
  2863. //Set font
  2864. if ($family)
  2865. $this->SetFont($family, $style, $size, true, true); // forces write
  2866. //Set colors
  2867. $this->DrawColor = $dc;
  2868. if ($dc != $this->defDrawColor)
  2869. $this->_out($dc);
  2870. $this->FillColor = $fc;
  2871. if ($fc != $this->defFillColor)
  2872. $this->_out($fc);
  2873. $this->TextColor = $tc;
  2874. $this->ColorFlag = $cf;
  2875. //Page header
  2876. $this->Header();
  2877. //Restore line width
  2878. if ($this->LineWidth != $lw) {
  2879. $this->LineWidth = $lw;
  2880. $this->_out(sprintf('%.3F w', $lw * _MPDFK));
  2881. }
  2882. //Restore font
  2883. if ($family)
  2884. $this->SetFont($family, $style, $size, true, true); // forces write
  2885. //Restore colors
  2886. if ($this->DrawColor != $dc) {
  2887. $this->DrawColor = $dc;
  2888. $this->_out($dc);
  2889. }
  2890. if ($this->FillColor != $fc) {
  2891. $this->FillColor = $fc;
  2892. $this->_out($fc);
  2893. }
  2894. $this->TextColor = $tc;
  2895. $this->ColorFlag = $cf;
  2896. $this->InFooter = false;
  2897. if ($save_layer > 0)
  2898. $this->BeginLayer($save_layer);
  2899. if ($save_vis != 'visible')
  2900. $this->SetVisibility($save_vis);
  2901. /* -- COLUMNS -- */
  2902. if ($save_cols) {
  2903. // Restore columns
  2904. $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
  2905. }
  2906. if ($this->ColActive) {
  2907. $this->SetCol(0);
  2908. }
  2909. /* -- END COLUMNS -- */
  2910. //RESET BLOCK BORDER TOP
  2911. if (!$this->ColActive) {
  2912. for ($bl = 1; $bl <= $this->blklvl; $bl++) {
  2913. $this->blk[$bl]['y0'] = $this->y;
  2914. if (isset($this->blk[$bl]['x0'])) {
  2915. $this->blk[$bl]['x0'] += $this->MarginCorrection;
  2916. } else {
  2917. $this->blk[$bl]['x0'] = $this->MarginCorrection;
  2918. }
  2919. // Added mPDF 3.0 Float DIV
  2920. $this->blk[$bl]['marginCorrected'][$this->page] = true;
  2921. }
  2922. }
  2923. $this->table_rotate = $save_tr; // *TABLES*
  2924. $this->kwt = $save_kwt;
  2925. $this->keep_block_together = $save_kt;
  2926. $this->cMarginL = $bak_cml;
  2927. $this->cMarginR = $bak_cmr;
  2928. $this->divwidth = $bak_dw;
  2929. $this->lineheight = $bak_lh;
  2930. }
  2931. function PageNo()
  2932. {
  2933. //Get current page number
  2934. return $this->page;
  2935. }
  2936. function AddSpotColorsFromFile($file)
  2937. {
  2938. $colors = @file($file);
  2939. if (!$colors) {
  2940. throw new MpdfException("Cannot load spot colors file - " . $file);
  2941. }
  2942. foreach ($colors AS $sc) {
  2943. list($name, $c, $m, $y, $k) = preg_split("/\t/", $sc);
  2944. $c = intval($c);
  2945. $m = intval($m);
  2946. $y = intval($y);
  2947. $k = intval($k);
  2948. $this->AddSpotColor($name, $c, $m, $y, $k);
  2949. }
  2950. }
  2951. function AddSpotColor($name, $c, $m, $y, $k)
  2952. {
  2953. $name = strtoupper(trim($name));
  2954. if (!isset($this->spotColors[$name])) {
  2955. $i = count($this->spotColors) + 1;
  2956. $this->spotColors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
  2957. $this->spotColorIDs[$i] = $name;
  2958. }
  2959. }
  2960. function SetColor($col, $type = '')
  2961. {
  2962. $out = '';
  2963. if (!$col) {
  2964. return '';
  2965. } // mPDF 6
  2966. if ($col{0} == 3 || $col{0} == 5) { // RGB / RGBa
  2967. $out = sprintf('%.3F %.3F %.3F rg', ord($col{1}) / 255, ord($col{2}) / 255, ord($col{3}) / 255);
  2968. } elseif ($col{0} == 1) { // GRAYSCALE
  2969. $out = sprintf('%.3F g', ord($col{1}) / 255);
  2970. } elseif ($col{0} == 2) { // SPOT COLOR
  2971. $out = sprintf('/CS%d cs %.3F scn', ord($col{1}), ord($col{2}) / 100);
  2972. } elseif ($col{0} == 4 || $col{0} == 6) { // CMYK / CMYKa
  2973. $out = sprintf('%.3F %.3F %.3F %.3F k', ord($col{1}) / 100, ord($col{2}) / 100, ord($col{3}) / 100, ord($col{4}) / 100);
  2974. }
  2975. if ($type == 'Draw') {
  2976. $out = strtoupper($out);
  2977. } // e.g. rg => RG
  2978. elseif ($type == 'CodeOnly') {
  2979. $out = preg_replace('/\s(rg|g|k)/', '', $out);
  2980. }
  2981. return $out;
  2982. }
  2983. function SetDColor($col, $return = false)
  2984. {
  2985. $out = $this->SetColor($col, 'Draw');
  2986. if ($return) {
  2987. return $out;
  2988. }
  2989. if ($out == '') {
  2990. return '';
  2991. }
  2992. $this->DrawColor = $out;
  2993. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['DrawColor']) && $this->pageoutput[$this->page]['DrawColor'] != $this->DrawColor) || !isset($this->pageoutput[$this->page]['DrawColor']))) {
  2994. $this->_out($this->DrawColor);
  2995. }
  2996. $this->pageoutput[$this->page]['DrawColor'] = $this->DrawColor;
  2997. }
  2998. function SetFColor($col, $return = false)
  2999. {
  3000. $out = $this->SetColor($col, 'Fill');
  3001. if ($return) {
  3002. return $out;
  3003. }
  3004. if ($out == '') {
  3005. return '';
  3006. }
  3007. $this->FillColor = $out;
  3008. $this->ColorFlag = ($out != $this->TextColor);
  3009. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor']))) {
  3010. $this->_out($this->FillColor);
  3011. }
  3012. $this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
  3013. }
  3014. function SetTColor($col, $return = false)
  3015. {
  3016. $out = $this->SetColor($col, 'Text');
  3017. if ($return) {
  3018. return $out;
  3019. }
  3020. if ($out == '') {
  3021. return '';
  3022. }
  3023. $this->TextColor = $out;
  3024. $this->ColorFlag = ($this->FillColor != $out);
  3025. }
  3026. function SetDrawColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
  3027. {
  3028. //Set color for all stroking operations
  3029. $col = array();
  3030. if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
  3031. $col = $this->ConvertColor($r);
  3032. } elseif ($col4 == -1) {
  3033. $col = $this->ConvertColor('rgb(' . $r . ',' . $g . ',' . $b . ')');
  3034. } else {
  3035. $col = $this->ConvertColor('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')');
  3036. }
  3037. $out = $this->SetDColor($col, $return);
  3038. return $out;
  3039. }
  3040. function SetFillColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
  3041. {
  3042. //Set color for all filling operations
  3043. $col = array();
  3044. if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
  3045. $col = $this->ConvertColor($r);
  3046. } elseif ($col4 == -1) {
  3047. $col = $this->ConvertColor('rgb(' . $r . ',' . $g . ',' . $b . ')');
  3048. } else {
  3049. $col = $this->ConvertColor('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')');
  3050. }
  3051. $out = $this->SetFColor($col, $return);
  3052. return $out;
  3053. }
  3054. function SetTextColor($r, $g = -1, $b = -1, $col4 = -1, $return = false)
  3055. {
  3056. //Set color for text
  3057. $col = array();
  3058. if (($r == 0 and $g == 0 and $b == 0 && $col4 == -1) or $g == -1) {
  3059. $col = $this->ConvertColor($r);
  3060. } elseif ($col4 == -1) {
  3061. $col = $this->ConvertColor('rgb(' . $r . ',' . $g . ',' . $b . ')');
  3062. } else {
  3063. $col = $this->ConvertColor('cmyk(' . $r . ',' . $g . ',' . $b . ',' . $col4 . ')');
  3064. }
  3065. $out = $this->SetTColor($col, $return);
  3066. return $out;
  3067. }
  3068. function _getCharWidth(&$cw, $u, $isdef = true)
  3069. {
  3070. $w = 0;
  3071. if ($u == 0) {
  3072. $w = false;
  3073. } elseif (isset($cw[$u * 2 + 1])) {
  3074. $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
  3075. }
  3076. if ($w == 65535) {
  3077. return 0;
  3078. } elseif ($w) {
  3079. return $w;
  3080. } elseif ($isdef) {
  3081. return false;
  3082. } else {
  3083. return 0;
  3084. }
  3085. }
  3086. function _charDefined(&$cw, $u)
  3087. {
  3088. $w = 0;
  3089. if ($u == 0) {
  3090. return false;
  3091. }
  3092. if (isset($cw[$u * 2 + 1])) {
  3093. $w = (ord($cw[$u * 2]) << 8) + ord($cw[$u * 2 + 1]);
  3094. }
  3095. if ($w) {
  3096. return true;
  3097. } else {
  3098. return false;
  3099. }
  3100. }
  3101. function GetCharWidthCore($c)
  3102. {
  3103. //Get width of a single character in the current Core font
  3104. $c = (string) $c;
  3105. $w = 0;
  3106. // Soft Hyphens chr(173)
  3107. if ($c == chr(173) && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  3108. return 0;
  3109. } elseif (($this->textvar & FC_SMALLCAPS) && isset($this->upperCase[ord($c)])) { // mPDF 5.7.1
  3110. $charw = $this->CurrentFont['cw'][chr($this->upperCase[ord($c)])];
  3111. if ($charw !== false) {
  3112. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3113. $w+=$charw;
  3114. }
  3115. } elseif (isset($this->CurrentFont['cw'][$c])) {
  3116. $w += $this->CurrentFont['cw'][$c];
  3117. } elseif (isset($this->CurrentFont['cw'][ord($c)])) {
  3118. $w += $this->CurrentFont['cw'][ord($c)];
  3119. }
  3120. $w *= ($this->FontSize / 1000);
  3121. if ($this->minwSpacing || $this->fixedlSpacing) {
  3122. if ($c == ' ')
  3123. $nb_spaces = 1;
  3124. else
  3125. $nb_spaces = 0;
  3126. $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
  3127. }
  3128. return ($w);
  3129. }
  3130. function GetCharWidthNonCore($c, $addSubset = true)
  3131. {
  3132. //Get width of a single character in the current Non-Core font
  3133. $c = (string) $c;
  3134. $w = 0;
  3135. $unicode = $this->UTF8StringToArray($c, $addSubset);
  3136. $char = $unicode[0];
  3137. /* -- CJK-FONTS -- */
  3138. if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
  3139. if ($char == 173) {
  3140. return 0;
  3141. } // Soft Hyphens
  3142. elseif (isset($this->CurrentFont['cw'][$char])) {
  3143. $w+=$this->CurrentFont['cw'][$char];
  3144. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3145. $w += $this->CurrentFont['MissingWidth'];
  3146. } else {
  3147. $w += 500;
  3148. }
  3149. } else {
  3150. /* -- END CJK-FONTS -- */
  3151. if ($char == 173) {
  3152. return 0;
  3153. } // Soft Hyphens
  3154. elseif (($this->textvar & FC_SMALLCAPS) && isset($this->upperCase[$char])) { // mPDF 5.7.1
  3155. $charw = $this->_getCharWidth($this->CurrentFont['cw'], $this->upperCase[$char]);
  3156. if ($charw !== false) {
  3157. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3158. $w+=$charw;
  3159. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3160. $w += $this->CurrentFont['desc']['MissingWidth'];
  3161. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3162. $w += $this->CurrentFont['MissingWidth'];
  3163. } else {
  3164. $w += 500;
  3165. }
  3166. } else {
  3167. $charw = $this->_getCharWidth($this->CurrentFont['cw'], $char);
  3168. if ($charw !== false) {
  3169. $w+=$charw;
  3170. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3171. $w += $this->CurrentFont['desc']['MissingWidth'];
  3172. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3173. $w += $this->CurrentFont['MissingWidth'];
  3174. } else {
  3175. $w += 500;
  3176. }
  3177. }
  3178. } // *CJK-FONTS*
  3179. $w *= ($this->FontSize / 1000);
  3180. if ($this->minwSpacing || $this->fixedlSpacing) {
  3181. if ($c == ' ')
  3182. $nb_spaces = 1;
  3183. else
  3184. $nb_spaces = 0;
  3185. $w += $this->fixedlSpacing + ($nb_spaces * $this->minwSpacing);
  3186. }
  3187. return ($w);
  3188. }
  3189. function GetCharWidth($c, $addSubset = true)
  3190. {
  3191. if (!$this->usingCoreFont) {
  3192. return $this->GetCharWidthNonCore($c, $addSubset);
  3193. } else {
  3194. return $this->GetCharWidthCore($c);
  3195. }
  3196. }
  3197. function GetStringWidth($s, $addSubset = true, $OTLdata = false, $textvar = 0, $includeKashida = false)
  3198. { // mPDF 5.7.1
  3199. //Get width of a string in the current font
  3200. $s = (string) $s;
  3201. $cw = &$this->CurrentFont['cw'];
  3202. $w = 0;
  3203. $kerning = 0;
  3204. $lastchar = 0;
  3205. $nb_carac = 0;
  3206. $nb_spaces = 0;
  3207. $kashida = 0;
  3208. // mPDF ITERATION
  3209. if ($this->iterationCounter)
  3210. $s = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $s);
  3211. if (!$this->usingCoreFont) {
  3212. $discards = substr_count($s, "\xc2\xad"); // mPDF 6 soft hyphens [U+00AD]
  3213. $unicode = $this->UTF8StringToArray($s, $addSubset);
  3214. if ($this->minwSpacing || $this->fixedlSpacing) {
  3215. $nb_spaces = mb_substr_count($s, ' ', $this->mb_enc);
  3216. $nb_carac = count($unicode) - $discards; // mPDF 6
  3217. // mPDF 5.7.1
  3218. // Use GPOS OTL
  3219. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  3220. if (isset($OTLdata['group']) && $OTLdata['group']) {
  3221. $nb_carac -= substr_count($OTLdata['group'], 'M');
  3222. }
  3223. }
  3224. }
  3225. /* -- CJK-FONTS -- */
  3226. if ($this->CurrentFont['type'] == 'Type0') { // CJK Adobe fonts
  3227. foreach ($unicode as $char) {
  3228. if ($char == 0x00AD) {
  3229. continue;
  3230. } // mPDF 6 soft hyphens [U+00AD]
  3231. if (isset($cw[$char])) {
  3232. $w+=$cw[$char];
  3233. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3234. $w += $this->CurrentFont['MissingWidth'];
  3235. } else {
  3236. $w += 500;
  3237. }
  3238. }
  3239. } else {
  3240. /* -- END CJK-FONTS -- */
  3241. foreach ($unicode as $i => $char) {
  3242. if ($char == 0x00AD) {
  3243. continue;
  3244. } // mPDF 6 soft hyphens [U+00AD]
  3245. if (($textvar & FC_SMALLCAPS) && isset($this->upperCase[$char])) {
  3246. $charw = $this->_getCharWidth($cw, $this->upperCase[$char]);
  3247. if ($charw !== false) {
  3248. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3249. $w+=$charw;
  3250. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3251. $w += $this->CurrentFont['desc']['MissingWidth'];
  3252. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3253. $w += $this->CurrentFont['MissingWidth'];
  3254. } else {
  3255. $w += 500;
  3256. }
  3257. } else {
  3258. $charw = $this->_getCharWidth($cw, $char);
  3259. if ($charw !== false) {
  3260. $w+=$charw;
  3261. } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
  3262. $w += $this->CurrentFont['desc']['MissingWidth'];
  3263. } elseif (isset($this->CurrentFont['MissingWidth'])) {
  3264. $w += $this->CurrentFont['MissingWidth'];
  3265. } else {
  3266. $w += 500;
  3267. }
  3268. // mPDF 5.7.1
  3269. // Use GPOS OTL
  3270. // ...GetStringWidth...
  3271. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata)) {
  3272. if (isset($OTLdata['GPOSinfo'][$i]['wDir']) && $OTLdata['GPOSinfo'][$i]['wDir'] == 'RTL') {
  3273. if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceR']) && $OTLdata['GPOSinfo'][$i]['XAdvanceR']) {
  3274. $w += $OTLdata['GPOSinfo'][$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  3275. }
  3276. } else {
  3277. if (isset($OTLdata['GPOSinfo'][$i]['XAdvanceL']) && $OTLdata['GPOSinfo'][$i]['XAdvanceL']) {
  3278. $w += $OTLdata['GPOSinfo'][$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  3279. }
  3280. }
  3281. // Kashida from GPOS
  3282. // Kashida is set as an absolute length value (already set as a proportion based on useKashida %)
  3283. if ($includeKashida && isset($OTLdata['GPOSinfo'][$i]['kashida_space']) && $OTLdata['GPOSinfo'][$i]['kashida_space']) {
  3284. $kashida += $OTLdata['GPOSinfo'][$i]['kashida_space'];
  3285. }
  3286. }
  3287. if (($textvar & FC_KERNING) && $lastchar) {
  3288. if (isset($this->CurrentFont['kerninfo'][$lastchar][$char])) {
  3289. $kerning += $this->CurrentFont['kerninfo'][$lastchar][$char];
  3290. }
  3291. }
  3292. $lastchar = $char;
  3293. }
  3294. }
  3295. } // *CJK-FONTS*
  3296. } else {
  3297. if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  3298. $s = str_replace(chr(173), '', $s);
  3299. }
  3300. $nb_carac = $l = strlen($s);
  3301. if ($this->minwSpacing || $this->fixedlSpacing) {
  3302. $nb_spaces = substr_count($s, ' ');
  3303. }
  3304. for ($i = 0; $i < $l; $i++) {
  3305. if (($textvar & FC_SMALLCAPS) && isset($this->upperCase[ord($s[$i])])) { // mPDF 5.7.1
  3306. $charw = $cw[chr($this->upperCase[ord($s[$i])])];
  3307. if ($charw !== false) {
  3308. $charw = $charw * $this->smCapsScale * $this->smCapsStretch / 100;
  3309. $w+=$charw;
  3310. }
  3311. } elseif (isset($cw[$s[$i]])) {
  3312. $w += $cw[$s[$i]];
  3313. } elseif (isset($cw[ord($s[$i])])) {
  3314. $w += $cw[ord($s[$i])];
  3315. }
  3316. if (($textvar & FC_KERNING) && $i > 0) { // mPDF 5.7.1
  3317. if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]])) {
  3318. $kerning += $this->CurrentFont['kerninfo'][$s[($i - 1)]][$s[$i]];
  3319. }
  3320. }
  3321. }
  3322. }
  3323. unset($cw);
  3324. if ($textvar & FC_KERNING) {
  3325. $w += $kerning;
  3326. } // mPDF 5.7.1
  3327. $w *= ($this->FontSize / 1000);
  3328. $w += (($nb_carac + $nb_spaces) * $this->fixedlSpacing) + ($nb_spaces * $this->minwSpacing);
  3329. $w += $kashida / _MPDFK;
  3330. return ($w);
  3331. }
  3332. function SetLineWidth($width)
  3333. {
  3334. //Set line width
  3335. $this->LineWidth = $width;
  3336. $lwout = (sprintf('%.3F w', $width * _MPDFK));
  3337. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineWidth']) && $this->pageoutput[$this->page]['LineWidth'] != $lwout) || !isset($this->pageoutput[$this->page]['LineWidth']))) {
  3338. $this->_out($lwout);
  3339. }
  3340. $this->pageoutput[$this->page]['LineWidth'] = $lwout;
  3341. }
  3342. function Line($x1, $y1, $x2, $y2)
  3343. {
  3344. //Draw a line
  3345. $this->_out(sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * _MPDFK, ($this->h - $y1) * _MPDFK, $x2 * _MPDFK, ($this->h - $y2) * _MPDFK));
  3346. }
  3347. function Arrow($x1, $y1, $x2, $y2, $headsize = 3, $fill = 'B', $angle = 25)
  3348. {
  3349. //F == fill //S == stroke //B == stroke and fill
  3350. // angle = splay of arrowhead - 1 - 89 degrees
  3351. if ($fill == 'F')
  3352. $fill = 'f';
  3353. elseif ($fill == 'FD' or $fill == 'DF' or $fill == 'B')
  3354. $fill = 'B';
  3355. else
  3356. $fill = 'S';
  3357. $a = atan2(($y2 - $y1), ($x2 - $x1));
  3358. $b = $a + deg2rad($angle);
  3359. $c = $a - deg2rad($angle);
  3360. $x3 = $x2 - ($headsize * cos($b));
  3361. $y3 = $this->h - ($y2 - ($headsize * sin($b)));
  3362. $x4 = $x2 - ($headsize * cos($c));
  3363. $y4 = $this->h - ($y2 - ($headsize * sin($c)));
  3364. $x5 = $x3 - ($x3 - $x4) / 2; // mid point of base of arrowhead - to join arrow line to
  3365. $y5 = $y3 - ($y3 - $y4) / 2;
  3366. $s = '';
  3367. $s.=sprintf('%.3F %.3F m %.3F %.3F l S', $x1 * _MPDFK, ($this->h - $y1) * _MPDFK, $x5 * _MPDFK, $y5 * _MPDFK);
  3368. $this->_out($s);
  3369. $s = '';
  3370. $s.=sprintf('%.3F %.3F m %.3F %.3F l %.3F %.3F l %.3F %.3F l %.3F %.3F l ', $x5 * _MPDFK, $y5 * _MPDFK, $x3 * _MPDFK, $y3 * _MPDFK, $x2 * _MPDFK, ($this->h - $y2) * _MPDFK, $x4 * _MPDFK, $y4 * _MPDFK, $x5 * _MPDFK, $y5 * _MPDFK);
  3371. $s.=$fill;
  3372. $this->_out($s);
  3373. }
  3374. function Rect($x, $y, $w, $h, $style = '')
  3375. {
  3376. //Draw a rectangle
  3377. if ($style == 'F')
  3378. $op = 'f';
  3379. elseif ($style == 'FD' or $style == 'DF')
  3380. $op = 'B';
  3381. else
  3382. $op = 'S';
  3383. $this->_out(sprintf('%.3F %.3F %.3F %.3F re %s', $x * _MPDFK, ($this->h - $y) * _MPDFK, $w * _MPDFK, -$h * _MPDFK, $op));
  3384. }
  3385. function AddFont($family, $style = '')
  3386. {
  3387. if (empty($family)) {
  3388. return;
  3389. }
  3390. $family = strtolower($family);
  3391. $style = strtoupper($style);
  3392. $style = str_replace('U', '', $style);
  3393. if ($style == 'IB')
  3394. $style = 'BI';
  3395. $fontkey = $family . $style;
  3396. // check if the font has been already added
  3397. if (isset($this->fonts[$fontkey])) {
  3398. return;
  3399. }
  3400. /* -- CJK-FONTS -- */
  3401. if (in_array($family, $this->available_CJK_fonts)) {
  3402. if (empty($this->Big5_widths)) {
  3403. require(_MPDF_PATH . 'includes/CJKdata.php');
  3404. }
  3405. $this->AddCJKFont($family); // don't need to add style
  3406. return;
  3407. }
  3408. /* -- END CJK-FONTS -- */
  3409. if ($this->usingCoreFont) {
  3410. throw new MpdfException("mPDF Error - problem with Font management");
  3411. }
  3412. $stylekey = $style;
  3413. if (!$style) {
  3414. $stylekey = 'R';
  3415. }
  3416. if (!isset($this->fontdata[$family][$stylekey]) || !$this->fontdata[$family][$stylekey]) {
  3417. throw new MpdfException('mPDF Error - Font is not supported - ' . $family . ' ' . $style);
  3418. }
  3419. $name = '';
  3420. $originalsize = 0;
  3421. $sip = false;
  3422. $smp = false;
  3423. $useOTL = 0; // mPDF 5.7.1
  3424. $fontmetrics = ''; // mPDF 6
  3425. $haskerninfo = false;
  3426. $haskernGPOS = false;
  3427. $hassmallcapsGSUB = false;
  3428. $BMPselected = false;
  3429. $GSUBScriptLang = array();
  3430. $GSUBFeatures = array();
  3431. $GSUBLookups = array();
  3432. $GPOSScriptLang = array();
  3433. $GPOSFeatures = array();
  3434. $GPOSLookups = array();
  3435. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.mtx.php')) {
  3436. include(_MPDF_TTFONTDATAPATH . $fontkey . '.mtx.php');
  3437. }
  3438. $ttffile = '';
  3439. if (defined('_MPDF_SYSTEM_TTFONTS')) {
  3440. $ttffile = _MPDF_SYSTEM_TTFONTS . $this->fontdata[$family][$stylekey];
  3441. if (!file_exists($ttffile)) {
  3442. $ttffile = '';
  3443. }
  3444. }
  3445. if (!$ttffile) {
  3446. $ttffile = _MPDF_TTFONTPATH . $this->fontdata[$family][$stylekey];
  3447. if (!file_exists($ttffile)) {
  3448. throw new MpdfException("mPDF Error - cannot find TTF TrueType font file - " . $ttffile);
  3449. }
  3450. }
  3451. $ttfstat = stat($ttffile);
  3452. if (isset($this->fontdata[$family]['TTCfontID'][$stylekey])) {
  3453. $TTCfontID = $this->fontdata[$family]['TTCfontID'][$stylekey];
  3454. } else {
  3455. $TTCfontID = 0;
  3456. }
  3457. $BMPonly = false;
  3458. if (in_array($family, $this->BMPonly)) {
  3459. $BMPonly = true;
  3460. }
  3461. $regenerate = false;
  3462. if ($BMPonly && !$BMPselected) {
  3463. $regenerate = true;
  3464. } elseif (!$BMPonly && $BMPselected) {
  3465. $regenerate = true;
  3466. }
  3467. // mPDF 5.7.1
  3468. if (isset($this->fontdata[$family]['useOTL']) && $this->fontdata[$family]['useOTL'] && $useOTL != $this->fontdata[$family]['useOTL']) {
  3469. $regenerate = true;
  3470. $useOTL = $this->fontdata[$family]['useOTL'];
  3471. } elseif ((!isset($this->fontdata[$family]['useOTL']) || !$this->fontdata[$family]['useOTL']) && $useOTL) {
  3472. $regenerate = true;
  3473. $useOTL = 0;
  3474. }
  3475. if (_FONT_DESCRIPTOR != $fontmetrics) {
  3476. $regenerate = true;
  3477. } // mPDF 6
  3478. if (!isset($name) || $originalsize != $ttfstat['size'] || $regenerate) {
  3479. if (!class_exists('TTFontFile', false)) {
  3480. include(_MPDF_PATH . 'classes/ttfontsuni.php');
  3481. }
  3482. $ttf = new TTFontFile();
  3483. $ttf->getMetrics($ttffile, $fontkey, $TTCfontID, $this->debugfonts, $BMPonly, $useOTL); // mPDF 5.7.1
  3484. $cw = $ttf->charWidths;
  3485. $kerninfo = $ttf->kerninfo;
  3486. if ($kerninfo)
  3487. $haskerninfo = true;
  3488. $haskernGPOS = $ttf->haskernGPOS;
  3489. $hassmallcapsGSUB = $ttf->hassmallcapsGSUB;
  3490. $name = preg_replace('/[ ()]/', '', $ttf->fullName);
  3491. $sip = $ttf->sipset;
  3492. $smp = $ttf->smpset;
  3493. // mPDF 6
  3494. $GSUBScriptLang = $ttf->GSUBScriptLang;
  3495. $GSUBFeatures = $ttf->GSUBFeatures;
  3496. $GSUBLookups = $ttf->GSUBLookups;
  3497. $rtlPUAstr = $ttf->rtlPUAstr;
  3498. $GPOSScriptLang = $ttf->GPOSScriptLang;
  3499. $GPOSFeatures = $ttf->GPOSFeatures;
  3500. $GPOSLookups = $ttf->GPOSLookups;
  3501. $glyphIDtoUni = $ttf->glyphIDtoUni;
  3502. $desc = array(
  3503. 'CapHeight' => round($ttf->capHeight),
  3504. 'XHeight' => round($ttf->xHeight),
  3505. 'FontBBox' => '[' . round($ttf->bbox[0]) . " " . round($ttf->bbox[1]) . " " . round($ttf->bbox[2]) . " " . round($ttf->bbox[3]) . ']', /* FontBBox from head table */
  3506. /* 'MaxWidth' => round($ttf->advanceWidthMax), // AdvanceWidthMax from hhea table NB ArialUnicode MS = 31990 ! */
  3507. 'Flags' => $ttf->flags,
  3508. 'Ascent' => round($ttf->ascent),
  3509. 'Descent' => round($ttf->descent),
  3510. 'Leading' => round($ttf->lineGap),
  3511. 'ItalicAngle' => $ttf->italicAngle,
  3512. 'StemV' => round($ttf->stemV),
  3513. 'MissingWidth' => round($ttf->defaultWidth)
  3514. );
  3515. $panose = '';
  3516. if (count($ttf->panose)) {
  3517. $panoseArray = array_merge(array($ttf->sFamilyClass, $ttf->sFamilySubClass), $ttf->panose);
  3518. foreach ($panoseArray as $value)
  3519. $panose .= ' ' . dechex($value);
  3520. }
  3521. $unitsPerEm = round($ttf->unitsPerEm);
  3522. $up = round($ttf->underlinePosition);
  3523. $ut = round($ttf->underlineThickness);
  3524. $strp = round($ttf->strikeoutPosition); // mPDF 6
  3525. $strs = round($ttf->strikeoutSize); // mPDF 6
  3526. $originalsize = $ttfstat['size'] + 0;
  3527. $type = 'TTF';
  3528. //Generate metrics .php file
  3529. $s = '<?php' . "\n";
  3530. $s.='$name=\'' . $name . "';\n";
  3531. $s.='$type=\'' . $type . "';\n";
  3532. $s.='$desc=' . var_export($desc, true) . ";\n";
  3533. $s.='$unitsPerEm=' . $unitsPerEm . ";\n";
  3534. $s.='$up=' . $up . ";\n";
  3535. $s.='$ut=' . $ut . ";\n";
  3536. $s.='$strp=' . $strp . ";\n"; // mPDF 6
  3537. $s.='$strs=' . $strs . ";\n"; // mPDF 6
  3538. $s.='$ttffile=\'' . $ttffile . "';\n";
  3539. $s.='$TTCfontID=\'' . $TTCfontID . "';\n";
  3540. $s.='$originalsize=' . $originalsize . ";\n";
  3541. if ($sip)
  3542. $s.='$sip=true;' . "\n";
  3543. else
  3544. $s.='$sip=false;' . "\n";
  3545. if ($smp)
  3546. $s.='$smp=true;' . "\n";
  3547. else
  3548. $s.='$smp=false;' . "\n";
  3549. if ($BMPonly)
  3550. $s.='$BMPselected=true;' . "\n";
  3551. else
  3552. $s.='$BMPselected=false;' . "\n";
  3553. $s.='$fontkey=\'' . $fontkey . "';\n";
  3554. $s.='$panose=\'' . $panose . "';\n";
  3555. if ($haskerninfo)
  3556. $s.='$haskerninfo=true;' . "\n";
  3557. else
  3558. $s.='$haskerninfo=false;' . "\n";
  3559. if ($haskernGPOS)
  3560. $s.='$haskernGPOS=true;' . "\n";
  3561. else
  3562. $s.='$haskernGPOS=false;' . "\n";
  3563. if ($hassmallcapsGSUB)
  3564. $s.='$hassmallcapsGSUB=true;' . "\n";
  3565. else
  3566. $s.='$hassmallcapsGSUB=false;' . "\n";
  3567. $s.='$fontmetrics=\'' . _FONT_DESCRIPTOR . "';\n"; // mPDF 6
  3568. $s.='// TypoAscender/TypoDescender/TypoLineGap = ' . round($ttf->typoAscender) . ', ' . round($ttf->typoDescender) . ', ' . round($ttf->typoLineGap) . "\n";
  3569. $s.='// usWinAscent/usWinDescent = ' . round($ttf->usWinAscent) . ', ' . round(-$ttf->usWinDescent) . "\n";
  3570. $s.='// hhea Ascent/Descent/LineGap = ' . round($ttf->hheaascent) . ', ' . round($ttf->hheadescent) . ', ' . round($ttf->hhealineGap) . "\n";
  3571. // mPDF 5.7.1
  3572. if (isset($this->fontdata[$family]['useOTL'])) {
  3573. $s.='$useOTL=' . $this->fontdata[$family]['useOTL'] . ';' . "\n";
  3574. } else
  3575. $s.='$useOTL=0x0000;' . "\n";
  3576. if ($rtlPUAstr) {
  3577. $s.='$rtlPUAstr=\'' . $rtlPUAstr . "';\n";
  3578. } else
  3579. $s.='$rtlPUAstr=\'\';' . "\n";
  3580. if (count($GSUBScriptLang)) {
  3581. $s.='$GSUBScriptLang=' . var_export($GSUBScriptLang, true) . ";\n";
  3582. }
  3583. if (count($GSUBFeatures)) {
  3584. $s.='$GSUBFeatures=' . var_export($GSUBFeatures, true) . ";\n";
  3585. }
  3586. if (count($GSUBLookups)) {
  3587. $s.='$GSUBLookups=' . var_export($GSUBLookups, true) . ";\n";
  3588. }
  3589. if (count($GPOSScriptLang)) {
  3590. $s.='$GPOSScriptLang=' . var_export($GPOSScriptLang, true) . ";\n";
  3591. }
  3592. if (count($GPOSFeatures)) {
  3593. $s.='$GPOSFeatures=' . var_export($GPOSFeatures, true) . ";\n";
  3594. }
  3595. if (count($GPOSLookups)) {
  3596. $s.='$GPOSLookups=' . var_export($GPOSLookups, true) . ";\n";
  3597. }
  3598. if ($kerninfo) {
  3599. $s.='$kerninfo=' . var_export($kerninfo, true) . ";\n";
  3600. }
  3601. $s.="?>";
  3602. if (is_writable(dirname(_MPDF_TTFONTDATAPATH . 'x'))) {
  3603. $fh = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.mtx.php', "w");
  3604. fwrite($fh, $s, strlen($s));
  3605. fclose($fh);
  3606. $fh = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.cw.dat', "wb");
  3607. fwrite($fh, $cw, strlen($cw));
  3608. fclose($fh);
  3609. // mPDF 5.7.1
  3610. $fh = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.gid.dat', "wb");
  3611. fwrite($fh, $glyphIDtoUni, strlen($glyphIDtoUni));
  3612. fclose($fh);
  3613. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.cgm'))
  3614. unlink(_MPDF_TTFONTDATAPATH . $fontkey . '.cgm');
  3615. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.z'))
  3616. unlink(_MPDF_TTFONTDATAPATH . $fontkey . '.z');
  3617. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.cw127.php'))
  3618. unlink(_MPDF_TTFONTDATAPATH . $fontkey . '.cw127.php');
  3619. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.cw'))
  3620. unlink(_MPDF_TTFONTDATAPATH . $fontkey . '.cw');
  3621. }
  3622. elseif ($this->debugfonts) {
  3623. throw new MpdfException('Cannot write to the font caching directory - ' . _MPDF_TTFONTDATAPATH);
  3624. }
  3625. unset($ttf);
  3626. } else {
  3627. $cw = '';
  3628. $glyphIDtoUni = '';
  3629. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.cw.dat'))
  3630. $cw = file_get_contents(_MPDF_TTFONTDATAPATH . $fontkey . '.cw.dat');
  3631. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.gid.dat'))
  3632. $glyphIDtoUni = file_get_contents(_MPDF_TTFONTDATAPATH . $fontkey . '.gid.dat');
  3633. }
  3634. /* -- OTL -- */
  3635. // mPDF 5.7.1
  3636. // Use OTL OpenType Table Layout - GSUB
  3637. if (isset($this->fontdata[$family]['useOTL']) && ($this->fontdata[$family]['useOTL'])) {
  3638. if (!class_exists('otl', false)) {
  3639. include(_MPDF_PATH . 'classes/otl.php');
  3640. }
  3641. if (empty($this->otl)) {
  3642. $this->otl = new otl($this);
  3643. }
  3644. }
  3645. /* -- END OTL -- */
  3646. if (isset($this->fontdata[$family]['sip-ext']) && $this->fontdata[$family]['sip-ext']) {
  3647. $sipext = $this->fontdata[$family]['sip-ext'];
  3648. } else {
  3649. $sipext = '';
  3650. }
  3651. // Override with values from config_font.php
  3652. if (isset($this->fontdata[$family]['Ascent']) && $this->fontdata[$family]['Ascent']) {
  3653. $desc['Ascent'] = $this->fontdata[$family]['Ascent'];
  3654. }
  3655. if (isset($this->fontdata[$family]['Descent']) && $this->fontdata[$family]['Descent']) {
  3656. $desc['Descent'] = $this->fontdata[$family]['Descent'];
  3657. }
  3658. if (isset($this->fontdata[$family]['Leading']) && $this->fontdata[$family]['Leading']) {
  3659. $desc['Leading'] = $this->fontdata[$family]['Leading'];
  3660. }
  3661. $i = count($this->fonts) + $this->extraFontSubsets + 1;
  3662. if ($sip || $smp) {
  3663. $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'panose' => $panose, 'unitsPerEm' => $unitsPerEm, 'up' => $up, 'ut' => $ut, 'strs' => $strs, 'strp' => $strp, 'cw' => $cw, 'ttffile' => $ttffile, 'fontkey' => $fontkey, 'subsets' => array(0 => range(0, 127)), 'subsetfontids' => array($i), 'used' => false, 'sip' => $sip, 'sipext' => $sipext, 'smp' => $smp, 'TTCfontID' => $TTCfontID, 'useOTL' => (isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false), 'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false), 'GSUBScriptLang' => $GSUBScriptLang, 'GSUBFeatures' => $GSUBFeatures, 'GSUBLookups' => $GSUBLookups, 'GPOSScriptLang' => $GPOSScriptLang, 'GPOSFeatures' => $GPOSFeatures, 'GPOSLookups' => $GPOSLookups, 'rtlPUAstr' => $rtlPUAstr, 'glyphIDtoUni' => $glyphIDtoUni, 'haskerninfo' => $haskerninfo, 'haskernGPOS' => $haskernGPOS, 'hassmallcapsGSUB' => $hassmallcapsGSUB); // mPDF 5.7.1 // mPDF 6
  3664. } else {
  3665. $ss = array();
  3666. for ($s = 32; $s < 128; $s++) {
  3667. $ss[$s] = $s;
  3668. }
  3669. $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'panose' => $panose, 'unitsPerEm' => $unitsPerEm, 'up' => $up, 'ut' => $ut, 'strs' => $strs, 'strp' => $strp, 'cw' => $cw, 'ttffile' => $ttffile, 'fontkey' => $fontkey, 'subset' => $ss, 'used' => false, 'sip' => $sip, 'sipext' => $sipext, 'smp' => $smp, 'TTCfontID' => $TTCfontID, 'useOTL' => (isset($this->fontdata[$family]['useOTL']) ? $this->fontdata[$family]['useOTL'] : false), 'useKashida' => (isset($this->fontdata[$family]['useKashida']) ? $this->fontdata[$family]['useKashida'] : false), 'GSUBScriptLang' => $GSUBScriptLang, 'GSUBFeatures' => $GSUBFeatures, 'GSUBLookups' => $GSUBLookups, 'GPOSScriptLang' => $GPOSScriptLang, 'GPOSFeatures' => $GPOSFeatures, 'GPOSLookups' => $GPOSLookups, 'rtlPUAstr' => $rtlPUAstr, 'glyphIDtoUni' => $glyphIDtoUni, 'haskerninfo' => $haskerninfo, 'haskernGPOS' => $haskernGPOS, 'hassmallcapsGSUB' => $hassmallcapsGSUB); // mPDF 5.7.1 // mPDF 6
  3670. }
  3671. if ($haskerninfo) {
  3672. $this->fonts[$fontkey]['kerninfo'] = $kerninfo;
  3673. }
  3674. $this->FontFiles[$fontkey] = array('length1' => $originalsize, 'type' => "TTF", 'ttffile' => $ttffile, 'sip' => $sip, 'smp' => $smp);
  3675. unset($cw);
  3676. }
  3677. function SetFont($family, $style = '', $size = 0, $write = true, $forcewrite = false)
  3678. {
  3679. $family = strtolower($family);
  3680. if (!$this->onlyCoreFonts) {
  3681. if ($family == 'sans' || $family == 'sans-serif') {
  3682. $family = $this->sans_fonts[0];
  3683. }
  3684. if ($family == 'serif') {
  3685. $family = $this->serif_fonts[0];
  3686. }
  3687. if ($family == 'mono' || $family == 'monospace') {
  3688. $family = $this->mono_fonts[0];
  3689. }
  3690. }
  3691. if (isset($this->fonttrans[$family]) && $this->fonttrans[$family]) {
  3692. $family = $this->fonttrans[$family];
  3693. }
  3694. if ($family == '') {
  3695. if ($this->FontFamily) {
  3696. $family = $this->FontFamily;
  3697. } elseif ($this->default_font) {
  3698. $family = $this->default_font;
  3699. } else {
  3700. throw new MpdfException("No font or default font set!");
  3701. }
  3702. }
  3703. $this->ReqFontStyle = $style; // required or requested style - used later for artificial bold/italic
  3704. if (($family == 'csymbol') || ($family == 'czapfdingbats') || ($family == 'ctimes') || ($family == 'ccourier') || ($family == 'chelvetica')) {
  3705. if ($this->PDFA || $this->PDFX) {
  3706. if ($family == 'csymbol' || $family == 'czapfdingbats') {
  3707. throw new MpdfException("Symbol and Zapfdingbats cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a).");
  3708. }
  3709. if ($family == 'ctimes' || $family == 'ccourier' || $family == 'chelvetica') {
  3710. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  3711. $this->PDFAXwarnings[] = "Core Adobe font " . ucfirst($family) . " cannot be embedded in mPDF, which is required for PDFA1-b or PDFX/1-a. (Embedded font will be substituted.)";
  3712. }
  3713. if ($family == 'chelvetica') {
  3714. $family = 'sans';
  3715. }
  3716. if ($family == 'ctimes') {
  3717. $family = 'serif';
  3718. }
  3719. if ($family == 'ccourier') {
  3720. $family = 'mono';
  3721. }
  3722. }
  3723. $this->usingCoreFont = false;
  3724. } else {
  3725. $this->usingCoreFont = true;
  3726. }
  3727. if ($family == 'csymbol' || $family == 'czapfdingbats') {
  3728. $style = '';
  3729. }
  3730. } else {
  3731. $this->usingCoreFont = false;
  3732. }
  3733. // mPDF 5.7.1
  3734. if ($style) {
  3735. $style = strtoupper($style);
  3736. if ($style == 'IB')
  3737. $style = 'BI';
  3738. }
  3739. if ($size == 0)
  3740. $size = $this->FontSizePt;
  3741. $fontkey = $family . $style;
  3742. $stylekey = $style;
  3743. if (!$stylekey) {
  3744. $stylekey = "R";
  3745. }
  3746. if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
  3747. if (!isset($this->fonts[$fontkey]) || count($this->default_available_fonts) != count($this->available_unifonts)) { // not already added
  3748. /* -- CJK-FONTS -- */
  3749. // CJK fonts
  3750. if (in_array($fontkey, $this->available_CJK_fonts)) {
  3751. if (!isset($this->fonts[$fontkey])) { // already added
  3752. if (empty($this->Big5_widths)) {
  3753. require(_MPDF_PATH . 'includes/CJKdata.php');
  3754. }
  3755. $this->AddCJKFont($family); // don't need to add style
  3756. }
  3757. }
  3758. // Test to see if requested font/style is available - or substitute
  3759. else
  3760. /* -- END CJK-FONTS -- */
  3761. if (!in_array($fontkey, $this->available_unifonts)) {
  3762. // If font[nostyle] exists - set it
  3763. if (in_array($family, $this->available_unifonts)) {
  3764. $style = '';
  3765. }
  3766. // elseif only one font available - set it (assumes if only one font available it will not have a style)
  3767. elseif (count($this->available_unifonts) == 1) {
  3768. $family = $this->available_unifonts[0];
  3769. $style = '';
  3770. } else {
  3771. $found = 0;
  3772. // else substitute font of similar type
  3773. if (in_array($family, $this->sans_fonts)) {
  3774. $i = array_intersect($this->sans_fonts, $this->available_unifonts);
  3775. if (count($i)) {
  3776. $i = array_values($i);
  3777. // with requested style if possible
  3778. if (!in_array(($i[0] . $style), $this->available_unifonts)) {
  3779. $style = '';
  3780. }
  3781. $family = $i[0];
  3782. $found = 1;
  3783. }
  3784. } elseif (in_array($family, $this->serif_fonts)) {
  3785. $i = array_intersect($this->serif_fonts, $this->available_unifonts);
  3786. if (count($i)) {
  3787. $i = array_values($i);
  3788. // with requested style if possible
  3789. if (!in_array(($i[0] . $style), $this->available_unifonts)) {
  3790. $style = '';
  3791. }
  3792. $family = $i[0];
  3793. $found = 1;
  3794. }
  3795. } elseif (in_array($family, $this->mono_fonts)) {
  3796. $i = array_intersect($this->mono_fonts, $this->available_unifonts);
  3797. if (count($i)) {
  3798. $i = array_values($i);
  3799. // with requested style if possible
  3800. if (!in_array(($i[0] . $style), $this->available_unifonts)) {
  3801. $style = '';
  3802. }
  3803. $family = $i[0];
  3804. $found = 1;
  3805. }
  3806. }
  3807. if (!$found) {
  3808. // set first available font
  3809. $fs = $this->available_unifonts[0];
  3810. preg_match('/^([a-z_0-9\-]+)([BI]{0,2})$/', $fs, $fas); // Allow "-"
  3811. // with requested style if possible
  3812. $ws = $fas[1] . $style;
  3813. if (in_array($ws, $this->available_unifonts)) {
  3814. $family = $fas[1]; // leave $style as is
  3815. } elseif (in_array($fas[1], $this->available_unifonts)) {
  3816. // or without style
  3817. $family = $fas[1];
  3818. $style = '';
  3819. } else {
  3820. // or with the style specified
  3821. $family = $fas[1];
  3822. $style = $fas[2];
  3823. }
  3824. }
  3825. }
  3826. $fontkey = $family . $style;
  3827. }
  3828. }
  3829. // try to add font (if not already added)
  3830. $this->AddFont($family, $style);
  3831. //Test if font is already selected
  3832. if ($this->FontFamily == $family && $this->FontFamily == $this->currentfontfamily && $this->FontStyle == $style && $this->FontStyle == $this->currentfontstyle && $this->FontSizePt == $size && $this->FontSizePt == $this->currentfontsize && !$forcewrite) {
  3833. return $family;
  3834. }
  3835. $fontkey = $family . $style;
  3836. //Select it
  3837. $this->FontFamily = $family;
  3838. $this->FontStyle = $style;
  3839. $this->FontSizePt = $size;
  3840. $this->FontSize = $size / _MPDFK;
  3841. $this->CurrentFont = &$this->fonts[$fontkey];
  3842. if ($write) {
  3843. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  3844. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  3845. $this->_out($fontout);
  3846. }
  3847. $this->pageoutput[$this->page]['Font'] = $fontout;
  3848. }
  3849. // Added - currentfont (lowercase) used in HTML2PDF
  3850. $this->currentfontfamily = $family;
  3851. $this->currentfontsize = $size;
  3852. $this->currentfontstyle = $style;
  3853. $this->setMBencoding('UTF-8');
  3854. } else { // if using core fonts
  3855. if ($this->PDFA || $this->PDFX) {
  3856. throw new MpdfException('Core Adobe fonts cannot be embedded in mPDF (required for PDFA1-b or PDFX/1-a) - cannot use option to use core fonts.');
  3857. }
  3858. $this->setMBencoding('windows-1252');
  3859. //Test if font is already selected
  3860. if (($this->FontFamily == $family) AND ( $this->FontStyle == $style) AND ( $this->FontSizePt == $size) && !$forcewrite) {
  3861. return $family;
  3862. }
  3863. if (!isset($this->CoreFonts[$fontkey])) {
  3864. if (in_array($family, $this->serif_fonts)) {
  3865. $family = 'ctimes';
  3866. } elseif (in_array($family, $this->mono_fonts)) {
  3867. $family = 'ccourier';
  3868. } else {
  3869. $family = 'chelvetica';
  3870. }
  3871. $this->usingCoreFont = true;
  3872. $fontkey = $family . $style;
  3873. }
  3874. if (!isset($this->fonts[$fontkey])) {
  3875. // STANDARD CORE FONTS
  3876. if (isset($this->CoreFonts[$fontkey])) {
  3877. //Load metric file
  3878. $file = $family;
  3879. if ($family == 'ctimes' || $family == 'chelvetica' || $family == 'ccourier') {
  3880. $file.=strtolower($style);
  3881. }
  3882. $file.='.php';
  3883. include(_MPDF_PATH . 'font/' . $file);
  3884. if (!isset($cw)) {
  3885. throw new MpdfException('Could not include font metric file');
  3886. }
  3887. $i = count($this->fonts) + $this->extraFontSubsets + 1;
  3888. $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw);
  3889. if ($this->useKerning && isset($kerninfo)) {
  3890. $this->fonts[$fontkey]['kerninfo'] = $kerninfo;
  3891. }
  3892. } else {
  3893. throw new MpdfException('mPDF error - Font not defined');
  3894. }
  3895. }
  3896. //Test if font is already selected
  3897. if (($this->FontFamily == $family) AND ( $this->FontStyle == $style) AND ( $this->FontSizePt == $size) && !$forcewrite) {
  3898. return $family;
  3899. }
  3900. //Select it
  3901. $this->FontFamily = $family;
  3902. $this->FontStyle = $style;
  3903. $this->FontSizePt = $size;
  3904. $this->FontSize = $size / _MPDFK;
  3905. $this->CurrentFont = &$this->fonts[$fontkey];
  3906. if ($write) {
  3907. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  3908. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  3909. $this->_out($fontout);
  3910. }
  3911. $this->pageoutput[$this->page]['Font'] = $fontout;
  3912. }
  3913. // Added - currentfont (lowercase) used in HTML2PDF
  3914. $this->currentfontfamily = $family;
  3915. $this->currentfontsize = $size;
  3916. $this->currentfontstyle = $style;
  3917. }
  3918. return $family;
  3919. }
  3920. function SetFontSize($size, $write = true)
  3921. {
  3922. //Set font size in points
  3923. if ($this->FontSizePt == $size)
  3924. return;
  3925. $this->FontSizePt = $size;
  3926. $this->FontSize = $size / _MPDFK;
  3927. $this->currentfontsize = $size;
  3928. if ($write) {
  3929. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  3930. // Edited mPDF 3.0
  3931. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  3932. $this->_out($fontout);
  3933. }
  3934. $this->pageoutput[$this->page]['Font'] = $fontout;
  3935. }
  3936. }
  3937. function AddLink()
  3938. {
  3939. //Create a new internal link
  3940. $n = count($this->links) + 1;
  3941. $this->links[$n] = array(0, 0);
  3942. return $n;
  3943. }
  3944. function SetLink($link, $y = 0, $page = -1)
  3945. {
  3946. //Set destination of internal link
  3947. if ($y == -1)
  3948. $y = $this->y;
  3949. if ($page == -1)
  3950. $page = $this->page;
  3951. $this->links[$link] = array($page, $y);
  3952. }
  3953. function Link($x, $y, $w, $h, $link)
  3954. {
  3955. $l = array($x * _MPDFK, $this->hPt - $y * _MPDFK, $w * _MPDFK, $h * _MPDFK, $link);
  3956. if ($this->keep_block_together) { // don't write yet
  3957. return;
  3958. } elseif ($this->table_rotate) { // *TABLES*
  3959. $this->tbrot_Links[$this->page][] = $l; // *TABLES*
  3960. return; // *TABLES*
  3961. } // *TABLES*
  3962. elseif ($this->kwt) {
  3963. $this->kwt_Links[$this->page][] = $l;
  3964. return;
  3965. }
  3966. if ($this->writingHTMLheader || $this->writingHTMLfooter) {
  3967. $this->HTMLheaderPageLinks[] = $l;
  3968. return;
  3969. }
  3970. //Put a link on the page
  3971. $this->PageLinks[$this->page][] = $l;
  3972. // Save cross-reference to Column buffer
  3973. $ref = count($this->PageLinks[$this->page]) - 1; // *COLUMNS*
  3974. $this->columnLinks[$this->CurrCol][INTVAL($this->x)][INTVAL($this->y)] = $ref; // *COLUMNS*
  3975. }
  3976. function Text($x, $y, $txt, $OTLdata = array(), $textvar = 0, $aixextra = '', $coordsys = '', $return = false)
  3977. {
  3978. // Output (or return) a string
  3979. // Called (internally) by Watermark() & _tableWrite() [rotated cells] & TableHeaderFooter() & WriteText()
  3980. // Called also from classes/svg.php
  3981. // Expects Font to be set
  3982. // Expects input to be mb_encoded if necessary and RTL reversed & OTL processed
  3983. // ARTIFICIAL BOLD AND ITALIC
  3984. $s = 'q ';
  3985. if ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false) {
  3986. $s .= '2 Tr 1 J 1 j ';
  3987. $s .= sprintf('%.3F w ', ($this->FontSize / 130) * _MPDFK * $this->falseBoldWeight);
  3988. $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  3989. if ($this->FillColor != $tc) {
  3990. $s .= $tc . ' ';
  3991. } // stroke (outline) = same colour as text(fill)
  3992. }
  3993. if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) {
  3994. $aix = '1 0 0.261799 1 %.3F %.3F Tm';
  3995. } else {
  3996. $aix = '%.3F %.3F Td';
  3997. }
  3998. $aix = $aixextra . $aix;
  3999. if ($this->ColorFlag)
  4000. $s.=$this->TextColor . ' ';
  4001. $this->CurrentFont['used'] = true;
  4002. if ($this->usingCoreFont) {
  4003. $txt2 = str_replace(chr(160), chr(32), $txt);
  4004. } else {
  4005. $txt2 = str_replace(chr(194) . chr(160), chr(32), $txt);
  4006. }
  4007. $px = $x;
  4008. $py = $y;
  4009. if ($coordsys != 'SVG') {
  4010. $px = $x * _MPDFK;
  4011. $py = ($this->h - $y) * _MPDFK;
  4012. }
  4013. /* * ************** SIMILAR TO Cell() ************************ */
  4014. // IF corefonts AND NOT SmCaps AND NOT Kerning
  4015. // Just output text
  4016. if ($this->usingCoreFont && !($textvar & FC_SMALLCAPS) && !($textvar & FC_KERNING)) {
  4017. $txt2 = $this->_escape($txt2);
  4018. $s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  4019. }
  4020. // IF NOT corefonts [AND NO wordspacing] AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  4021. // Just output text
  4022. elseif (!$this->usingCoreFont && !($textvar & FC_SMALLCAPS) && !($textvar & FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
  4023. // IF SIP/SMP
  4024. if ($this->CurrentFont['sip'] || $this->CurrentFont['smp']) {
  4025. $txt2 = $this->UTF8toSubset($txt2);
  4026. $s .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
  4027. }
  4028. // NOT SIP/SMP
  4029. else {
  4030. $txt2 = $this->UTF8ToUTF16BE($txt2, false);
  4031. $txt2 = $this->_escape($txt2);
  4032. $s .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  4033. }
  4034. }
  4035. // IF NOT corefonts [AND IS wordspacing] AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  4036. // Not required here (cf. Cell() )
  4037. // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
  4038. else {
  4039. $s .= $this->applyGPOSpdf($txt2, $aix, $px, $py, $OTLdata, $textvar);
  4040. }
  4041. /* * ************** END ************************ */
  4042. $s .= ' ';
  4043. if (($textvar & FD_UNDERLINE) && $txt != '') { // mPDF 5.7.1
  4044. $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  4045. if ($this->FillColor != $c) {
  4046. $s.= ' ' . $c . ' ';
  4047. }
  4048. if (isset($this->CurrentFont['up']) && $this->CurrentFont['up']) {
  4049. $up = $this->CurrentFont['up'];
  4050. } else {
  4051. $up = -100;
  4052. }
  4053. $adjusty = (-$up / 1000 * $this->FontSize);
  4054. if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
  4055. $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
  4056. } else {
  4057. $ut = 60 / 1000 * $this->FontSize;
  4058. }
  4059. $olw = $this->LineWidth;
  4060. $s.=' ' . (sprintf(' %.3F w', $ut * _MPDFK));
  4061. $s.=' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
  4062. $s.=' ' . (sprintf(' %.3F w', $olw * _MPDFK));
  4063. if ($this->FillColor != $c) {
  4064. $s.= ' ' . $this->FillColor . ' ';
  4065. }
  4066. }
  4067. // STRIKETHROUGH
  4068. if (($textvar & FD_LINETHROUGH) && $txt != '') { // mPDF 5.7.1
  4069. $c = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  4070. if ($this->FillColor != $c) {
  4071. $s.= ' ' . $c . ' ';
  4072. }
  4073. //Superscript and Subscript Y coordinate adjustment (now for striked-through texts)
  4074. if (isset($this->CurrentFont['desc']['CapHeight']) && $this->CurrentFont['desc']['CapHeight']) {
  4075. $ch = $this->CurrentFont['desc']['CapHeight'];
  4076. } else {
  4077. $ch = 700;
  4078. }
  4079. $adjusty = (-$ch / 1000 * $this->FontSize) * 0.35;
  4080. if (isset($this->CurrentFont['ut']) && $this->CurrentFont['ut']) {
  4081. $ut = $this->CurrentFont['ut'] / 1000 * $this->FontSize;
  4082. } else {
  4083. $ut = 60 / 1000 * $this->FontSize;
  4084. }
  4085. $olw = $this->LineWidth;
  4086. $s.=' ' . (sprintf(' %.3F w', $ut * _MPDFK));
  4087. $s.=' ' . $this->_dounderline($x, $y + $adjusty, $txt, $OTLdata, $textvar);
  4088. $s.=' ' . (sprintf(' %.3F w', $olw * _MPDFK));
  4089. if ($this->FillColor != $c) {
  4090. $s.= ' ' . $this->FillColor . ' ';
  4091. }
  4092. }
  4093. $s .= 'Q';
  4094. if ($return) {
  4095. return $s . " \n";
  4096. }
  4097. $this->_out($s);
  4098. }
  4099. /* -- DIRECTW -- */
  4100. function WriteText($x, $y, $txt)
  4101. {
  4102. // Output a string using Text() but does encoding and text reversing of RTL
  4103. $txt = $this->purify_utf8_text($txt);
  4104. if ($this->text_input_as_HTML) {
  4105. $txt = $this->all_entities_to_utf8($txt);
  4106. }
  4107. if ($this->usingCoreFont) {
  4108. $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
  4109. }
  4110. // DIRECTIONALITY
  4111. if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
  4112. $this->biDirectional = true;
  4113. } // *OTL*
  4114. $textvar = 0;
  4115. $save_OTLtags = $this->OTLtags;
  4116. $this->OTLtags = array();
  4117. if ($this->useKerning) {
  4118. if ($this->CurrentFont['haskernGPOS']) {
  4119. $this->OTLtags['Plus'] .= ' kern';
  4120. } else {
  4121. $textvar = ($textvar | FC_KERNING);
  4122. }
  4123. }
  4124. /* -- OTL -- */
  4125. // Use OTL OpenType Table Layout - GSUB & GPOS
  4126. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  4127. $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
  4128. $OTLdata = $this->otl->OTLdata;
  4129. }
  4130. /* -- END OTL -- */
  4131. $this->OTLtags = $save_OTLtags;
  4132. $this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
  4133. $this->Text($x, $y, $txt, $OTLdata, $textvar);
  4134. }
  4135. function WriteCell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0)
  4136. {
  4137. //Output a cell using Cell() but does encoding and text reversing of RTL
  4138. $txt = $this->purify_utf8_text($txt);
  4139. if ($this->text_input_as_HTML) {
  4140. $txt = $this->all_entities_to_utf8($txt);
  4141. }
  4142. if ($this->usingCoreFont) {
  4143. $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
  4144. }
  4145. // DIRECTIONALITY
  4146. if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
  4147. $this->biDirectional = true;
  4148. } // *OTL*
  4149. $textvar = 0;
  4150. $save_OTLtags = $this->OTLtags;
  4151. $this->OTLtags = array();
  4152. if ($this->useKerning) {
  4153. if ($this->CurrentFont['haskernGPOS']) {
  4154. $this->OTLtags['Plus'] .= ' kern';
  4155. } else {
  4156. $textvar = ($textvar | FC_KERNING);
  4157. }
  4158. }
  4159. /* -- OTL -- */
  4160. // Use OTL OpenType Table Layout - GSUB & GPOS
  4161. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  4162. $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
  4163. $OTLdata = $this->otl->OTLdata;
  4164. }
  4165. /* -- END OTL -- */
  4166. $this->OTLtags = $save_OTLtags;
  4167. $this->magic_reverse_dir($txt, $this->directionality, $OTLdata);
  4168. $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link, $currentx, 0, 0, 'M', 0, false, $OTLdata, $textvar);
  4169. }
  4170. /* -- END DIRECTW -- */
  4171. function ResetSpacing()
  4172. {
  4173. if ($this->ws != 0) {
  4174. $this->_out('BT 0 Tw ET');
  4175. }
  4176. $this->ws = 0;
  4177. if ($this->charspacing != 0) {
  4178. $this->_out('BT 0 Tc ET');
  4179. }
  4180. $this->charspacing = 0;
  4181. }
  4182. function SetSpacing($cs, $ws)
  4183. {
  4184. if (intval($cs * 1000) == 0) {
  4185. $cs = 0;
  4186. }
  4187. if ($cs) {
  4188. $this->_out(sprintf('BT %.3F Tc ET', $cs));
  4189. } elseif ($this->charspacing != 0) {
  4190. $this->_out('BT 0 Tc ET');
  4191. }
  4192. $this->charspacing = $cs;
  4193. if (intval($ws * 1000) == 0) {
  4194. $ws = 0;
  4195. }
  4196. if ($ws) {
  4197. $this->_out(sprintf('BT %.3F Tw ET', $ws));
  4198. } elseif ($this->ws != 0) {
  4199. $this->_out('BT 0 Tw ET');
  4200. }
  4201. $this->ws = $ws;
  4202. }
  4203. // WORD SPACING
  4204. function GetJspacing($nc, $ns, $w, $inclCursive, &$cOTLdata)
  4205. {
  4206. $kashida_present = false;
  4207. $kashida_space = 0;
  4208. if ($w > 0 && $inclCursive && isset($this->CurrentFont['useKashida']) && $this->CurrentFont['useKashida'] && !empty($cOTLdata)) {
  4209. for ($c = 0; $c < count($cOTLdata); $c++) {
  4210. for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
  4211. if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
  4212. $kashida_present = true;
  4213. break 2;
  4214. }
  4215. }
  4216. }
  4217. }
  4218. if ($kashida_present) {
  4219. $k_ctr = 0; // Number of kashida points
  4220. $k_total = 0; // Total of kashida values (priority)
  4221. // Reset word
  4222. $max_kashida_in_word = 0;
  4223. $last_kashida_in_word = -1;
  4224. for ($c = 0; $c < count($cOTLdata); $c++) {
  4225. for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
  4226. if ($cOTLdata[$c]['group']{$i} == 'S') {
  4227. // Save from last word
  4228. if ($max_kashida_in_word) {
  4229. $k_ctr++;
  4230. $k_total = $max_kashida_in_word;
  4231. }
  4232. // Reset word
  4233. $max_kashida_in_word = 0;
  4234. $last_kashida_in_word = -1;
  4235. }
  4236. if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
  4237. if ($max_kashida_in_word) {
  4238. if ($cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > $max_kashida_in_word) {
  4239. $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
  4240. $cOTLdata[$c]['GPOSinfo'][$last_kashida_in_word]['kashida'] = 0;
  4241. $last_kashida_in_word = $i;
  4242. } else {
  4243. $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] = 0;
  4244. }
  4245. } else {
  4246. $max_kashida_in_word = $cOTLdata[$c]['GPOSinfo'][$i]['kashida'];
  4247. $last_kashida_in_word = $i;
  4248. }
  4249. }
  4250. }
  4251. }
  4252. // Save from last word
  4253. if ($max_kashida_in_word) {
  4254. $k_ctr++;
  4255. $k_total = $max_kashida_in_word;
  4256. }
  4257. // Number of kashida points = $k_ctr
  4258. // $useKashida is a % value from CurrentFont/config_fonts.php
  4259. // % ratio divided between word-spacing and kashida-spacing
  4260. $kashida_space_ratio = intval($this->CurrentFont['useKashida']) / 100;
  4261. $kashida_space = $w * $kashida_space_ratio;
  4262. $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
  4263. // Only use kashida if each allocated kashida width is > 0.01 x width of a tatweel
  4264. // Otherwise fontstretch is too small and errors
  4265. // If not just leave to adjust word-spacing
  4266. if ($tatw && (($kashida_space / $k_ctr) / $tatw) > 0.01) {
  4267. for ($c = 0; $c < count($cOTLdata); $c++) {
  4268. for ($i = 0; $i < strlen($cOTLdata[$c]['group']); $i++) {
  4269. if (isset($cOTLdata[$c]['GPOSinfo'][$i]['kashida']) && $cOTLdata[$c]['GPOSinfo'][$i]['kashida'] > 0) {
  4270. // At this point kashida is a number representing priority (higher number - higher priority)
  4271. // We are now going to set it as an actual length
  4272. // This shares it equally amongst words:
  4273. $cOTLdata[$c]['GPOSinfo'][$i]['kashida_space'] = (1 / $k_ctr) * $kashida_space;
  4274. }
  4275. }
  4276. }
  4277. $w -= $kashida_space;
  4278. }
  4279. }
  4280. $ws = 0;
  4281. $charspacing = 0;
  4282. $ww = $this->jSWord;
  4283. $ncx = $nc - 1;
  4284. if ($nc == 0) {
  4285. return array(0, 0, 0);
  4286. }
  4287. // Only word spacing allowed / possible
  4288. elseif ($this->fixedlSpacing !== false || $inclCursive) {
  4289. if ($ns) {
  4290. $ws = $w / $ns;
  4291. }
  4292. } elseif ($nc == 1) {
  4293. $charspacing = $w;
  4294. } elseif (!$ns) {
  4295. $charspacing = $w / ($ncx );
  4296. if (($this->jSmaxChar > 0) && ($charspacing > $this->jSmaxChar)) {
  4297. $charspacing = $this->jSmaxChar;
  4298. }
  4299. } elseif ($ns == ($ncx )) {
  4300. $charspacing = $w / $ns;
  4301. } else {
  4302. if ($this->usingCoreFont) {
  4303. $cs = ($w * (1 - $this->jSWord)) / ($ncx );
  4304. if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
  4305. $cs = $this->jSmaxChar;
  4306. $ww = 1 - (($cs * ($ncx )) / $w);
  4307. }
  4308. $charspacing = $cs;
  4309. $ws = ($w * ($ww) ) / $ns;
  4310. } else {
  4311. $cs = ($w * (1 - $this->jSWord)) / ($ncx - $ns);
  4312. if (($this->jSmaxChar > 0) && ($cs > $this->jSmaxChar)) {
  4313. $cs = $this->jSmaxChar;
  4314. $ww = 1 - (($cs * ($ncx - $ns)) / $w);
  4315. }
  4316. $charspacing = $cs;
  4317. $ws = (($w * ($ww) ) / $ns) - $charspacing;
  4318. }
  4319. }
  4320. return array($charspacing, $ws, $kashida_space);
  4321. }
  4322. function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '', $currentx = 0, $lcpaddingL = 0, $lcpaddingR = 0, $valign = 'M', $spanfill = 0, $exactWidth = false, $OTLdata = false, $textvar = 0, $lineBox = false)
  4323. { // mPDF 5.7.1
  4324. //Output a cell
  4325. // Expects input to be mb_encoded if necessary and RTL reversed
  4326. // NON_BREAKING SPACE
  4327. if ($this->usingCoreFont) {
  4328. $txt = str_replace(chr(160), chr(32), $txt);
  4329. } else {
  4330. $txt = str_replace(chr(194) . chr(160), chr(32), $txt);
  4331. }
  4332. $oldcolumn = $this->CurrCol;
  4333. // Automatic page break
  4334. // Allows PAGE-BREAK-AFTER = avoid to work
  4335. if (isset($this->blk[$this->blklvl])) {
  4336. $bottom = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['margin_bottom'];
  4337. } else {
  4338. $bottom = 0;
  4339. }
  4340. if (!$this->tableLevel && (($this->y + $this->divheight > $this->PageBreakTrigger) || ($this->y + $h > $this->PageBreakTrigger) ||
  4341. ($this->y + ($h * 2) + $bottom > $this->PageBreakTrigger && $this->blk[$this->blklvl]['page_break_after_avoid'])) and ! $this->InFooter and $this->AcceptPageBreak()) { // mPDF 5.7.2
  4342. $x = $this->x; //Current X position
  4343. // WORD SPACING
  4344. $ws = $this->ws; //Word Spacing
  4345. $charspacing = $this->charspacing; //Character Spacing
  4346. $this->ResetSpacing();
  4347. $this->AddPage($this->CurOrientation);
  4348. // Added to correct for OddEven Margins
  4349. $x += $this->MarginCorrection;
  4350. if ($currentx) {
  4351. $currentx += $this->MarginCorrection;
  4352. }
  4353. $this->x = $x;
  4354. // WORD SPACING
  4355. $this->SetSpacing($charspacing, $ws);
  4356. }
  4357. // Test: to put line through centre of cell: $this->Line($this->x,$this->y+($h/2),$this->x+50,$this->y+($h/2));
  4358. // Test: to put border around cell as it is specified: $border='LRTB';
  4359. /* -- COLUMNS -- */
  4360. // COLS
  4361. // COLUMN CHANGE
  4362. if ($this->CurrCol != $oldcolumn) {
  4363. if ($currentx) {
  4364. $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  4365. }
  4366. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  4367. }
  4368. // COLUMNS Update/overwrite the lowest bottom of printing y value for a column
  4369. if ($this->ColActive) {
  4370. if ($h) {
  4371. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
  4372. } else {
  4373. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $this->divheight;
  4374. }
  4375. }
  4376. /* -- END COLUMNS -- */
  4377. if ($w == 0)
  4378. $w = $this->w - $this->rMargin - $this->x;
  4379. $s = '';
  4380. if ($fill == 1 && $this->FillColor) {
  4381. if ((isset($this->pageoutput[$this->page]['FillColor']) && $this->pageoutput[$this->page]['FillColor'] != $this->FillColor) || !isset($this->pageoutput[$this->page]['FillColor'])) {
  4382. $s .= $this->FillColor . ' ';
  4383. }
  4384. $this->pageoutput[$this->page]['FillColor'] = $this->FillColor;
  4385. }
  4386. if ($lineBox && isset($lineBox['boxtop']) && $txt) { // i.e. always from WriteFlowingBlock/finishFlowingBlock (but not objects -
  4387. // which only have $lineBox['top'] set)
  4388. $boxtop = $this->y + $lineBox['boxtop'];
  4389. $boxbottom = $this->y + $lineBox['boxbottom'];
  4390. $glyphYorigin = $lineBox['glyphYorigin'];
  4391. $baseline_shift = $lineBox['baseline-shift'];
  4392. $bord_boxtop = $bg_boxtop = $boxtop = $boxtop - $baseline_shift;
  4393. $bord_boxbottom = $bg_boxbottom = $boxbottom = $boxbottom - $baseline_shift;
  4394. $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
  4395. // If inline element BACKGROUND has bounding box set by parent element:
  4396. if (isset($lineBox['background-boxtop'])) {
  4397. $bg_boxtop = $this->y + $lineBox['background-boxtop'] - $lineBox['background-baseline-shift'];
  4398. $bg_boxbottom = $this->y + $lineBox['background-boxbottom'] - $lineBox['background-baseline-shift'];
  4399. $bg_boxheight = $bg_boxbottom - $bg_boxtop;
  4400. }
  4401. // If inline element BORDER has bounding box set by parent element:
  4402. if (isset($lineBox['border-boxtop'])) {
  4403. $bord_boxtop = $this->y + $lineBox['border-boxtop'] - $lineBox['border-baseline-shift'];
  4404. $bord_boxbottom = $this->y + $lineBox['border-boxbottom'] - $lineBox['border-baseline-shift'];
  4405. $bord_boxheight = $bord_boxbottom - $bord_boxtop;
  4406. }
  4407. } else {
  4408. $boxtop = $this->y;
  4409. $boxheight = $h;
  4410. $boxbottom = $this->y + $h;
  4411. $baseline_shift = 0;
  4412. if ($txt != '') {
  4413. // FONT SIZE - this determines the baseline caculation
  4414. $bfs = $this->FontSize;
  4415. //Calculate baseline Superscript and Subscript Y coordinate adjustment
  4416. $bfx = $this->baselineC;
  4417. $baseline = $bfx * $bfs;
  4418. if ($textvar & FA_SUPERSCRIPT) {
  4419. $baseline_shift = $this->textparam['text-baseline'];
  4420. } // mPDF 5.7.1 // mPDF 6
  4421. elseif ($textvar & FA_SUBSCRIPT) {
  4422. $baseline_shift = $this->textparam['text-baseline'];
  4423. } // mPDF 5.7.1 // mPDF 6
  4424. elseif ($this->bullet) {
  4425. $baseline += ($bfx - 0.7) * $this->FontSize;
  4426. }
  4427. // Vertical align (for Images)
  4428. if ($valign == 'T') {
  4429. $va = (0.5 * $bfs * $this->normalLineheight);
  4430. } elseif ($valign == 'B') {
  4431. $va = $h - (0.5 * $bfs * $this->normalLineheight);
  4432. } else {
  4433. $va = 0.5 * $h;
  4434. } // Middle
  4435. // ONLY SET THESE IF WANT TO CONFINE BORDER +/- FILL TO FIT FONTSIZE - NOT FULL CELL AS IS ORIGINAL FUNCTION
  4436. // spanfill or spanborder are set in FlowingBlock functions
  4437. if ($spanfill || !empty($this->spanborddet) || $link != '') {
  4438. $exth = 0.2; // Add to fontsize to increase height of background / link / border
  4439. $boxtop = $this->y + $baseline + $va - ($this->FontSize * (1 + $exth / 2) * (0.5 + $bfx));
  4440. $boxheight = $this->FontSize * (1 + $exth);
  4441. $boxbottom = $boxtop + $boxheight;
  4442. }
  4443. $glyphYorigin = $baseline + $va;
  4444. }
  4445. $boxtop -= $baseline_shift;
  4446. $boxbottom -= $baseline_shift;
  4447. $bord_boxtop = $bg_boxtop = $boxtop;
  4448. $bord_boxbottom = $bg_boxbottom = $boxbottom;
  4449. $bord_boxheight = $bg_boxheight = $boxheight = $boxbottom - $boxtop;
  4450. }
  4451. $bbw = $tbw = $lbw = $rbw = 0; // Border widths
  4452. if (!empty($this->spanborddet)) {
  4453. if (!isset($this->spanborddet['B'])) {
  4454. $this->spanborddet['B'] = array('s' => 0, 'style' => '', 'w' => 0);
  4455. }
  4456. if (!isset($this->spanborddet['T'])) {
  4457. $this->spanborddet['T'] = array('s' => 0, 'style' => '', 'w' => 0);
  4458. }
  4459. if (!isset($this->spanborddet['L'])) {
  4460. $this->spanborddet['L'] = array('s' => 0, 'style' => '', 'w' => 0);
  4461. }
  4462. if (!isset($this->spanborddet['R'])) {
  4463. $this->spanborddet['R'] = array('s' => 0, 'style' => '', 'w' => 0);
  4464. }
  4465. $bbw = $this->spanborddet['B']['w'];
  4466. $tbw = $this->spanborddet['T']['w'];
  4467. $lbw = $this->spanborddet['L']['w'];
  4468. $rbw = $this->spanborddet['R']['w'];
  4469. }
  4470. if ($fill == 1 || $border == 1 || !empty($this->spanborddet)) {
  4471. if (!empty($this->spanborddet)) {
  4472. if ($fill == 1) {
  4473. $s.=sprintf('%.3F %.3F %.3F %.3F re f ', ($this->x - $lbw) * _MPDFK, ($this->h - $bg_boxtop + $tbw) * _MPDFK, ($w + $lbw + $rbw) * _MPDFK, (-$bg_boxheight - $tbw - $bbw) * _MPDFK);
  4474. }
  4475. $s.= ' q ';
  4476. $dashon = 3;
  4477. $dashoff = 3.5;
  4478. $dot = 2.5;
  4479. if ($tbw) {
  4480. $short = 0;
  4481. if ($this->spanborddet['T']['style'] == 'dashed') {
  4482. $s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $tbw * $dashon * _MPDFK, $tbw * $dashoff * _MPDFK);
  4483. } elseif ($this->spanborddet['T']['style'] == 'dotted') {
  4484. $s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $tbw * $dot * _MPDFK, -$tbw / 2 * _MPDFK);
  4485. $short = $tbw / 2;
  4486. } else {
  4487. $s.=' 0 j 0 J [] 0 d ';
  4488. }
  4489. if ($this->spanborddet['T']['style'] != 'dotted') {
  4490. $s .= 'q ';
  4491. $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK);
  4492. $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK);
  4493. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK);
  4494. $s .= sprintf('%.3F %.3F l ', ($this->x) * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK);
  4495. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4496. }
  4497. $c = $this->SetDColor($this->spanborddet['T']['c'], true);
  4498. if ($this->spanborddet['T']['style'] == 'double') {
  4499. $s.=sprintf(' %s %.3F w ', $c, $tbw / 3 * _MPDFK);
  4500. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw * 5 / 6) * _MPDFK, ($this->x + $w + $rbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw * 5 / 6) * _MPDFK);
  4501. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw / 6) * _MPDFK, ($this->x + $w + $rbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw / 6) * _MPDFK);
  4502. } elseif ($this->spanborddet['T']['style'] == 'dotted') {
  4503. $s.=sprintf(' %s %.3F w ', $c, $tbw * _MPDFK);
  4504. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw / 2) * _MPDFK, ($this->x + $w + $rbw - $short) * _MPDFK, ($this->h - $bord_boxtop + $tbw / 2) * _MPDFK);
  4505. } else {
  4506. $s.=sprintf(' %s %.3F w ', $c, $tbw * _MPDFK);
  4507. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw / 2) * _MPDFK, ($this->x + $w + $rbw - $short) * _MPDFK, ($this->h - $bord_boxtop + $tbw / 2) * _MPDFK);
  4508. }
  4509. if ($this->spanborddet['T']['style'] != 'dotted') {
  4510. $s .= ' Q ';
  4511. }
  4512. }
  4513. if ($bbw) {
  4514. $short = 0;
  4515. if ($this->spanborddet['B']['style'] == 'dashed') {
  4516. $s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $bbw * $dashon * _MPDFK, $bbw * $dashoff * _MPDFK);
  4517. } elseif ($this->spanborddet['B']['style'] == 'dotted') {
  4518. $s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $bbw * $dot * _MPDFK, -$bbw / 2 * _MPDFK);
  4519. $short = $bbw / 2;
  4520. } else {
  4521. $s.=' 0 j 0 J [] 0 d ';
  4522. }
  4523. if ($this->spanborddet['B']['style'] != 'dotted') {
  4524. $s .= 'q ';
  4525. $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw) * _MPDFK);
  4526. $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw) * _MPDFK);
  4527. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * _MPDFK, ($this->h - $bord_boxbottom) * _MPDFK);
  4528. $s .= sprintf('%.3F %.3F l ', ($this->x) * _MPDFK, ($this->h - $bord_boxbottom) * _MPDFK);
  4529. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4530. }
  4531. $c = $this->SetDColor($this->spanborddet['B']['c'], true);
  4532. if ($this->spanborddet['B']['style'] == 'double') {
  4533. $s.=sprintf(' %s %.3F w ', $c, $bbw / 3 * _MPDFK);
  4534. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw / 6) * _MPDFK, ($this->x + $w + $rbw - $short) * _MPDFK, ($this->h - $bord_boxbottom - $bbw / 6) * _MPDFK);
  4535. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * _MPDFK, ($this->x + $w + $rbw - $short) * _MPDFK, ($this->h - $bord_boxbottom - $bbw * 5 / 6) * _MPDFK);
  4536. } elseif ($this->spanborddet['B']['style'] == 'dotted') {
  4537. $s.=sprintf(' %s %.3F w ', $c, $bbw * _MPDFK);
  4538. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw / 2) * _MPDFK, ($this->x + $w + $rbw - $short) * _MPDFK, ($this->h - $bord_boxbottom - $bbw / 2) * _MPDFK);
  4539. } else {
  4540. $s.=sprintf(' %s %.3F w ', $c, $bbw * _MPDFK);
  4541. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw / 2) * _MPDFK, ($this->x + $w + $rbw - $short) * _MPDFK, ($this->h - $bord_boxbottom - $bbw / 2) * _MPDFK);
  4542. }
  4543. if ($this->spanborddet['B']['style'] != 'dotted') {
  4544. $s .= ' Q ';
  4545. }
  4546. }
  4547. if ($lbw) {
  4548. $short = 0;
  4549. if ($this->spanborddet['L']['style'] == 'dashed') {
  4550. $s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $lbw * $dashon * _MPDFK, $lbw * $dashoff * _MPDFK);
  4551. } elseif ($this->spanborddet['L']['style'] == 'dotted') {
  4552. $s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $lbw * $dot * _MPDFK, -$lbw / 2 * _MPDFK);
  4553. $short = $lbw / 2;
  4554. } else {
  4555. $s.=' 0 j 0 J [] 0 d ';
  4556. }
  4557. if ($this->spanborddet['L']['style'] != 'dotted') {
  4558. $s .= 'q ';
  4559. $s .= sprintf('%.3F %.3F m ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw) * _MPDFK);
  4560. $s .= sprintf('%.3F %.3F l ', ($this->x) * _MPDFK, ($this->h - $bord_boxbottom) * _MPDFK);
  4561. $s .= sprintf('%.3F %.3F l ', ($this->x) * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK);
  4562. $s .= sprintf('%.3F %.3F l ', ($this->x - $lbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK);
  4563. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4564. }
  4565. $c = $this->SetDColor($this->spanborddet['L']['c'], true);
  4566. if ($this->spanborddet['L']['style'] == 'double') {
  4567. $s.=sprintf(' %s %.3F w ', $c, $lbw / 3 * _MPDFK);
  4568. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 6) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x - $lbw / 6) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4569. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw * 5 / 6) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x - $lbw * 5 / 6) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4570. } elseif ($this->spanborddet['L']['style'] == 'dotted') {
  4571. $s.=sprintf(' %s %.3F w ', $c, $lbw * _MPDFK);
  4572. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x - $lbw / 2) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4573. } else {
  4574. $s.=sprintf(' %s %.3F w ', $c, $lbw * _MPDFK);
  4575. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x - $lbw / 2) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x - $lbw / 2) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4576. }
  4577. if ($this->spanborddet['L']['style'] != 'dotted') {
  4578. $s .= ' Q ';
  4579. }
  4580. }
  4581. if ($rbw) {
  4582. $short = 0;
  4583. if ($this->spanborddet['R']['style'] == 'dashed') {
  4584. $s.=sprintf(' 0 j 0 J [%.3F %.3F] 0 d ', $rbw * $dashon * _MPDFK, $rbw * $dashoff * _MPDFK);
  4585. } elseif ($this->spanborddet['R']['style'] == 'dotted') {
  4586. $s.=sprintf(' 1 j 1 J [%.3F %.3F] %.3F d ', 0.001, $rbw * $dot * _MPDFK, -$rbw / 2 * _MPDFK);
  4587. $short = $rbw / 2;
  4588. } else {
  4589. $s.=' 0 j 0 J [] 0 d ';
  4590. }
  4591. if ($this->spanborddet['R']['style'] != 'dotted') {
  4592. $s .= 'q ';
  4593. $s .= sprintf('%.3F %.3F m ', ($this->x + $w + $rbw) * _MPDFK, ($this->h - $bord_boxbottom - $bbw) * _MPDFK);
  4594. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * _MPDFK, ($this->h - $bord_boxbottom) * _MPDFK);
  4595. $s .= sprintf('%.3F %.3F l ', ($this->x + $w) * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK);
  4596. $s .= sprintf('%.3F %.3F l ', ($this->x + $w + $rbw) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK);
  4597. $s .= ' h W n '; // Ends path no-op & Sets the clipping path
  4598. }
  4599. $c = $this->SetDColor($this->spanborddet['R']['c'], true);
  4600. if ($this->spanborddet['R']['style'] == 'double') {
  4601. $s.=sprintf(' %s %.3F w ', $c, $rbw / 3 * _MPDFK);
  4602. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 6) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x + $w + $rbw / 6) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4603. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw * 5 / 6) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x + $w + $rbw * 5 / 6) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4604. } elseif ($this->spanborddet['R']['style'] == 'dotted') {
  4605. $s.=sprintf(' %s %.3F w ', $c, $rbw * _MPDFK);
  4606. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x + $w + $rbw / 2) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4607. } else {
  4608. $s.=sprintf(' %s %.3F w ', $c, $rbw * _MPDFK);
  4609. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($this->x + $w + $rbw / 2) * _MPDFK, ($this->h - $bord_boxtop + $tbw) * _MPDFK, ($this->x + $w + $rbw / 2) * _MPDFK, ($this->h - $bord_boxbottom - $bbw + $short) * _MPDFK);
  4610. }
  4611. if ($this->spanborddet['R']['style'] != 'dotted') {
  4612. $s .= ' Q ';
  4613. }
  4614. }
  4615. $s.= ' Q ';
  4616. } else { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
  4617. if ($fill == 1)
  4618. $op = ($border == 1) ? 'B' : 'f';
  4619. else
  4620. $op = 'S';
  4621. $s.=sprintf('%.3F %.3F %.3F %.3F re %s ', $this->x * _MPDFK, ($this->h - $bg_boxtop) * _MPDFK, $w * _MPDFK, -$bg_boxheight * _MPDFK, $op);
  4622. }
  4623. }
  4624. if (is_string($border)) { // If "border", does not come from WriteFlowingBlock or FinishFlowingBlock
  4625. $x = $this->x;
  4626. $y = $this->y;
  4627. if (is_int(strpos($border, 'L')))
  4628. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', $x * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK, $x * _MPDFK, ($this->h - ($bord_boxbottom)) * _MPDFK);
  4629. if (is_int(strpos($border, 'T')))
  4630. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', $x * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK, ($x + $w) * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK);
  4631. if (is_int(strpos($border, 'R')))
  4632. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', ($x + $w) * _MPDFK, ($this->h - $bord_boxtop) * _MPDFK, ($x + $w) * _MPDFK, ($this->h - ($bord_boxbottom)) * _MPDFK);
  4633. if (is_int(strpos($border, 'B')))
  4634. $s.=sprintf('%.3F %.3F m %.3F %.3F l S ', $x * _MPDFK, ($this->h - ($bord_boxbottom)) * _MPDFK, ($x + $w) * _MPDFK, ($this->h - ($bord_boxbottom)) * _MPDFK);
  4635. }
  4636. if ($txt != '') {
  4637. if ($exactWidth)
  4638. $stringWidth = $w;
  4639. else
  4640. $stringWidth = $this->GetStringWidth($txt, true, $OTLdata, $textvar) + ( $this->charspacing * mb_strlen($txt, $this->mb_enc) / _MPDFK ) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc) / _MPDFK );
  4641. // Set x OFFSET FOR PRINTING
  4642. if ($align == 'R') {
  4643. $dx = $w - $this->cMarginR - $stringWidth - $lcpaddingR;
  4644. } elseif ($align == 'C') {
  4645. $dx = (($w - $stringWidth ) / 2);
  4646. } elseif ($align == 'L' or $align == 'J')
  4647. $dx = $this->cMarginL + $lcpaddingL;
  4648. else
  4649. $dx = 0;
  4650. if ($this->ColorFlag)
  4651. $s .='q ' . $this->TextColor . ' ';
  4652. // OUTLINE
  4653. if (isset($this->textparam['outline-s']) && $this->textparam['outline-s'] && !($textvar & FC_SMALLCAPS)) { // mPDF 5.7.1
  4654. $s .=' ' . sprintf('%.3F w', $this->LineWidth * _MPDFK) . ' ';
  4655. $s .=" $this->DrawColor ";
  4656. $s .=" 2 Tr ";
  4657. } elseif ($this->falseBoldWeight && strpos($this->ReqFontStyle, "B") !== false && strpos($this->FontStyle, "B") === false && !($textvar & FC_SMALLCAPS)) { // can't use together with OUTLINE or Small Caps // mPDF 5.7.1 ??? why not with SmallCaps ???
  4658. $s .= ' 2 Tr 1 J 1 j ';
  4659. $s .= ' ' . sprintf('%.3F w', ($this->FontSize / 130) * _MPDFK * $this->falseBoldWeight) . ' ';
  4660. $tc = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  4661. if ($this->FillColor != $tc) {
  4662. $s .= ' ' . $tc . ' ';
  4663. } // stroke (outline) = same colour as text(fill)
  4664. } else {
  4665. $s .=" 0 Tr ";
  4666. }
  4667. if (strpos($this->ReqFontStyle, "I") !== false && strpos($this->FontStyle, "I") === false) { // Artificial italic
  4668. $aix = '1 0 0.261799 1 %.3F %.3F Tm ';
  4669. } else {
  4670. $aix = '%.3F %.3F Td ';
  4671. }
  4672. $px = ($this->x + $dx) * _MPDFK;
  4673. $py = ($this->h - ($this->y + $glyphYorigin - $baseline_shift)) * _MPDFK;
  4674. // THE TEXT
  4675. $txt2 = $txt;
  4676. $sub = '';
  4677. $this->CurrentFont['used'] = true;
  4678. /* * ************** SIMILAR TO Text() ************************ */
  4679. // IF corefonts AND NOT SmCaps AND NOT Kerning
  4680. // Just output text; charspacing and wordspacing already set by charspacing (Tc) and ws (Tw)
  4681. if ($this->usingCoreFont && !($textvar & FC_SMALLCAPS) && !($textvar & FC_KERNING)) {
  4682. $txt2 = $this->_escape($txt2);
  4683. $sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  4684. }
  4685. // IF NOT corefonts AND NO wordspacing AND NOT SIP/SMP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  4686. // Just output text
  4687. elseif (!$this->usingCoreFont && !$this->ws && !($textvar & FC_SMALLCAPS) && !($textvar & FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && !empty($OTLdata['GPOSinfo']))) {
  4688. // IF SIP/SMP
  4689. if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
  4690. $txt2 = $this->UTF8toSubset($txt2);
  4691. $sub .=sprintf('BT ' . $aix . ' %s Tj ET', $px, $py, $txt2);
  4692. }
  4693. // NOT SIP/SMP
  4694. else {
  4695. $txt2 = $this->UTF8ToUTF16BE($txt2, false);
  4696. $txt2 = $this->_escape($txt2);
  4697. $sub .=sprintf('BT ' . $aix . ' (%s) Tj ET', $px, $py, $txt2);
  4698. }
  4699. }
  4700. // IF NOT corefonts AND IS wordspacing AND NOT SIP AND NOT SmCaps AND NOT Kerning AND NOT OTL
  4701. // Output text word by word with an adjustment to the intercharacter spacing for SPACEs to form word spacing
  4702. // IF multibyte - Tw has no effect - need to do word spacing using an adjustment before each space
  4703. elseif (!$this->usingCoreFont && $this->ws && !((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) && !($textvar & FC_SMALLCAPS) && !($textvar & FC_KERNING) && !(isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF) && (!empty($OTLdata['GPOSinfo']) || (strpos($OTLdata['group'], 'M') !== false && $this->charspacing)) )) {
  4704. $space = " ";
  4705. $space = $this->UTF8ToUTF16BE($space, false);
  4706. $space = $this->_escape($space);
  4707. $sub .=sprintf('BT ' . $aix . ' %.3F Tc [', $px, $py, $this->charspacing);
  4708. $t = explode(' ', $txt2);
  4709. $numt = count($t);
  4710. for ($i = 0; $i < $numt; $i++) {
  4711. $tx = $t[$i];
  4712. $tx = $this->UTF8ToUTF16BE($tx, false);
  4713. $tx = $this->_escape($tx);
  4714. $sub .=sprintf('(%s) ', $tx);
  4715. if (($i + 1) < $numt) {
  4716. $adj = -($this->ws) * 1000 / $this->FontSizePt;
  4717. $sub .=sprintf('%d(%s) ', $adj, $space);
  4718. }
  4719. }
  4720. $sub .='] TJ ';
  4721. $sub .=' ET';
  4722. }
  4723. // ELSE (IF SmCaps || Kerning || OTL) [corefonts or not corefonts; SIP or SMP or BMP]
  4724. else {
  4725. $sub = $this->applyGPOSpdf($txt, $aix, $px, $py, $OTLdata, $textvar);
  4726. }
  4727. /* * ************** END SIMILAR TO Text() ************************ */
  4728. if ($this->shrin_k > 1) {
  4729. $shrin_k = $this->shrin_k;
  4730. } else {
  4731. $shrin_k = 1;
  4732. }
  4733. // UNDERLINE
  4734. if ($textvar & FD_UNDERLINE) { // mPDF 5.7.1 // mPDF 6
  4735. // mPDF 5.7.3 inline text-decoration parameters
  4736. $c = $this->textparam['u-decoration']['color'];
  4737. if ($this->FillColor != $c) {
  4738. $sub .= ' ' . $c . ' ';
  4739. }
  4740. // mPDF 5.7.3 inline text-decoration parameters
  4741. $decorationfontkey = $this->textparam['u-decoration']['fontkey'];
  4742. $decorationfontsize = $this->textparam['u-decoration']['fontsize'] / $shrin_k;
  4743. if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
  4744. $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
  4745. } else {
  4746. $ut = 60 / 1000 * $decorationfontsize;
  4747. }
  4748. if (isset($this->fonts[$decorationfontkey]['up']) && $this->fonts[$decorationfontkey]['up']) {
  4749. $up = $this->fonts[$decorationfontkey]['up'];
  4750. } else {
  4751. $up = -100;
  4752. }
  4753. $adjusty = (-$up / 1000 * $decorationfontsize) + $ut / 2;
  4754. $ubaseline = $glyphYorigin - $this->textparam['u-decoration']['baseline'] / $shrin_k;
  4755. $olw = $this->LineWidth;
  4756. $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * _MPDFK));
  4757. $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $ubaseline + $adjusty, $txt, $OTLdata, $textvar);
  4758. $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * _MPDFK));
  4759. if ($this->FillColor != $c) {
  4760. $sub .= ' ' . $this->FillColor . ' ';
  4761. }
  4762. }
  4763. // STRIKETHROUGH
  4764. if ($textvar & FD_LINETHROUGH) { // mPDF 5.7.1 // mPDF 6
  4765. // mPDF 5.7.3 inline text-decoration parameters
  4766. $c = $this->textparam['s-decoration']['color'];
  4767. if ($this->FillColor != $c) {
  4768. $sub .= ' ' . $c . ' ';
  4769. }
  4770. // mPDF 5.7.3 inline text-decoration parameters
  4771. $decorationfontkey = $this->textparam['s-decoration']['fontkey'];
  4772. $decorationfontsize = $this->textparam['s-decoration']['fontsize'] / $shrin_k;
  4773. // Use yStrikeoutSize from OS/2 if available
  4774. if (isset($this->fonts[$decorationfontkey]['strs']) && $this->fonts[$decorationfontkey]['strs']) {
  4775. $ut = $this->fonts[$decorationfontkey]['strs'] / 1000 * $decorationfontsize;
  4776. }
  4777. // else use underlineThickness from post if available
  4778. elseif (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
  4779. $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
  4780. } else {
  4781. $ut = 50 / 1000 * $decorationfontsize;
  4782. }
  4783. // Use yStrikeoutPosition from OS/2 if available
  4784. if (isset($this->fonts[$decorationfontkey]['strp']) && $this->fonts[$decorationfontkey]['strp']) {
  4785. $up = $this->fonts[$decorationfontkey]['strp'];
  4786. $adjusty = (-$up / 1000 * $decorationfontsize);
  4787. }
  4788. // else use a fraction ($this->baselineS) of CapHeight
  4789. else {
  4790. if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
  4791. $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
  4792. } else {
  4793. $ch = 700;
  4794. }
  4795. $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineS;
  4796. }
  4797. $sbaseline = $glyphYorigin - $this->textparam['s-decoration']['baseline'] / $shrin_k;
  4798. $olw = $this->LineWidth;
  4799. $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * _MPDFK));
  4800. $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $sbaseline + $adjusty, $txt, $OTLdata, $textvar);
  4801. $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * _MPDFK));
  4802. if ($this->FillColor != $c) {
  4803. $sub .= ' ' . $this->FillColor . ' ';
  4804. }
  4805. }
  4806. // mPDF 5.7.3 inline text-decoration parameters
  4807. // OVERLINE
  4808. if ($textvar & FD_OVERLINE) { // mPDF 5.7.1 // mPDF 6
  4809. // mPDF 5.7.3 inline text-decoration parameters
  4810. $c = $this->textparam['o-decoration']['color'];
  4811. if ($this->FillColor != $c) {
  4812. $sub .= ' ' . $c . ' ';
  4813. }
  4814. // mPDF 5.7.3 inline text-decoration parameters
  4815. $decorationfontkey = $this->textparam['o-decoration']['fontkey'] / $shrin_k;
  4816. $decorationfontsize = $this->textparam['o-decoration']['fontsize'];
  4817. if (isset($this->fonts[$decorationfontkey]['ut']) && $this->fonts[$decorationfontkey]['ut']) {
  4818. $ut = $this->fonts[$decorationfontkey]['ut'] / 1000 * $decorationfontsize;
  4819. } else {
  4820. $ut = 60 / 1000 * $decorationfontsize;
  4821. }
  4822. if (isset($this->fonts[$decorationfontkey]['desc']['CapHeight']) && $this->fonts[$decorationfontkey]['desc']['CapHeight']) {
  4823. $ch = $this->fonts[$decorationfontkey]['desc']['CapHeight'];
  4824. } else {
  4825. $ch = 700;
  4826. }
  4827. $adjusty = (-$ch / 1000 * $decorationfontsize) * $this->baselineO;
  4828. $obaseline = $glyphYorigin - $this->textparam['o-decoration']['baseline'] / $shrin_k;
  4829. $olw = $this->LineWidth;
  4830. $sub .=' ' . (sprintf(' %.3F w 0 j 0 J ', $ut * _MPDFK));
  4831. $sub .=' ' . $this->_dounderline($this->x + $dx, $this->y + $obaseline + $adjusty, $txt, $OTLdata, $textvar);
  4832. $sub .=' ' . (sprintf(' %.3F w 2 j 2 J ', $olw * _MPDFK));
  4833. if ($this->FillColor != $c) {
  4834. $sub .= ' ' . $this->FillColor . ' ';
  4835. }
  4836. }
  4837. // TEXT SHADOW
  4838. if ($this->textshadow) { // First to process is last in CSS comma separated shadows
  4839. foreach ($this->textshadow AS $ts) {
  4840. $s .= ' q ';
  4841. $s .= $this->SetTColor($ts['col'], true) . "\n";
  4842. if ($ts['col']{0} == 5 && ord($ts['col']{4}) < 100) { // RGBa
  4843. $s .= $this->SetAlpha(ord($ts['col']{4}) / 100, 'Normal', true, 'F') . "\n";
  4844. } elseif ($ts['col']{0} == 6 && ord($ts['col']{5}) < 100) { // CMYKa
  4845. $s .= $this->SetAlpha(ord($ts['col']{5}) / 100, 'Normal', true, 'F') . "\n";
  4846. } elseif ($ts['col']{0} == 1 && $ts['col']{2} == 1 && ord($ts['col']{3}) < 100) { // Gray
  4847. $s .= $this->SetAlpha(ord($ts['col']{3}) / 100, 'Normal', true, 'F') . "\n";
  4848. }
  4849. $s .= sprintf(' 1 0 0 1 %.4F %.4F cm', $ts['x'] * _MPDFK, -$ts['y'] * _MPDFK) . "\n";
  4850. $s .= $sub;
  4851. $s .= ' Q ';
  4852. }
  4853. }
  4854. $s .= $sub;
  4855. // COLOR
  4856. if ($this->ColorFlag)
  4857. $s .=' Q';
  4858. // LINK
  4859. if ($link != '') {
  4860. $this->Link($this->x, $boxtop, $w, $boxheight, $link);
  4861. }
  4862. }
  4863. if ($s)
  4864. $this->_out($s);
  4865. // WORD SPACING
  4866. if ($this->ws && !$this->usingCoreFont) {
  4867. $this->_out(sprintf('BT %.3F Tc ET', $this->charspacing));
  4868. }
  4869. $this->lasth = $h;
  4870. if (strpos($txt, "\n") !== false)
  4871. $ln = 1; // cell recognizes \n from <BR> tag
  4872. if ($ln > 0) {
  4873. //Go to next line
  4874. $this->y += $h;
  4875. if ($ln == 1) {
  4876. //Move to next line
  4877. if ($currentx != 0) {
  4878. $this->x = $currentx;
  4879. } else {
  4880. $this->x = $this->lMargin;
  4881. }
  4882. }
  4883. } else
  4884. $this->x+=$w;
  4885. }
  4886. function applyGPOSpdf($txt, $aix, $x, $y, $OTLdata, $textvar = 0)
  4887. {
  4888. // Generate PDF string
  4889. //==============================
  4890. if ((isset($this->CurrentFont['sip']) && $this->CurrentFont['sip']) || (isset($this->CurrentFont['smp']) && $this->CurrentFont['smp'])) {
  4891. $sipset = true;
  4892. } else {
  4893. $sipset = false;
  4894. }
  4895. if ($textvar & FC_SMALLCAPS) {
  4896. $smcaps = true;
  4897. } // IF SmallCaps using transformation, NOT OTL
  4898. else {
  4899. $smcaps = false;
  4900. }
  4901. if ($sipset) {
  4902. $fontid = $last_fontid = $original_fontid = $this->CurrentFont['subsetfontids'][0];
  4903. } else {
  4904. $fontid = $last_fontid = $original_fontid = $this->CurrentFont['i'];
  4905. }
  4906. $SmallCapsON = false; // state: uppercase/not
  4907. $lastSmallCapsON = false; // state: uppercase/not
  4908. $last_fontsize = $fontsize = $this->FontSizePt;
  4909. $last_fontstretch = $fontstretch = 100;
  4910. $groupBreak = false;
  4911. $unicode = $this->UTF8StringToArray($txt);
  4912. $GPOSinfo = (isset($OTLdata['GPOSinfo']) ? $OTLdata['GPOSinfo'] : array());
  4913. $charspacing = ($this->charspacing * 1000 / $this->FontSizePt);
  4914. $wordspacing = ($this->ws * 1000 / $this->FontSizePt);
  4915. $XshiftBefore = 0;
  4916. $XshiftAfter = 0;
  4917. $lastYPlacement = 0;
  4918. if ($sipset) {
  4919. // mPDF 6 DELETED ********
  4920. // $txt= preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $txt); // ? Need to adjust OTL info
  4921. // $txt= preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $txt); // ? Need to adjust OTL info
  4922. $tj = '<';
  4923. } else {
  4924. $tj = '(';
  4925. }
  4926. for ($i = 0; $i < count($unicode); $i++) {
  4927. $c = $unicode[$i];
  4928. $tx = '';
  4929. $XshiftBefore = $XshiftAfter;
  4930. $XshiftAfter = 0;
  4931. $YPlacement = 0;
  4932. $groupBreak = false;
  4933. $kashida = 0;
  4934. if (!empty($OTLdata)) {
  4935. // YPlacement from GPOS
  4936. if (isset($GPOSinfo[$i]['YPlacement']) && $GPOSinfo[$i]['YPlacement']) {
  4937. $YPlacement = $GPOSinfo[$i]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
  4938. $groupBreak = true;
  4939. }
  4940. // XPlacement from GPOS
  4941. if (isset($GPOSinfo[$i]['XPlacement']) && $GPOSinfo[$i]['XPlacement']) {
  4942. if (!isset($GPOSinfo[$i]['wDir']) || $GPOSinfo[$i]['wDir'] != 'RTL') {
  4943. if (isset($GPOSinfo[$i]['BaseWidth'])) {
  4944. $GPOSinfo[$i]['XPlacement'] -= $GPOSinfo[$i]['BaseWidth'];
  4945. }
  4946. }
  4947. // Convert to PDF Text space (thousandths of a unit );
  4948. $XshiftBefore += $GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4949. $XshiftAfter += -$GPOSinfo[$i]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4950. }
  4951. // Kashida from GPOS
  4952. // Kashida is set as an absolute length value, but to adjust text needs to be converted to
  4953. // font-related size
  4954. if (isset($GPOSinfo[$i]['kashida_space']) && $GPOSinfo[$i]['kashida_space']) {
  4955. $kashida = $GPOSinfo[$i]['kashida_space'];
  4956. }
  4957. if ($c == 32) { // word spacing
  4958. $XshiftAfter += $wordspacing;
  4959. }
  4960. if (substr($OTLdata['group'], ($i + 1), 1) != 'M') { // Don't add inter-character spacing before Marks
  4961. $XshiftAfter += $charspacing;
  4962. }
  4963. // ...applyGPOSpdf...
  4964. // XAdvance from GPOS - Convert to PDF Text space (thousandths of a unit );
  4965. if (((isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] != 'RTL') || !isset($GPOSinfo[$i]['wDir'])) && isset($GPOSinfo[$i]['XAdvanceL']) && $GPOSinfo[$i]['XAdvanceL']) {
  4966. $XshiftAfter += $GPOSinfo[$i]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4967. } elseif (isset($GPOSinfo[$i]['wDir']) && $GPOSinfo[$i]['wDir'] == 'RTL' && isset($GPOSinfo[$i]['XAdvanceR']) && $GPOSinfo[$i]['XAdvanceR']) {
  4968. $XshiftAfter += $GPOSinfo[$i]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  4969. }
  4970. }
  4971. // Character & Word spacing - if NOT OTL
  4972. else {
  4973. $XshiftAfter += $charspacing;
  4974. if ($c == 32) {
  4975. $XshiftAfter += $wordspacing;
  4976. }
  4977. }
  4978. // IF Kerning done using pairs rather than OTL
  4979. if ($textvar & FC_KERNING) {
  4980. if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
  4981. $XshiftBefore += $this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
  4982. }
  4983. }
  4984. if ($YPlacement != $lastYPlacement) {
  4985. $groupBreak = true;
  4986. }
  4987. if ($XshiftBefore) { // +ve value in PDF moves to the left
  4988. // If Fontstretch is ongoing, need to adjust X adjustments because these will be stretched out.
  4989. $XshiftBefore *= 100 / $last_fontstretch;
  4990. if ($sipset) {
  4991. $tj .= sprintf('>%d<', (-$XshiftBefore));
  4992. } else {
  4993. $tj .= sprintf(')%d(', (-$XshiftBefore));
  4994. }
  4995. }
  4996. // Small-Caps
  4997. if ($smcaps) {
  4998. if (isset($this->upperCase[$c])) {
  4999. $c = $this->upperCase[$c];
  5000. //$this->CurrentFont['subset'][$this->upperCase[$c]] = $this->upperCase[$c]; // add the CAP to subset
  5001. $SmallCapsON = true;
  5002. // For $sipset
  5003. if (!$lastSmallCapsON) { // Turn ON SmallCaps
  5004. $groupBreak = true;
  5005. $fontstretch = $this->smCapsStretch;
  5006. $fontsize = $this->FontSizePt * $this->smCapsScale;
  5007. }
  5008. } else {
  5009. $SmallCapsON = false;
  5010. if ($lastSmallCapsON) { // Turn OFF SmallCaps
  5011. $groupBreak = true;
  5012. $fontstretch = 100;
  5013. $fontsize = $this->FontSizePt;
  5014. }
  5015. }
  5016. }
  5017. // Prepare Text and Select Font ID
  5018. if ($sipset) {
  5019. // mPDF 6 DELETED ********
  5020. //if ($c == 7 || $c == 8) {
  5021. // if ($original_fontid != $last_fontid) {
  5022. // $groupBreak = true;
  5023. // $fontid = $original_fontid;
  5024. // }
  5025. // if ($c == 7) { $tj .= $this->aliasNbPgHex; }
  5026. // else { $tj .= $this->aliasNbPgGpHex; }
  5027. // continue;
  5028. //}
  5029. for ($j = 0; $j < 99; $j++) {
  5030. $init = array_search($c, $this->CurrentFont['subsets'][$j]);
  5031. if ($init !== false) {
  5032. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  5033. $groupBreak = true;
  5034. $fontid = $this->CurrentFont['subsetfontids'][$j];
  5035. }
  5036. $tx = sprintf("%02s", strtoupper(dechex($init)));
  5037. break;
  5038. } elseif (count($this->CurrentFont['subsets'][$j]) < 255) {
  5039. $n = count($this->CurrentFont['subsets'][$j]);
  5040. $this->CurrentFont['subsets'][$j][$n] = $c;
  5041. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  5042. $groupBreak = true;
  5043. $fontid = $this->CurrentFont['subsetfontids'][$j];
  5044. }
  5045. $tx = sprintf("%02s", strtoupper(dechex($n)));
  5046. break;
  5047. } elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {
  5048. $this->CurrentFont['subsets'][($j + 1)] = array(0 => 0);
  5049. $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
  5050. $this->extraFontSubsets++;
  5051. }
  5052. }
  5053. } else {
  5054. $tx = code2utf($c);
  5055. if ($this->usingCoreFont) {
  5056. $tx = utf8_decode($tx);
  5057. } else {
  5058. $tx = $this->UTF8ToUTF16BE($tx, false);
  5059. }
  5060. $tx = $this->_escape($tx);
  5061. }
  5062. // If any settings require a new Text Group
  5063. if ($groupBreak || $fontstretch != $last_fontstretch) {
  5064. if ($sipset) {
  5065. $tj .= '>] TJ ';
  5066. } else {
  5067. $tj .= ')] TJ ';
  5068. }
  5069. if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
  5070. $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
  5071. }
  5072. if ($fontstretch != $last_fontstretch) {
  5073. $tj .= sprintf('%d Tz ', $fontstretch);
  5074. }
  5075. if ($YPlacement != $lastYPlacement) {
  5076. $tj .= sprintf('%.3F Ts ', $YPlacement);
  5077. }
  5078. if ($sipset) {
  5079. $tj .= '[<';
  5080. } else {
  5081. $tj .= '[(';
  5082. }
  5083. }
  5084. // Output the code for the txt character
  5085. $tj .= $tx;
  5086. $lastSmallCapsON = $SmallCapsON;
  5087. $last_fontid = $fontid;
  5088. $last_fontsize = $fontsize;
  5089. $last_fontstretch = $fontstretch;
  5090. // Kashida
  5091. if ($kashida) {
  5092. $c = 0x0640; // add the Tatweel U+0640
  5093. if (isset($this->CurrentFont['subset'])) {
  5094. $this->CurrentFont['subset'][$c] = $c;
  5095. }
  5096. $kashida *= 1000 / $this->FontSizePt;
  5097. $tatw = $this->_getCharWidth($this->CurrentFont['cw'], 0x0640);
  5098. // Get YPlacement from next Base character
  5099. $nextbase = $i + 1;
  5100. while ($OTLdata['group']{$nextbase} != 'C') {
  5101. $nextbase++;
  5102. }
  5103. if (isset($GPOSinfo[$nextbase]) && isset($GPOSinfo[$nextbase]['YPlacement']) && $GPOSinfo[$nextbase]['YPlacement']) {
  5104. $YPlacement = $GPOSinfo[$nextbase]['YPlacement'] * $this->FontSizePt / $this->CurrentFont['unitsPerEm'];
  5105. }
  5106. // Prepare Text and Select Font ID
  5107. if ($sipset) {
  5108. for ($j = 0; $j < 99; $j++) {
  5109. $init = array_search($c, $this->CurrentFont['subsets'][$j]);
  5110. if ($init !== false) {
  5111. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  5112. $fontid = $this->CurrentFont['subsetfontids'][$j];
  5113. }
  5114. $tx = sprintf("%02s", strtoupper(dechex($init)));
  5115. break;
  5116. } elseif (count($this->CurrentFont['subsets'][$j]) < 255) {
  5117. $n = count($this->CurrentFont['subsets'][$j]);
  5118. $this->CurrentFont['subsets'][$j][$n] = $c;
  5119. if ($this->CurrentFont['subsetfontids'][$j] != $last_fontid) {
  5120. $fontid = $this->CurrentFont['subsetfontids'][$j];
  5121. }
  5122. $tx = sprintf("%02s", strtoupper(dechex($n)));
  5123. break;
  5124. } elseif (!isset($this->CurrentFont['subsets'][($j + 1)])) {
  5125. $this->CurrentFont['subsets'][($j + 1)] = array(0 => 0);
  5126. $this->CurrentFont['subsetfontids'][($j + 1)] = count($this->fonts) + $this->extraFontSubsets + 1;
  5127. $this->extraFontSubsets++;
  5128. }
  5129. }
  5130. } else {
  5131. $tx = code2utf($c);
  5132. $tx = $this->UTF8ToUTF16BE($tx, false);
  5133. $tx = $this->_escape($tx);
  5134. }
  5135. if ($kashida > $tatw) {
  5136. // Insert multiple tatweel characters, repositioning the last one to give correct total length
  5137. $fontstretch = 100;
  5138. $nt = intval($kashida / $tatw);
  5139. $nudgeback = (($nt + 1) * $tatw) - $kashida;
  5140. $optx = str_repeat($tx, $nt);
  5141. if ($sipset) {
  5142. $optx .= sprintf('>%d<', ($nudgeback));
  5143. } else {
  5144. $optx .= sprintf(')%d(', ($nudgeback));
  5145. }
  5146. $optx .= $tx; // #last
  5147. } else {
  5148. // Insert single tatweel character and use fontstretch to get correct length
  5149. $fontstretch = ($kashida / $tatw) * 100;
  5150. $optx = $tx;
  5151. }
  5152. if ($sipset) {
  5153. $tj .= '>] TJ ';
  5154. } else {
  5155. $tj .= ')] TJ ';
  5156. }
  5157. if ($fontid != $last_fontid || $fontsize != $last_fontsize) {
  5158. $tj .= sprintf(' /F%d %.3F Tf ', $fontid, $fontsize);
  5159. }
  5160. if ($fontstretch != $last_fontstretch) {
  5161. $tj .= sprintf('%d Tz ', $fontstretch);
  5162. }
  5163. $tj .= sprintf('%.3F Ts ', $YPlacement);
  5164. if ($sipset) {
  5165. $tj .= '[<';
  5166. } else {
  5167. $tj .= '[(';
  5168. }
  5169. // Output the code for the txt character(s)
  5170. $tj .= $optx;
  5171. $last_fontid = $fontid;
  5172. $last_fontstretch = $fontstretch;
  5173. $fontstretch = 100;
  5174. }
  5175. $lastYPlacement = $YPlacement;
  5176. }
  5177. // Finish up
  5178. if ($sipset) {
  5179. $tj .= '>';
  5180. if ($XshiftAfter) {
  5181. $tj .= sprintf('%d', (-$XshiftAfter));
  5182. }
  5183. if ($last_fontid != $original_fontid) {
  5184. $tj .= '] TJ ';
  5185. $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
  5186. $tj .= '[';
  5187. }
  5188. $tj = preg_replace('/([^\\\])<>/', '\\1 ', $tj);
  5189. } else {
  5190. $tj .= ')';
  5191. if ($XshiftAfter) {
  5192. $tj .= sprintf('%d', (-$XshiftAfter));
  5193. }
  5194. if ($last_fontid != $original_fontid) {
  5195. $tj .= '] TJ ';
  5196. $tj .= sprintf(' /F%d %.3F Tf ', $original_fontid, $fontsize);
  5197. $tj .= '[';
  5198. }
  5199. $tj = preg_replace('/([^\\\])\(\)/', '\\1 ', $tj);
  5200. }
  5201. $s = sprintf(' BT ' . $aix . ' 0 Tc 0 Tw [%s] TJ ET ', $x, $y, $tj);
  5202. //echo $s."\n\n"; // exit;
  5203. return $s;
  5204. }
  5205. function _kern($txt, $mode, $aix, $x, $y)
  5206. {
  5207. if ($mode == 'MBTw') { // Multibyte requiring word spacing
  5208. $space = ' ';
  5209. //Convert string to UTF-16BE without BOM
  5210. $space = $this->UTF8ToUTF16BE($space, false);
  5211. $space = $this->_escape($space);
  5212. $s = sprintf(' BT ' . $aix, $x * _MPDFK, ($this->h - $y) * _MPDFK);
  5213. $t = explode(' ', $txt);
  5214. for ($i = 0; $i < count($t); $i++) {
  5215. $tx = $t[$i];
  5216. $tj = '(';
  5217. $unicode = $this->UTF8StringToArray($tx);
  5218. for ($ti = 0; $ti < count($unicode); $ti++) {
  5219. if ($ti > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]])) {
  5220. $kern = -$this->CurrentFont['kerninfo'][$unicode[($ti - 1)]][$unicode[$ti]];
  5221. $tj .= sprintf(')%d(', $kern);
  5222. }
  5223. $tc = code2utf($unicode[$ti]);
  5224. $tc = $this->UTF8ToUTF16BE($tc, false);
  5225. $tj .= $this->_escape($tc);
  5226. }
  5227. $tj .= ')';
  5228. $s.=sprintf(' %.3F Tc [%s] TJ', $this->charspacing, $tj);
  5229. if (($i + 1) < count($t)) {
  5230. $s.=sprintf(' %.3F Tc (%s) Tj', $this->ws + $this->charspacing, $space);
  5231. }
  5232. }
  5233. $s.=' ET ';
  5234. } elseif (!$this->usingCoreFont) {
  5235. $s = '';
  5236. $tj = '(';
  5237. $unicode = $this->UTF8StringToArray($txt);
  5238. for ($i = 0; $i < count($unicode); $i++) {
  5239. if ($i > 0 && isset($this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]])) {
  5240. $kern = -$this->CurrentFont['kerninfo'][$unicode[($i - 1)]][$unicode[$i]];
  5241. $tj .= sprintf(')%d(', $kern);
  5242. }
  5243. $tx = code2utf($unicode[$i]);
  5244. $tx = $this->UTF8ToUTF16BE($tx, false);
  5245. $tj .= $this->_escape($tx);
  5246. }
  5247. $tj .= ')';
  5248. $s.=sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * _MPDFK, ($this->h - $y) * _MPDFK, $tj);
  5249. } else { // CORE Font
  5250. $s = '';
  5251. $tj = '(';
  5252. $l = strlen($txt);
  5253. for ($i = 0; $i < $l; $i++) {
  5254. if ($i > 0 && isset($this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]])) {
  5255. $kern = -$this->CurrentFont['kerninfo'][$txt[($i - 1)]][$txt[$i]];
  5256. $tj .= sprintf(')%d(', $kern);
  5257. }
  5258. $tj .= $this->_escape($txt[$i]);
  5259. }
  5260. $tj .= ')';
  5261. $s.=sprintf(' BT ' . $aix . ' [%s] TJ ET ', $x * _MPDFK, ($this->h - $y) * _MPDFK, $tj);
  5262. }
  5263. return $s;
  5264. }
  5265. function MultiCell($w, $h, $txt, $border = 0, $align = '', $fill = 0, $link = '', $directionality = 'ltr', $encoded = false, $OTLdata = false, $maxrows = false)
  5266. {
  5267. // maxrows is called from mpdfform->TEXTAREA
  5268. // Parameter (pre-)encoded - When called internally from form::textarea - mb_encoding already done and OTL - but not reverse RTL
  5269. if (!$encoded) {
  5270. $txt = $this->purify_utf8_text($txt);
  5271. if ($this->text_input_as_HTML) {
  5272. $txt = $this->all_entities_to_utf8($txt);
  5273. }
  5274. if ($this->usingCoreFont) {
  5275. $txt = mb_convert_encoding($txt, $this->mb_enc, 'UTF-8');
  5276. }
  5277. if (preg_match("/([" . $this->pregRTLchars . "])/u", $txt)) {
  5278. $this->biDirectional = true;
  5279. } // *OTL*
  5280. /* -- OTL -- */
  5281. $OTLdata = array();
  5282. // Use OTL OpenType Table Layout - GSUB & GPOS
  5283. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5284. $txt = $this->otl->applyOTL($txt, $this->CurrentFont['useOTL']);
  5285. $OTLdata = $this->otl->OTLdata;
  5286. }
  5287. if ($directionality == 'rtl' || $this->biDirectional) {
  5288. if (!isset($OTLdata)) {
  5289. $unicode = $this->UTF8StringToArray($txt, false);
  5290. $is_strong = false;
  5291. $this->getBasicOTLdata($OTLdata, $unicode, $is_strong);
  5292. }
  5293. }
  5294. /* -- END OTL -- */
  5295. }
  5296. if (!$align) {
  5297. $align = $this->defaultAlign;
  5298. }
  5299. //Output text with automatic or explicit line breaks
  5300. $cw = &$this->CurrentFont['cw'];
  5301. if ($w == 0)
  5302. $w = $this->w - $this->rMargin - $this->x;
  5303. $wmax = ($w - ($this->cMarginL + $this->cMarginR));
  5304. if ($this->usingCoreFont) {
  5305. $s = str_replace("\r", '', $txt);
  5306. $nb = strlen($s);
  5307. while ($nb > 0 and $s[$nb - 1] == "\n")
  5308. $nb--;
  5309. } else {
  5310. $s = str_replace("\r", '', $txt);
  5311. $nb = mb_strlen($s, $this->mb_enc);
  5312. while ($nb > 0 and mb_substr($s, $nb - 1, 1, $this->mb_enc) == "\n")
  5313. $nb--;
  5314. }
  5315. $b = 0;
  5316. if ($border) {
  5317. if ($border == 1) {
  5318. $border = 'LTRB';
  5319. $b = 'LRT';
  5320. $b2 = 'LR';
  5321. } else {
  5322. $b2 = '';
  5323. if (is_int(strpos($border, 'L')))
  5324. $b2.='L';
  5325. if (is_int(strpos($border, 'R')))
  5326. $b2.='R';
  5327. $b = is_int(strpos($border, 'T')) ? $b2 . 'T' : $b2;
  5328. }
  5329. }
  5330. $sep = -1;
  5331. $i = 0;
  5332. $j = 0;
  5333. $l = 0;
  5334. $ns = 0;
  5335. $nl = 1;
  5336. $rows = 0;
  5337. $start_y = $this->y;
  5338. if (!$this->usingCoreFont) {
  5339. $inclCursive = false;
  5340. if (preg_match("/([" . $this->pregCURSchars . "])/u", $s)) {
  5341. $inclCursive = true;
  5342. }
  5343. while ($i < $nb) {
  5344. //Get next character
  5345. $c = mb_substr($s, $i, 1, $this->mb_enc);
  5346. if ($c == "\n") {
  5347. //Explicit line break
  5348. // WORD SPACING
  5349. $this->ResetSpacing();
  5350. $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
  5351. $tmpOTLdata = false;
  5352. /* -- OTL -- */
  5353. if (isset($OTLdata)) {
  5354. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
  5355. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5356. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5357. }
  5358. /* -- END OTL -- */
  5359. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5360. if ($maxrows != false && isset($this->mpdfform) && ($this->y - $start_y) / $h > $maxrows) {
  5361. return false;
  5362. }
  5363. $i++;
  5364. $sep = -1;
  5365. $j = $i;
  5366. $l = 0;
  5367. $ns = 0;
  5368. $nl++;
  5369. if ($border and $nl == 2)
  5370. $b = $b2;
  5371. continue;
  5372. }
  5373. if ($c == " ") {
  5374. $sep = $i;
  5375. $ls = $l;
  5376. $ns++;
  5377. }
  5378. $l += $this->GetCharWidthNonCore($c);
  5379. if ($l > $wmax) {
  5380. //Automatic line break
  5381. if ($sep == -1) { // Only one word
  5382. if ($i == $j)
  5383. $i++;
  5384. // WORD SPACING
  5385. $this->ResetSpacing();
  5386. $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
  5387. $tmpOTLdata = false;
  5388. /* -- OTL -- */
  5389. if (isset($OTLdata)) {
  5390. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
  5391. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5392. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5393. }
  5394. /* -- END OTL -- */
  5395. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5396. } else {
  5397. $tmp = rtrim(mb_substr($s, $j, $sep - $j, $this->mb_enc));
  5398. $tmpOTLdata = false;
  5399. /* -- OTL -- */
  5400. if (isset($OTLdata)) {
  5401. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $sep - $j);
  5402. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5403. }
  5404. /* -- END OTL -- */
  5405. if ($align == 'J') {
  5406. //////////////////////////////////////////
  5407. // JUSTIFY J using Unicode fonts (Word spacing doesn't work)
  5408. // WORD SPACING UNICODE
  5409. // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
  5410. $tmp = str_replace(chr(194) . chr(160), chr(32), $tmp);
  5411. $len_ligne = $this->GetStringWidth($tmp, false, $tmpOTLdata);
  5412. $nb_carac = mb_strlen($tmp, $this->mb_enc);
  5413. $nb_spaces = mb_substr_count($tmp, ' ', $this->mb_enc);
  5414. // Take off number of Marks
  5415. // Use GPOS OTL
  5416. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'])) {
  5417. if (isset($tmpOTLdata['group']) && $tmpOTLdata['group']) {
  5418. $nb_carac -= substr_count($tmpOTLdata['group'], 'M');
  5419. }
  5420. }
  5421. list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * _MPDFK), $inclCursive, $tmpOTLdata);
  5422. $this->SetSpacing($charspacing, $ws);
  5423. //////////////////////////////////////////
  5424. }
  5425. if (isset($OTLdata)) {
  5426. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5427. }
  5428. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5429. $i = $sep + 1;
  5430. }
  5431. if ($maxrows != false && isset($this->mpdfform) && ($this->y - $start_y) / $h > $maxrows) {
  5432. return false;
  5433. }
  5434. $sep = -1;
  5435. $j = $i;
  5436. $l = 0;
  5437. $ns = 0;
  5438. $nl++;
  5439. if ($border and $nl == 2)
  5440. $b = $b2;
  5441. } else
  5442. $i++;
  5443. }
  5444. //Last chunk
  5445. // WORD SPACING
  5446. $this->ResetSpacing();
  5447. }
  5448. else {
  5449. while ($i < $nb) {
  5450. //Get next character
  5451. $c = $s[$i];
  5452. if ($c == "\n") {
  5453. //Explicit line break
  5454. // WORD SPACING
  5455. $this->ResetSpacing();
  5456. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
  5457. if ($maxrows != false && isset($this->mpdfform) && ($this->y - $start_y) / $h > $maxrows) {
  5458. return false;
  5459. }
  5460. $i++;
  5461. $sep = -1;
  5462. $j = $i;
  5463. $l = 0;
  5464. $ns = 0;
  5465. $nl++;
  5466. if ($border and $nl == 2)
  5467. $b = $b2;
  5468. continue;
  5469. }
  5470. if ($c == " ") {
  5471. $sep = $i;
  5472. $ls = $l;
  5473. $ns++;
  5474. }
  5475. $l += $this->GetCharWidthCore($c);
  5476. if ($l > $wmax) {
  5477. //Automatic line break
  5478. if ($sep == -1) {
  5479. if ($i == $j)
  5480. $i++;
  5481. // WORD SPACING
  5482. $this->ResetSpacing();
  5483. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
  5484. }
  5485. else {
  5486. if ($align == 'J') {
  5487. $tmp = rtrim(substr($s, $j, $sep - $j));
  5488. //////////////////////////////////////////
  5489. // JUSTIFY J using Unicode fonts (Word spacing doesn't work)
  5490. // WORD SPACING NON_UNICODE/CJK
  5491. // Change NON_BREAKING SPACE to spaces so they are 'spaced' properly
  5492. $tmp = str_replace(chr(160), chr(32), $tmp);
  5493. $len_ligne = $this->GetStringWidth($tmp);
  5494. $nb_carac = strlen($tmp);
  5495. $nb_spaces = substr_count($tmp, ' ');
  5496. $tmpOTLdata = array();
  5497. list($charspacing, $ws, $kashida) = $this->GetJspacing($nb_carac, $nb_spaces, ((($wmax) - $len_ligne) * _MPDFK), false, $tmpOTLdata);
  5498. $this->SetSpacing($charspacing, $ws);
  5499. //////////////////////////////////////////
  5500. }
  5501. $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill, $link);
  5502. $i = $sep + 1;
  5503. }
  5504. if ($maxrows != false && isset($this->mpdfform) && ($this->y - $start_y) / $h > $maxrows) {
  5505. return false;
  5506. }
  5507. $sep = -1;
  5508. $j = $i;
  5509. $l = 0;
  5510. $ns = 0;
  5511. $nl++;
  5512. if ($border and $nl == 2)
  5513. $b = $b2;
  5514. } else
  5515. $i++;
  5516. }
  5517. //Last chunk
  5518. // WORD SPACING
  5519. $this->ResetSpacing();
  5520. }
  5521. //Last chunk
  5522. if ($border and is_int(strpos($border, 'B')))
  5523. $b.='B';
  5524. if (!$this->usingCoreFont) {
  5525. $tmp = rtrim(mb_substr($s, $j, $i - $j, $this->mb_enc));
  5526. $tmpOTLdata = false;
  5527. /* -- OTL -- */
  5528. if (isset($OTLdata)) {
  5529. $tmpOTLdata = $this->otl->sliceOTLdata($OTLdata, $j, $i - $j);
  5530. $this->otl->trimOTLdata($tmpOTLdata, false, true);
  5531. $this->magic_reverse_dir($tmp, $directionality, $tmpOTLdata);
  5532. }
  5533. /* -- END OTL -- */
  5534. $this->Cell($w, $h, $tmp, $b, 2, $align, $fill, $link, 0, 0, 0, 'M', 0, false, $tmpOTLdata);
  5535. } else {
  5536. $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill, $link);
  5537. }
  5538. $this->x = $this->lMargin;
  5539. }
  5540. /* -- DIRECTW -- */
  5541. function Write($h, $txt, $currentx = 0, $link = '', $directionality = 'ltr', $align = '')
  5542. {
  5543. if (!class_exists('directw', false)) {
  5544. include(_MPDF_PATH . 'classes/directw.php');
  5545. }
  5546. if (empty($this->directw)) {
  5547. $this->directw = new directw($this);
  5548. }
  5549. $this->directw->Write($h, $txt, $currentx, $link, $directionality, $align);
  5550. }
  5551. /* -- END DIRECTW -- */
  5552. /* -- HTML-CSS -- */
  5553. function saveInlineProperties()
  5554. {
  5555. $saved = array();
  5556. $saved['family'] = $this->FontFamily;
  5557. $saved['style'] = $this->FontStyle;
  5558. $saved['sizePt'] = $this->FontSizePt;
  5559. $saved['size'] = $this->FontSize;
  5560. $saved['HREF'] = $this->HREF;
  5561. $saved['textvar'] = $this->textvar; // mPDF 5.7.1
  5562. $saved['OTLtags'] = $this->OTLtags; // mPDF 5.7.1
  5563. $saved['textshadow'] = $this->textshadow;
  5564. $saved['linewidth'] = $this->LineWidth;
  5565. $saved['drawcolor'] = $this->DrawColor;
  5566. $saved['textparam'] = $this->textparam;
  5567. $saved['lSpacingCSS'] = $this->lSpacingCSS;
  5568. $saved['wSpacingCSS'] = $this->wSpacingCSS;
  5569. $saved['I'] = $this->I;
  5570. $saved['B'] = $this->B;
  5571. $saved['colorarray'] = $this->colorarray;
  5572. $saved['bgcolorarray'] = $this->spanbgcolorarray;
  5573. $saved['border'] = $this->spanborddet;
  5574. $saved['color'] = $this->TextColor;
  5575. $saved['bgcolor'] = $this->FillColor;
  5576. $saved['lang'] = $this->currentLang;
  5577. $saved['fontLanguageOverride'] = $this->fontLanguageOverride; // mPDF 5.7.1
  5578. $saved['display_off'] = $this->inlineDisplayOff;
  5579. return $saved;
  5580. }
  5581. function restoreInlineProperties(&$saved)
  5582. {
  5583. $FontFamily = $saved['family'];
  5584. $this->FontStyle = $saved['style'];
  5585. $this->FontSizePt = $saved['sizePt'];
  5586. $this->FontSize = $saved['size'];
  5587. $this->currentLang = $saved['lang'];
  5588. $this->fontLanguageOverride = $saved['fontLanguageOverride']; // mPDF 5.7.1
  5589. $this->ColorFlag = ($this->FillColor != $this->TextColor); //Restore ColorFlag as well
  5590. $this->HREF = $saved['HREF'];
  5591. $this->textvar = $saved['textvar']; // mPDF 5.7.1
  5592. $this->OTLtags = $saved['OTLtags']; // mPDF 5.7.1
  5593. $this->textshadow = $saved['textshadow'];
  5594. $this->LineWidth = $saved['linewidth'];
  5595. $this->DrawColor = $saved['drawcolor'];
  5596. $this->textparam = $saved['textparam'];
  5597. $this->inlineDisplayOff = $saved['display_off'];
  5598. $this->lSpacingCSS = $saved['lSpacingCSS'];
  5599. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  5600. $this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS, $this->FontSize);
  5601. } else {
  5602. $this->fixedlSpacing = false;
  5603. }
  5604. $this->wSpacingCSS = $saved['wSpacingCSS'];
  5605. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  5606. $this->minwSpacing = $this->ConvertSize($this->wSpacingCSS, $this->FontSize);
  5607. } else {
  5608. $this->minwSpacing = 0;
  5609. }
  5610. $this->SetFont($FontFamily, $saved['style'], $saved['sizePt'], false);
  5611. $this->currentfontstyle = $saved['style'];
  5612. $this->currentfontsize = $saved['sizePt'];
  5613. $this->SetStylesArray(array('B' => $saved['B'], 'I' => $saved['I'])); // mPDF 5.7.1
  5614. $this->TextColor = $saved['color'];
  5615. $this->FillColor = $saved['bgcolor'];
  5616. $this->colorarray = $saved['colorarray'];
  5617. $cor = $saved['colorarray'];
  5618. if ($cor)
  5619. $this->SetTColor($cor);
  5620. $this->spanbgcolorarray = $saved['bgcolorarray'];
  5621. $cor = $saved['bgcolorarray'];
  5622. if ($cor)
  5623. $this->SetFColor($cor);
  5624. $this->spanborddet = $saved['border'];
  5625. }
  5626. // Used when ColActive for tables - updated to return first block with background fill OR borders
  5627. function GetFirstBlockFill()
  5628. {
  5629. // Returns the first blocklevel that uses a bgcolor fill
  5630. $startfill = 0;
  5631. for ($i = 1; $i <= $this->blklvl; $i++) {
  5632. if ($this->blk[$i]['bgcolor'] || $this->blk[$i]['border_left']['w'] || $this->blk[$i]['border_right']['w'] || $this->blk[$i]['border_top']['w'] || $this->blk[$i]['border_bottom']['w']) {
  5633. $startfill = $i;
  5634. break;
  5635. }
  5636. }
  5637. return $startfill;
  5638. }
  5639. //-------------------------FLOWING BLOCK------------------------------------//
  5640. //The following functions were originally written by Damon Kohler //
  5641. //--------------------------------------------------------------------------//
  5642. function saveFont()
  5643. {
  5644. $saved = array();
  5645. $saved['family'] = $this->FontFamily;
  5646. $saved['style'] = $this->FontStyle;
  5647. $saved['sizePt'] = $this->FontSizePt;
  5648. $saved['size'] = $this->FontSize;
  5649. $saved['curr'] = &$this->CurrentFont;
  5650. $saved['lang'] = $this->currentLang; // mPDF 6
  5651. $saved['color'] = $this->TextColor;
  5652. $saved['spanbgcolor'] = $this->spanbgcolor;
  5653. $saved['spanbgcolorarray'] = $this->spanbgcolorarray;
  5654. $saved['bord'] = $this->spanborder;
  5655. $saved['border'] = $this->spanborddet;
  5656. $saved['HREF'] = $this->HREF;
  5657. $saved['textvar'] = $this->textvar; // mPDF 5.7.1
  5658. $saved['textshadow'] = $this->textshadow;
  5659. $saved['linewidth'] = $this->LineWidth;
  5660. $saved['drawcolor'] = $this->DrawColor;
  5661. $saved['textparam'] = $this->textparam;
  5662. $saved['ReqFontStyle'] = $this->ReqFontStyle;
  5663. $saved['fixedlSpacing'] = $this->fixedlSpacing;
  5664. $saved['minwSpacing'] = $this->minwSpacing;
  5665. return $saved;
  5666. }
  5667. function restoreFont(&$saved, $write = true)
  5668. {
  5669. if (!isset($saved) || empty($saved))
  5670. return;
  5671. $this->FontFamily = $saved['family'];
  5672. $this->FontStyle = $saved['style'];
  5673. $this->FontSizePt = $saved['sizePt'];
  5674. $this->FontSize = $saved['size'];
  5675. $this->CurrentFont = &$saved['curr'];
  5676. $this->currentLang = $saved['lang']; // mPDF 6
  5677. $this->TextColor = $saved['color'];
  5678. $this->spanbgcolor = $saved['spanbgcolor'];
  5679. $this->spanbgcolorarray = $saved['spanbgcolorarray'];
  5680. $this->spanborder = $saved['bord'];
  5681. $this->spanborddet = $saved['border'];
  5682. $this->ColorFlag = ($this->FillColor != $this->TextColor); //Restore ColorFlag as well
  5683. $this->HREF = $saved['HREF'];
  5684. $this->fixedlSpacing = $saved['fixedlSpacing'];
  5685. $this->minwSpacing = $saved['minwSpacing'];
  5686. $this->textvar = $saved['textvar']; // mPDF 5.7.1
  5687. $this->textshadow = $saved['textshadow'];
  5688. $this->LineWidth = $saved['linewidth'];
  5689. $this->DrawColor = $saved['drawcolor'];
  5690. $this->textparam = $saved['textparam'];
  5691. if ($write) {
  5692. $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], true, true); // force output
  5693. $fontout = (sprintf('BT /F%d %.3F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
  5694. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Font']) && $this->pageoutput[$this->page]['Font'] != $fontout) || !isset($this->pageoutput[$this->page]['Font']))) {
  5695. $this->_out($fontout);
  5696. }
  5697. $this->pageoutput[$this->page]['Font'] = $fontout;
  5698. } else
  5699. $this->SetFont($saved['family'], $saved['style'], $saved['sizePt'], false);
  5700. $this->ReqFontStyle = $saved['ReqFontStyle'];
  5701. }
  5702. function newFlowingBlock($w, $h, $a = '', $is_table = false, $blockstate = 0, $newblock = true, $blockdir = 'ltr', $table_draft = false)
  5703. {
  5704. if (!$a) {
  5705. if ($blockdir == 'rtl') {
  5706. $a = 'R';
  5707. } else {
  5708. $a = 'L';
  5709. }
  5710. }
  5711. $this->flowingBlockAttr['width'] = ($w * _MPDFK);
  5712. // line height in user units
  5713. $this->flowingBlockAttr['is_table'] = $is_table;
  5714. $this->flowingBlockAttr['table_draft'] = $table_draft;
  5715. $this->flowingBlockAttr['height'] = $h;
  5716. $this->flowingBlockAttr['lineCount'] = 0;
  5717. $this->flowingBlockAttr['align'] = $a;
  5718. $this->flowingBlockAttr['font'] = array();
  5719. $this->flowingBlockAttr['content'] = array();
  5720. $this->flowingBlockAttr['contentB'] = array();
  5721. $this->flowingBlockAttr['contentWidth'] = 0;
  5722. $this->flowingBlockAttr['blockstate'] = $blockstate;
  5723. $this->flowingBlockAttr['newblock'] = $newblock;
  5724. $this->flowingBlockAttr['valign'] = 'M';
  5725. $this->flowingBlockAttr['blockdir'] = $blockdir;
  5726. $this->flowingBlockAttr['cOTLdata'] = array(); // mPDF 5.7.1
  5727. $this->flowingBlockAttr['lastBidiText'] = ''; // mPDF 5.7.1
  5728. if (!empty($this->otl)) {
  5729. $this->otl->lastBidiStrongType = '';
  5730. } // *OTL*
  5731. }
  5732. function finishFlowingBlock($endofblock = false, $next = '')
  5733. {
  5734. $currentx = $this->x;
  5735. //prints out the last chunk
  5736. $is_table = $this->flowingBlockAttr['is_table'];
  5737. $table_draft = $this->flowingBlockAttr['table_draft'];
  5738. $maxWidth = & $this->flowingBlockAttr['width'];
  5739. $stackHeight = & $this->flowingBlockAttr['height'];
  5740. $align = & $this->flowingBlockAttr['align'];
  5741. $content = & $this->flowingBlockAttr['content'];
  5742. $contentB = & $this->flowingBlockAttr['contentB'];
  5743. $font = & $this->flowingBlockAttr['font'];
  5744. $contentWidth = & $this->flowingBlockAttr['contentWidth'];
  5745. $lineCount = & $this->flowingBlockAttr['lineCount'];
  5746. $valign = & $this->flowingBlockAttr['valign'];
  5747. $blockstate = $this->flowingBlockAttr['blockstate'];
  5748. $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
  5749. $newblock = $this->flowingBlockAttr['newblock'];
  5750. $blockdir = $this->flowingBlockAttr['blockdir'];
  5751. // *********** BLOCK BACKGROUND COLOR *****************//
  5752. if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
  5753. $fill = 0;
  5754. } else {
  5755. $this->SetFColor($this->ConvertColor(255));
  5756. $fill = 0;
  5757. }
  5758. $hanger = '';
  5759. // Always right trim!
  5760. // Right trim last content and adjust width if needed to justify (later)
  5761. if (isset($content[count($content) - 1]) && preg_match('/[ ]+$/', $content[count($content) - 1], $m)) {
  5762. $strip = strlen($m[0]);
  5763. $content[count($content) - 1] = substr($content[count($content) - 1], 0, (strlen($content[count($content) - 1]) - $strip));
  5764. /* -- OTL -- */
  5765. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5766. $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true);
  5767. }
  5768. /* -- END OTL -- */
  5769. }
  5770. // the amount of space taken up so far in user units
  5771. $usedWidth = 0;
  5772. // COLS
  5773. $oldcolumn = $this->CurrCol;
  5774. if ($this->ColActive && !$is_table) {
  5775. $this->breakpoints[$this->CurrCol][] = $this->y;
  5776. } // *COLUMNS*
  5777. // Print out each chunk
  5778. /* -- TABLES -- */
  5779. if ($is_table) {
  5780. $ipaddingL = 0;
  5781. $ipaddingR = 0;
  5782. $paddingL = 0;
  5783. $paddingR = 0;
  5784. } else {
  5785. /* -- END TABLES -- */
  5786. $ipaddingL = $this->blk[$this->blklvl]['padding_left'];
  5787. $ipaddingR = $this->blk[$this->blklvl]['padding_right'];
  5788. $paddingL = ($ipaddingL * _MPDFK);
  5789. $paddingR = ($ipaddingR * _MPDFK);
  5790. $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
  5791. $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
  5792. // Added mPDF 3.0 Float DIV
  5793. $fpaddingR = 0;
  5794. $fpaddingL = 0;
  5795. /* -- CSS-FLOAT -- */
  5796. if (count($this->floatDivs)) {
  5797. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  5798. if ($r_exists) {
  5799. $fpaddingR = $r_width;
  5800. }
  5801. if ($l_exists) {
  5802. $fpaddingL = $l_width;
  5803. }
  5804. }
  5805. /* -- END CSS-FLOAT -- */
  5806. $usey = $this->y + 0.002;
  5807. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  5808. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  5809. }
  5810. /* -- CSS-IMAGE-FLOAT -- */
  5811. // If float exists at this level
  5812. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  5813. $fpaddingR += $this->floatmargins['R']['w'];
  5814. }
  5815. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  5816. $fpaddingL += $this->floatmargins['L']['w'];
  5817. }
  5818. /* -- END CSS-IMAGE-FLOAT -- */
  5819. } // *TABLES*
  5820. $lineBox = array();
  5821. $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
  5822. if ($is_table && count($content) == 0) {
  5823. $stackHeight = 0;
  5824. }
  5825. if ($table_draft) {
  5826. $this->y += $stackHeight;
  5827. $this->objectbuffer = array();
  5828. return 0;
  5829. }
  5830. // While we're at it, check if contains cursive text
  5831. // Change NBSP to SPACE.
  5832. // Re-calculate contentWidth
  5833. $contentWidth = 0;
  5834. foreach ($content as $k => $chunk) {
  5835. $this->restoreFont($font[$k], false);
  5836. if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  5837. // Soft Hyphens chr(173)
  5838. if (!$this->usingCoreFont) {
  5839. /* -- OTL -- */
  5840. // mPDF 5.7.1
  5841. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  5842. $this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
  5843. $this->otl->replaceSpace($chunk, $cOTLdata[$k]);
  5844. $content[$k] = $chunk;
  5845. }
  5846. /* -- END OTL -- */ else { // *OTL*
  5847. $content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
  5848. $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
  5849. } // *OTL*
  5850. } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  5851. $content[$k] = $chunk = str_replace(chr(173), '', $chunk);
  5852. $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
  5853. }
  5854. $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * _MPDFK;
  5855. } elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  5856. // LIST MARKERS // mPDF 6 Lists
  5857. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
  5858. // do nothing
  5859. } else {
  5860. $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * _MPDFK;
  5861. }
  5862. }
  5863. }
  5864. if (isset($font[count($font) - 1])) {
  5865. $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
  5866. $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
  5867. } else {
  5868. $lastfontreqstyle = null;
  5869. $lastfontstyle = null;
  5870. }
  5871. if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
  5872. $lastitalic = $this->FontSize * 0.15 * _MPDFK;
  5873. } else {
  5874. $lastitalic = 0;
  5875. }
  5876. // Get PAGEBREAK TO TEST for height including the bottom border/padding
  5877. $check_h = max($this->divheight, $stackHeight);
  5878. // This fixes a proven bug...
  5879. if ($endofblock && $newblock && $blockstate == 0 && !$content) {
  5880. $check_h = 0;
  5881. }
  5882. // but ? needs to fix potentially more widespread...
  5883. // if (!$content) { $check_h = 0; }
  5884. if ($this->blklvl > 0 && !$is_table) {
  5885. if ($endofblock && $blockstate > 1) {
  5886. if ($this->blk[$this->blklvl]['page_break_after_avoid']) {
  5887. $check_h += $stackHeight;
  5888. }
  5889. $check_h += ($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
  5890. }
  5891. if (($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0) || ($endofblock && $blockstate == 3 && $lineCount == 0)) {
  5892. $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
  5893. }
  5894. }
  5895. // Force PAGE break if column height cannot take check-height
  5896. if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
  5897. $this->SetCol($this->NbCol - 1);
  5898. }
  5899. // Avoid just border/background-color moved on to next page
  5900. if ($endofblock && $blockstate > 1 && !$content) {
  5901. $buff = $this->margBuffer;
  5902. } else {
  5903. $buff = 0;
  5904. }
  5905. // PAGEBREAK
  5906. if (!$is_table && ($this->y + $check_h) > ($this->PageBreakTrigger + $buff) and ! $this->InFooter and $this->AcceptPageBreak()) {
  5907. $bak_x = $this->x; //Current X position
  5908. // WORD SPACING
  5909. $ws = $this->ws; //Word Spacing
  5910. $charspacing = $this->charspacing; //Character Spacing
  5911. $this->ResetSpacing();
  5912. $this->AddPage($this->CurOrientation);
  5913. $this->x = $bak_x;
  5914. // Added to correct for OddEven Margins
  5915. $currentx += $this->MarginCorrection;
  5916. $this->x += $this->MarginCorrection;
  5917. // WORD SPACING
  5918. $this->SetSpacing($charspacing, $ws);
  5919. }
  5920. /* -- COLUMNS -- */
  5921. // COLS
  5922. // COLUMN CHANGE
  5923. if ($this->CurrCol != $oldcolumn) {
  5924. $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  5925. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  5926. $oldcolumn = $this->CurrCol;
  5927. }
  5928. if ($this->ColActive && !$is_table) {
  5929. $this->breakpoints[$this->CurrCol][] = $this->y;
  5930. }
  5931. /* -- END COLUMNS -- */
  5932. // TOP MARGIN
  5933. if ($newblock && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && $lineCount == 0 && !$is_table) {
  5934. $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
  5935. if ($this->ColActive) {
  5936. $this->breakpoints[$this->CurrCol][] = $this->y;
  5937. } // *COLUMNS*
  5938. }
  5939. if ($newblock && ($blockstate == 1 || $blockstate == 3) && $lineCount == 0 && !$is_table) {
  5940. $this->blk[$this->blklvl]['y0'] = $this->y;
  5941. $this->blk[$this->blklvl]['startpage'] = $this->page;
  5942. if ($this->blk[$this->blklvl]['float']) {
  5943. $this->blk[$this->blklvl]['float_start_y'] = $this->y;
  5944. }
  5945. if ($this->ColActive) {
  5946. $this->breakpoints[$this->CurrCol][] = $this->y;
  5947. } // *COLUMNS*
  5948. }
  5949. // Paragraph INDENT
  5950. $WidthCorrection = 0;
  5951. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
  5952. $ti = $this->ConvertSize($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4
  5953. $WidthCorrection = ($ti * _MPDFK);
  5954. }
  5955. // PADDING and BORDER spacing/fill
  5956. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 0) && (!$is_table)) {
  5957. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  5958. $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
  5959. if ($this->ColActive) {
  5960. $this->breakpoints[$this->CurrCol][] = $this->y;
  5961. } // *COLUMNS*
  5962. $this->x = $currentx;
  5963. }
  5964. // Added mPDF 3.0 Float DIV
  5965. $fpaddingR = 0;
  5966. $fpaddingL = 0;
  5967. /* -- CSS-FLOAT -- */
  5968. if (count($this->floatDivs)) {
  5969. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  5970. if ($r_exists) {
  5971. $fpaddingR = $r_width;
  5972. }
  5973. if ($l_exists) {
  5974. $fpaddingL = $l_width;
  5975. }
  5976. }
  5977. /* -- END CSS-FLOAT -- */
  5978. $usey = $this->y + 0.002;
  5979. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  5980. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  5981. }
  5982. /* -- CSS-IMAGE-FLOAT -- */
  5983. // If float exists at this level
  5984. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  5985. $fpaddingR += $this->floatmargins['R']['w'];
  5986. }
  5987. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  5988. $fpaddingL += $this->floatmargins['L']['w'];
  5989. }
  5990. /* -- END CSS-IMAGE-FLOAT -- */
  5991. if ($content) {
  5992. // In FinishFlowing Block no lines are justified as it is always last line
  5993. // but if CJKorphan has allowed content width to go over max width, use J charspacing to compress line
  5994. // JUSTIFICATION J - NOT!
  5995. $nb_carac = 0;
  5996. $nb_spaces = 0;
  5997. $jcharspacing = 0;
  5998. $jkashida = 0;
  5999. $jws = 0;
  6000. $inclCursive = false;
  6001. $dottab = false;
  6002. foreach ($content as $k => $chunk) {
  6003. if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  6004. $nb_carac += mb_strlen($chunk, $this->mb_enc);
  6005. $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
  6006. // mPDF 6
  6007. // Use GPOS OTL
  6008. $this->restoreFont($font[$k], false);
  6009. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  6010. if (isset($cOTLdata[$k]['group']) && $cOTLdata[$k]['group']) {
  6011. $nb_marks = substr_count($cOTLdata[$k]['group'], 'M');
  6012. $nb_carac -= $nb_marks;
  6013. }
  6014. if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
  6015. $inclCursive = true;
  6016. }
  6017. }
  6018. } else {
  6019. $nb_carac ++; // mPDF 6 allow spacing for inline object
  6020. if ($this->objectbuffer[$k]['type'] == 'dottab') {
  6021. $dottab = $this->objectbuffer[$k]['outdent'];
  6022. }
  6023. }
  6024. }
  6025. // DIRECTIONALITY RTL
  6026. $chunkorder = range(0, count($content) - 1); // mPDF 6
  6027. /* -- OTL -- */
  6028. // mPDF 6
  6029. if ($blockdir == 'rtl' || $this->biDirectional) {
  6030. $this->otl->_bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
  6031. // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
  6032. // $this->objectbuffer and $font ($chunkorder contains the mapping)
  6033. }
  6034. /* -- END OTL -- */
  6035. // Remove any XAdvance from OTL data at end of line
  6036. // And correct for XPlacement on last character
  6037. // BIDI is applied
  6038. foreach ($chunkorder AS $aord => $k) {
  6039. if (count($cOTLdata)) {
  6040. $this->restoreFont($font[$k], false);
  6041. // ...FinishFlowingBlock...
  6042. if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
  6043. $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
  6044. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
  6045. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
  6046. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  6047. } else {
  6048. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  6049. }
  6050. $w *= ($this->FontSize / 1000);
  6051. $contentWidth -= $w * _MPDFK;
  6052. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
  6053. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
  6054. }
  6055. // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
  6056. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
  6057. $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  6058. $w *= ($this->FontSize / 1000);
  6059. $contentWidth -= $w * _MPDFK;
  6060. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  6061. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  6062. }
  6063. }
  6064. }
  6065. }
  6066. // if it's justified, we need to find the char/word spacing (or if orphans have allowed length of line to go over the maxwidth)
  6067. // If "orphans" in fact is just a final space - ignore this
  6068. $lastchar = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
  6069. if (preg_match("/[" . $this->CJKoverflow . "]/u", $lastchar)) {
  6070. $CJKoverflow = true;
  6071. } else {
  6072. $CJKoverflow = false;
  6073. }
  6074. if ((((($contentWidth + $lastitalic) > $maxWidth) && ($content[(count($chunkorder) - 1)] != ' ') ) ||
  6075. (!$endofblock && $align == 'J' && ($next == 'image' || $next == 'select' || $next == 'input' || $next == 'textarea' || ($next == 'br' && $this->justifyB4br)))) && !($CJKoverflow && $this->allowCJKoverflow)) {
  6076. // WORD SPACING
  6077. list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) )), $inclCursive, $cOTLdata);
  6078. }
  6079. /* -- CJK-FONTS -- */ elseif ($this->checkCJK && $align == 'J' && $CJKoverflow && $this->allowCJKoverflow && $this->CJKforceend) {
  6080. // force-end overhang
  6081. $hanger = mb_substr($content[(count($chunkorder) - 1)], mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, 1, $this->mb_enc);
  6082. if (preg_match("/[" . $this->CJKoverflow . "]/u", $hanger)) {
  6083. $content[(count($chunkorder) - 1)] = mb_substr($content[(count($chunkorder) - 1)], 0, mb_strlen($content[(count($chunkorder) - 1)], $this->mb_enc) - 1, $this->mb_enc);
  6084. $this->restoreFont($font[$chunkorder[count($chunkorder) - 1]], false);
  6085. $contentWidth -= $this->GetStringWidth($hanger) * _MPDFK;
  6086. $nb_carac -= 1;
  6087. list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) )), $inclCursive, $cOTLdata);
  6088. }
  6089. }
  6090. /* -- END CJK-FONTS -- */
  6091. // Check if will fit at word/char spacing of previous line - if so continue it
  6092. // but only allow a maximum of $this->jSmaxWordLast and $this->jSmaxCharLast
  6093. elseif ($contentWidth < ($maxWidth - $lastitalic - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK))) && !$this->fixedlSpacing) {
  6094. if ($this->ws > $this->jSmaxWordLast) {
  6095. $jws = $this->jSmaxWordLast;
  6096. }
  6097. if ($this->charspacing > $this->jSmaxCharLast) {
  6098. $jcharspacing = $this->jSmaxCharLast;
  6099. }
  6100. $check = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) ) - ( $jcharspacing * $nb_carac) - ( $jws * $nb_spaces);
  6101. if ($check <= 0) {
  6102. $jcharspacing = 0;
  6103. $jws = 0;
  6104. }
  6105. }
  6106. $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) );
  6107. $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
  6108. $empty -= ($jws * $nb_spaces);
  6109. $empty -= ($jkashida);
  6110. $empty /= _MPDFK;
  6111. if (!$is_table) {
  6112. $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin'] - $empty));
  6113. $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $empty));
  6114. }
  6115. $arraysize = count($chunkorder);
  6116. $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
  6117. if (!$is_table) {
  6118. $this->DivLn($stackHeight, $this->blklvl, false);
  6119. } // false -> don't advance y
  6120. $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
  6121. if ($dottab !== false && $blockdir == 'rtl') {
  6122. $this->x -= $dottab;
  6123. } elseif ($align == 'R') {
  6124. $this->x += $empty;
  6125. } elseif ($align == 'J' && $blockdir == 'rtl') {
  6126. $this->x += $empty;
  6127. } elseif ($align == 'C') {
  6128. $this->x += ($empty / 2);
  6129. }
  6130. // Paragraph INDENT
  6131. $WidthCorrection = 0;
  6132. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
  6133. $ti = $this->ConvertSize($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4
  6134. if ($blockdir != 'rtl') {
  6135. $this->x += $ti;
  6136. } // mPDF 6
  6137. }
  6138. foreach ($chunkorder AS $aord => $k) { // mPDF 5.7
  6139. $chunk = $content[$aord];
  6140. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  6141. $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
  6142. $this->objectbuffer[$k]['OUTER-X'] += $xadj;
  6143. $this->objectbuffer[$k]['BORDER-X'] += $xadj;
  6144. $this->objectbuffer[$k]['INNER-X'] += $xadj;
  6145. if ($this->objectbuffer[$k]['type'] == 'listmarker') {
  6146. $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
  6147. }
  6148. $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
  6149. if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  6150. $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
  6151. }
  6152. if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
  6153. $yadj += $lineBox[$k]['top'];
  6154. }
  6155. $this->objectbuffer[$k]['OUTER-Y'] += $yadj;
  6156. $this->objectbuffer[$k]['BORDER-Y'] += $yadj;
  6157. $this->objectbuffer[$k]['INNER-Y'] += $yadj;
  6158. }
  6159. $this->restoreFont($font[$k]); // mPDF 5.7
  6160. if ($is_table && substr($align, 0, 1) == 'D' && $aord == 0) {
  6161. $dp = $this->decimal_align[substr($align, 0, 2)];
  6162. $s = preg_split('/' . preg_quote($dp, '/') . '/', $content[0], 2); // ? needs to be /u if not core
  6163. $s0 = $this->GetStringWidth($s[0], false);
  6164. $this->x += ($this->decimal_offset - $s0);
  6165. }
  6166. $this->SetSpacing(($this->fixedlSpacing * _MPDFK) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * _MPDFK + $jws);
  6167. $this->fixedlSpacing = false;
  6168. $this->minwSpacing = 0;
  6169. $save_vis = $this->visibility;
  6170. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
  6171. $this->SetVisibility($this->textparam['visibility']);
  6172. }
  6173. // *********** SPAN BACKGROUND COLOR ***************** //
  6174. if (isset($this->spanbgcolor) && $this->spanbgcolor) {
  6175. $cor = $this->spanbgcolorarray;
  6176. $this->SetFColor($cor);
  6177. $save_fill = $fill;
  6178. $spanfill = 1;
  6179. $fill = 1;
  6180. }
  6181. if (!empty($this->spanborddet)) {
  6182. if (strpos($contentB[$k], 'L') !== false && isset($this->spanborddet['L']))
  6183. $this->x += $this->spanborddet['L']['w'];
  6184. if (strpos($contentB[$k], 'L') === false)
  6185. $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
  6186. if (strpos($contentB[$k], 'R') === false)
  6187. $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
  6188. }
  6189. // WORD SPACING
  6190. // mPDF 5.7.1
  6191. $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar);
  6192. $nch = mb_strlen($chunk, $this->mb_enc);
  6193. // Use GPOS OTL
  6194. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  6195. if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
  6196. $nch -= substr_count($cOTLdata[$aord]['group'], 'M');
  6197. }
  6198. }
  6199. $stringWidth += ( $this->charspacing * $nch / _MPDFK );
  6200. $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / _MPDFK );
  6201. if (isset($this->objectbuffer[$k])) {
  6202. if ($this->objectbuffer[$k]['type'] == 'dottab') {
  6203. $this->objectbuffer[$k]['OUTER-WIDTH'] +=$empty;
  6204. $this->objectbuffer[$k]['OUTER-WIDTH'] +=$this->objectbuffer[$k]['outdent'];
  6205. }
  6206. // LIST MARKERS // mPDF 6 Lists
  6207. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
  6208. // do nothing
  6209. } else {
  6210. $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
  6211. }
  6212. }
  6213. if ($stringWidth == 0) {
  6214. $stringWidth = 0.000001;
  6215. }
  6216. if ($aord == $arraysize - 1) { // mPDF 5.7
  6217. // mPDF 5.7.1
  6218. if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
  6219. // force-end overhang
  6220. $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1
  6221. $this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1
  6222. } else {
  6223. $this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); // mPDF 5.7.1
  6224. }
  6225. } else
  6226. $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); //first or middle part // mPDF 5.7.1
  6227. if (!empty($this->spanborddet)) {
  6228. if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1)
  6229. $this->x += $this->spanborddet['R']['w'];
  6230. }
  6231. // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
  6232. if (isset($spanfill) && $spanfill) {
  6233. $fill = $save_fill;
  6234. $spanfill = 0;
  6235. if ($fill) {
  6236. $this->SetFColor($bcor);
  6237. }
  6238. }
  6239. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
  6240. $this->SetVisibility($save_vis);
  6241. }
  6242. }
  6243. $this->printobjectbuffer($is_table, $blockdir);
  6244. $this->objectbuffer = array();
  6245. $this->ResetSpacing();
  6246. } // END IF CONTENT
  6247. /* -- CSS-IMAGE-FLOAT -- */
  6248. // Update values if set to skipline
  6249. if ($this->floatmargins) {
  6250. $this->_advanceFloatMargins();
  6251. }
  6252. if ($endofblock && $blockstate > 1) {
  6253. // If float exists at this level
  6254. if (isset($this->floatmargins['R']['y1'])) {
  6255. $fry1 = $this->floatmargins['R']['y1'];
  6256. } else {
  6257. $fry1 = 0;
  6258. }
  6259. if (isset($this->floatmargins['L']['y1'])) {
  6260. $fly1 = $this->floatmargins['L']['y1'];
  6261. } else {
  6262. $fly1 = 0;
  6263. }
  6264. if ($this->y < $fry1 || $this->y < $fly1) {
  6265. $drop = max($fry1, $fly1) - $this->y;
  6266. $this->DivLn($drop);
  6267. $this->x = $currentx;
  6268. }
  6269. }
  6270. /* -- END CSS-IMAGE-FLOAT -- */
  6271. // PADDING and BORDER spacing/fill
  6272. if ($endofblock && ($blockstate > 1) && ($this->blk[$this->blklvl]['padding_bottom'] || $this->blk[$this->blklvl]['border_bottom'] || $this->blk[$this->blklvl]['css_set_height']) && (!$is_table)) {
  6273. // If CSS height set, extend bottom - if on same page as block started, and CSS HEIGHT > actual height,
  6274. // and does not force pagebreak
  6275. $extra = 0;
  6276. if (isset($this->blk[$this->blklvl]['css_set_height']) && $this->blk[$this->blklvl]['css_set_height'] && $this->blk[$this->blklvl]['startpage'] == $this->page) {
  6277. // predicted height
  6278. $h1 = ($this->y - $this->blk[$this->blklvl]['y0']) + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
  6279. if ($h1 < ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top'])) {
  6280. $extra = ($this->blk[$this->blklvl]['css_set_height'] + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['padding_top']) - $h1;
  6281. }
  6282. if ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra > $this->PageBreakTrigger) {
  6283. $extra = $this->PageBreakTrigger - ($this->y + $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w']);
  6284. }
  6285. }
  6286. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  6287. $this->DivLn($this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $extra, -3, true, false, 2);
  6288. $this->x = $currentx;
  6289. if ($this->ColActive) {
  6290. $this->breakpoints[$this->CurrCol][] = $this->y;
  6291. } // *COLUMNS*
  6292. }
  6293. // SET Bottom y1 of block (used for painting borders)
  6294. if (($endofblock) && ($blockstate > 1) && (!$is_table)) {
  6295. $this->blk[$this->blklvl]['y1'] = $this->y;
  6296. }
  6297. // BOTTOM MARGIN
  6298. if (($endofblock) && ($blockstate > 1) && ($this->blk[$this->blklvl]['margin_bottom']) && (!$is_table)) {
  6299. if ($this->y + $this->blk[$this->blklvl]['margin_bottom'] < $this->PageBreakTrigger and ! $this->InFooter) {
  6300. $this->DivLn($this->blk[$this->blklvl]['margin_bottom'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
  6301. if ($this->ColActive) {
  6302. $this->breakpoints[$this->CurrCol][] = $this->y;
  6303. } // *COLUMNS*
  6304. }
  6305. }
  6306. // Reset lineheight
  6307. $stackHeight = $this->divheight;
  6308. }
  6309. function printobjectbuffer($is_table = false, $blockdir = false)
  6310. {
  6311. if (!$blockdir) {
  6312. $blockdir = $this->directionality;
  6313. }
  6314. if ($is_table && $this->shrin_k > 1) {
  6315. $k = $this->shrin_k;
  6316. } else {
  6317. $k = 1;
  6318. }
  6319. $save_y = $this->y;
  6320. $save_x = $this->x;
  6321. $save_currentfontfamily = $this->FontFamily;
  6322. $save_currentfontsize = $this->FontSizePt;
  6323. $save_currentfontstyle = $this->FontStyle;
  6324. if ($blockdir == 'rtl') {
  6325. $rtlalign = 'R';
  6326. } else {
  6327. $rtlalign = 'L';
  6328. }
  6329. foreach ($this->objectbuffer AS $ib => $objattr) {
  6330. if ($objattr['type'] == 'bookmark' || $objattr['type'] == 'indexentry' || $objattr['type'] == 'toc') {
  6331. $x = $objattr['OUTER-X'];
  6332. $y = $objattr['OUTER-Y'];
  6333. $this->y = $y - $this->FontSize / 2;
  6334. $this->x = $x;
  6335. if ($objattr['type'] == 'bookmark') {
  6336. $this->Bookmark($objattr['CONTENT'], $objattr['bklevel'], $y - $this->FontSize);
  6337. } // *BOOKMARKS*
  6338. if ($objattr['type'] == 'indexentry') {
  6339. $this->IndexEntry($objattr['CONTENT']);
  6340. } // *INDEX*
  6341. if ($objattr['type'] == 'toc') {
  6342. $this->TOC_Entry($objattr['CONTENT'], $objattr['toclevel'], (isset($objattr['toc_id']) ? $objattr['toc_id'] : ''));
  6343. } // *TOC*
  6344. }
  6345. /* -- ANNOTATIONS -- */ elseif ($objattr['type'] == 'annot') {
  6346. if ($objattr['POS-X']) {
  6347. $x = $objattr['POS-X'];
  6348. } elseif ($this->annotMargin <> 0) {
  6349. $x = -$objattr['OUTER-X'];
  6350. } else {
  6351. $x = $objattr['OUTER-X'];
  6352. }
  6353. if ($objattr['POS-Y']) {
  6354. $y = $objattr['POS-Y'];
  6355. } else {
  6356. $y = $objattr['OUTER-Y'] - $this->FontSize / 2;
  6357. }
  6358. // Create a dummy entry in the _out/columnBuffer with position sensitive data,
  6359. // linking $y-1 in the Columnbuffer with entry in $this->columnAnnots
  6360. // and when columns are split in length will not break annotation from current line
  6361. $this->y = $y - 1;
  6362. $this->x = $x - 1;
  6363. $this->Line($x - 1, $y - 1, $x - 1, $y - 1);
  6364. $this->Annotation($objattr['CONTENT'], $x, $y, $objattr['ICON'], $objattr['AUTHOR'], $objattr['SUBJECT'], $objattr['OPACITY'], $objattr['COLOR'], (isset($objattr['POPUP']) ? $objattr['POPUP'] : ''), (isset($objattr['FILE']) ? $objattr['FILE'] : ''));
  6365. }
  6366. /* -- END ANNOTATIONS -- */ else {
  6367. $y = $objattr['OUTER-Y'];
  6368. $x = $objattr['OUTER-X'];
  6369. $w = $objattr['OUTER-WIDTH'];
  6370. $h = $objattr['OUTER-HEIGHT'];
  6371. if (isset($objattr['text'])) {
  6372. $texto = $objattr['text'];
  6373. }
  6374. $this->y = $y;
  6375. $this->x = $x;
  6376. if (isset($objattr['fontfamily'])) {
  6377. $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
  6378. }
  6379. }
  6380. // HR
  6381. if ($objattr['type'] == 'hr') {
  6382. $this->SetDColor($objattr['color']);
  6383. switch ($objattr['align']) {
  6384. case 'C':
  6385. $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
  6386. $empty /= 2;
  6387. $x += $empty;
  6388. break;
  6389. case 'R':
  6390. $empty = $objattr['OUTER-WIDTH'] - $objattr['INNER-WIDTH'];
  6391. $x += $empty;
  6392. break;
  6393. }
  6394. $oldlinewidth = $this->LineWidth;
  6395. $this->SetLineWidth($objattr['linewidth'] / $k);
  6396. $this->y += ($objattr['linewidth'] / 2) + $objattr['margin_top'] / $k;
  6397. $this->Line($x, $this->y, $x + $objattr['INNER-WIDTH'], $this->y);
  6398. $this->SetLineWidth($oldlinewidth);
  6399. $this->SetDColor($this->ConvertColor(0));
  6400. }
  6401. // IMAGE
  6402. if ($objattr['type'] == 'image') {
  6403. // mPDF 5.7.3 TRANSFORMS
  6404. if (isset($objattr['transform'])) {
  6405. $this->_out("\n" . '% BTR'); // Begin Transform
  6406. }
  6407. if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
  6408. $this->BeginLayer($objattr['z-index']);
  6409. }
  6410. if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
  6411. $this->SetVisibility($objattr['visibility']);
  6412. }
  6413. if (isset($objattr['opacity'])) {
  6414. $this->SetAlpha($objattr['opacity']);
  6415. }
  6416. $obiw = $objattr['INNER-WIDTH'];
  6417. $obih = $objattr['INNER-HEIGHT'];
  6418. $sx = $objattr['INNER-WIDTH'] * _MPDFK / $objattr['orig_w'];
  6419. $sy = abs($objattr['INNER-HEIGHT']) * _MPDFK / abs($objattr['orig_h']);
  6420. $sx = ($objattr['INNER-WIDTH'] * _MPDFK / $objattr['orig_w']);
  6421. $sy = ($objattr['INNER-HEIGHT'] * _MPDFK / $objattr['orig_h']);
  6422. $rotate = 0;
  6423. if (isset($objattr['ROTATE'])) {
  6424. $rotate = $objattr['ROTATE'];
  6425. }
  6426. if ($rotate == 90) {
  6427. // Clockwise
  6428. $obiw = $objattr['INNER-HEIGHT'];
  6429. $obih = $objattr['INNER-WIDTH'];
  6430. $tr = $this->transformTranslate(0, -$objattr['INNER-WIDTH'], true);
  6431. $tr .= ' ' . $this->transformRotate(90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
  6432. $sx = $obiw * _MPDFK / $objattr['orig_h'];
  6433. $sy = $obih * _MPDFK / $objattr['orig_w'];
  6434. } elseif ($rotate == -90 || $rotate == 270) {
  6435. // AntiClockwise
  6436. $obiw = $objattr['INNER-HEIGHT'];
  6437. $obih = $objattr['INNER-WIDTH'];
  6438. $tr = $this->transformTranslate($objattr['INNER-WIDTH'], ($objattr['INNER-HEIGHT'] - $objattr['INNER-WIDTH']), true);
  6439. $tr .= ' ' . $this->transformRotate(-90, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-WIDTH']), true);
  6440. $sx = $obiw * _MPDFK / $objattr['orig_h'];
  6441. $sy = $obih * _MPDFK / $objattr['orig_w'];
  6442. } elseif ($rotate == 180) {
  6443. // Mirror
  6444. $tr = $this->transformTranslate($objattr['INNER-WIDTH'], -$objattr['INNER-HEIGHT'], true);
  6445. $tr .= ' ' . $this->transformRotate(180, $objattr['INNER-X'], ($objattr['INNER-Y'] + $objattr['INNER-HEIGHT']), true);
  6446. } else {
  6447. $tr = '';
  6448. }
  6449. $tr = trim($tr);
  6450. if ($tr) {
  6451. $tr .= ' ';
  6452. }
  6453. $gradmask = '';
  6454. // mPDF 5.7.3 TRANSFORMS
  6455. $tr2 = '';
  6456. if (isset($objattr['transform'])) {
  6457. $maxsize_x = $w;
  6458. $maxsize_y = $h;
  6459. $cx = $x + $w / 2;
  6460. $cy = $y + $h / 2;
  6461. preg_match_all('/(translatex|translatey|translate|scalex|scaley|scale|rotate|skewX|skewY|skew)\((.*?)\)/is', $objattr['transform'], $m);
  6462. if (count($m[0])) {
  6463. for ($i = 0; $i < count($m[0]); $i++) {
  6464. $c = strtolower($m[1][$i]);
  6465. $v = trim($m[2][$i]);
  6466. $vv = preg_split('/[ ,]+/', $v);
  6467. if ($c == 'translate' && count($vv)) {
  6468. $translate_x = $this->ConvertSize($vv[0], $maxsize_x, false, false);
  6469. if (count($vv) == 2) {
  6470. $translate_y = $this->ConvertSize($vv[1], $maxsize_y, false, false);
  6471. } else {
  6472. $translate_y = 0;
  6473. }
  6474. $tr2 .= $this->transformTranslate($translate_x, $translate_y, true) . ' ';
  6475. } elseif ($c == 'translatex' && count($vv)) {
  6476. $translate_x = $this->ConvertSize($vv[0], $maxsize_x, false, false);
  6477. $tr2 .= $this->transformTranslate($translate_x, 0, true) . ' ';
  6478. } elseif ($c == 'translatey' && count($vv)) {
  6479. $translate_y = $this->ConvertSize($vv[1], $maxsize_y, false, false);
  6480. $tr2 .= $this->transformTranslate(0, $translate_y, true) . ' ';
  6481. } elseif ($c == 'scale' && count($vv)) {
  6482. $scale_x = $vv[0] * 100;
  6483. if (count($vv) == 2) {
  6484. $scale_y = $vv[1] * 100;
  6485. } else {
  6486. $scale_y = $scale_x;
  6487. }
  6488. $tr2 .= $this->transformScale($scale_x, $scale_y, $cx, $cy, true) . ' ';
  6489. } elseif ($c == 'scalex' && count($vv)) {
  6490. $scale_x = $vv[0] * 100;
  6491. $tr2 .= $this->transformScale($scale_x, 0, $cx, $cy, true) . ' ';
  6492. } elseif ($c == 'scaley' && count($vv)) {
  6493. $scale_y = $vv[1] * 100;
  6494. $tr2 .= $this->transformScale(0, $scale_y, $cx, $cy, true) . ' ';
  6495. } elseif ($c == 'skew' && count($vv)) {
  6496. $angle_x = $this->ConvertAngle($vv[0], false);
  6497. if (count($vv) == 2) {
  6498. $angle_y = $this->ConvertAngle($vv[1], false);
  6499. } else {
  6500. $angle_y = 0;
  6501. }
  6502. $tr2 .= $this->transformSkew($angle_x, $angle_y, $cx, $cy, true) . ' ';
  6503. } elseif ($c == 'skewx' && count($vv)) {
  6504. $angle = $this->ConvertAngle($vv[0], false);
  6505. $tr2 .= $this->transformSkew($angle, 0, $cx, $cy, true) . ' ';
  6506. } elseif ($c == 'skewy' && count($vv)) {
  6507. $angle = $this->ConvertAngle($vv[0], false);
  6508. $tr2 .= $this->transformSkew(0, $angle, $cx, $cy, true) . ' ';
  6509. } elseif ($c == 'rotate' && count($vv)) {
  6510. $angle = $this->ConvertAngle($vv[0]);
  6511. $tr2 .= $this->transformRotate($angle, $cx, $cy, true) . ' ';
  6512. }
  6513. }
  6514. }
  6515. }
  6516. // LIST MARKERS (Images) // mPDF 6 Lists
  6517. if (isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
  6518. $mw = $objattr['OUTER-WIDTH'];
  6519. // NB If change marker-offset, also need to alter in function _getListMarkerWidth
  6520. $adjx = $this->ConvertSize($this->list_marker_offset, $this->FontSize);
  6521. if ($objattr['dir'] == 'rtl') {
  6522. $objattr['INNER-X'] += $adjx;
  6523. } else {
  6524. $objattr['INNER-X'] -= $adjx;
  6525. $objattr['INNER-X'] -= $mw;
  6526. }
  6527. }
  6528. // mPDF 5.7.3 TRANSFORMS / BACKGROUND COLOR
  6529. // Transform also affects image background
  6530. if ($tr2) {
  6531. $this->_out('q ' . $tr2 . ' ');
  6532. }
  6533. if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
  6534. $bgcol = $objattr['bgcolor'];
  6535. $this->SetFColor($bgcol);
  6536. $this->Rect($x, $y, $w, $h, 'F');
  6537. $this->SetFColor($this->ConvertColor(255));
  6538. }
  6539. if ($tr2) {
  6540. $this->_out('Q');
  6541. }
  6542. /* -- BACKGROUNDS -- */
  6543. if (isset($objattr['GRADIENT-MASK'])) {
  6544. $g = $this->grad->parseMozGradient($objattr['GRADIENT-MASK']);
  6545. if ($g) {
  6546. $dummy = $this->grad->Gradient($objattr['INNER-X'], $objattr['INNER-Y'], $obiw, $obih, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend'], true, true);
  6547. $gradmask = '/TGS' . count($this->gradients) . ' gs ';
  6548. }
  6549. }
  6550. /* -- END BACKGROUNDS -- */
  6551. /* -- IMAGES-WMF -- */
  6552. if (isset($objattr['itype']) && $objattr['itype'] == 'wmf') {
  6553. $outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * _MPDFK - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * _MPDFK) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS
  6554. } else
  6555. /* -- END IMAGES-WMF -- */
  6556. if (isset($objattr['itype']) && $objattr['itype'] == 'svg') {
  6557. $outstring = sprintf('q ' . $tr . $tr2 . '%.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, -$sy, $objattr['INNER-X'] * _MPDFK - $sx * $objattr['wmf_x'], (($this->h - $objattr['INNER-Y']) * _MPDFK) + $sy * $objattr['wmf_y'], $objattr['ID']); // mPDF 5.7.3 TRANSFORMS
  6558. } else {
  6559. $outstring = sprintf("q " . $tr . $tr2 . "%.3F 0 0 %.3F %.3F %.3F cm " . $gradmask . "/I%d Do Q", $obiw * _MPDFK, $obih * _MPDFK, $objattr['INNER-X'] * _MPDFK, ($this->h - ($objattr['INNER-Y'] + $obih )) * _MPDFK, $objattr['ID']); // mPDF 5.7.3 TRANSFORMS
  6560. }
  6561. $this->_out($outstring);
  6562. // LINK
  6563. if (isset($objattr['link']))
  6564. $this->Link($objattr['INNER-X'], $objattr['INNER-Y'], $objattr['INNER-WIDTH'], $objattr['INNER-HEIGHT'], $objattr['link']);
  6565. if (isset($objattr['opacity'])) {
  6566. $this->SetAlpha(1);
  6567. }
  6568. // mPDF 5.7.3 TRANSFORMS
  6569. // Transform also affects image borders
  6570. if ($tr2) {
  6571. $this->_out('q ' . $tr2 . ' ');
  6572. }
  6573. if ((isset($objattr['border_top']) && $objattr['border_top'] > 0) || (isset($objattr['border_left']) && $objattr['border_left'] > 0) || (isset($objattr['border_right']) && $objattr['border_right'] > 0) || (isset($objattr['border_bottom']) && $objattr['border_bottom'] > 0)) {
  6574. $this->PaintImgBorder($objattr, $is_table);
  6575. }
  6576. if ($tr2) {
  6577. $this->_out('Q');
  6578. }
  6579. if (isset($objattr['visibility']) && $objattr['visibility'] != 'visible' && $objattr['visibility']) {
  6580. $this->SetVisibility('visible');
  6581. }
  6582. if (isset($objattr['z-index']) && $objattr['z-index'] > 0 && $this->current_layer == 0) {
  6583. $this->EndLayer();
  6584. }
  6585. // mPDF 5.7.3 TRANSFORMS
  6586. if (isset($objattr['transform'])) {
  6587. $this->_out("\n" . '% ETR'); // End Transform
  6588. }
  6589. }
  6590. /* -- BARCODES -- */
  6591. // BARCODE
  6592. if ($objattr['type'] == 'barcode') {
  6593. $bgcol = $this->ConvertColor(255);
  6594. if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
  6595. $bgcol = $objattr['bgcolor'];
  6596. }
  6597. $col = $this->ConvertColor(0);
  6598. if (isset($objattr['color']) && $objattr['color']) {
  6599. $col = $objattr['color'];
  6600. }
  6601. $this->SetFColor($bgcol);
  6602. $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
  6603. $this->SetFColor($this->ConvertColor(255));
  6604. if (isset($objattr['BORDER-WIDTH'])) {
  6605. $this->PaintImgBorder($objattr, $is_table);
  6606. }
  6607. if ($objattr['btype'] == 'EAN13' || $objattr['btype'] == 'ISBN' || $objattr['btype'] == 'ISSN' || $objattr['btype'] == 'UPCA' || $objattr['btype'] == 'UPCE' || $objattr['btype'] == 'EAN8') {
  6608. $this->WriteBarcode($objattr['code'], $objattr['showtext'], $objattr['INNER-X'], $objattr['INNER-Y'], $objattr['bsize'], 0, 0, 0, 0, 0, $objattr['bheight'], $bgcol, $col, $objattr['btype'], $objattr['bsupp'], (isset($objattr['bsupp_code']) ? $objattr['bsupp_code'] : ''), $k);
  6609. }
  6610. // QR-code
  6611. elseif ($objattr['btype'] == 'QR') {
  6612. if (!class_exists('QRcode', false)) {
  6613. include(_MPDF_PATH . 'qrcode/qrcode.class.php');
  6614. }
  6615. $this->qrcode = new QRcode($objattr['code'], $objattr['errorlevel']);
  6616. $this->qrcode->displayFPDF($this, $objattr['INNER-X'], $objattr['INNER-Y'], $objattr['bsize'] * 25, array(255, 255, 255), array(0, 0, 0));
  6617. } else {
  6618. $this->WriteBarcode2($objattr['code'], $objattr['INNER-X'], $objattr['INNER-Y'], $objattr['bsize'], $objattr['bheight'], $bgcol, $col, $objattr['btype'], $objattr['pr_ratio'], $k);
  6619. }
  6620. }
  6621. /* -- END BARCODES -- */
  6622. // TEXT CIRCLE
  6623. if ($objattr['type'] == 'textcircle') {
  6624. $bgcol = '';
  6625. if (isset($objattr['bgcolor']) && $objattr['bgcolor']) {
  6626. $bgcol = $objattr['bgcolor'];
  6627. }
  6628. $col = $this->ConvertColor(0);
  6629. if (isset($objattr['color']) && $objattr['color']) {
  6630. $col = $objattr['color'];
  6631. }
  6632. $this->SetTColor($col);
  6633. $this->SetFColor($bgcol);
  6634. if ($bgcol)
  6635. $this->Rect($objattr['BORDER-X'], $objattr['BORDER-Y'], $objattr['BORDER-WIDTH'], $objattr['BORDER-HEIGHT'], 'F');
  6636. $this->SetFColor($this->ConvertColor(255));
  6637. if (isset($objattr['BORDER-WIDTH'])) {
  6638. $this->PaintImgBorder($objattr, $is_table);
  6639. }
  6640. if (!class_exists('directw', false)) {
  6641. include(_MPDF_PATH . 'classes/directw.php');
  6642. }
  6643. if (empty($this->directw)) {
  6644. $this->directw = new directw($this);
  6645. }
  6646. if (isset($objattr['top-text'])) {
  6647. $this->directw->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['top-text'], 'top', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : ''));
  6648. }
  6649. if (isset($objattr['bottom-text'])) {
  6650. $this->directw->CircularText($objattr['INNER-X'] + $objattr['INNER-WIDTH'] / 2, $objattr['INNER-Y'] + $objattr['INNER-HEIGHT'] / 2, $objattr['r'] / $k, $objattr['bottom-text'], 'bottom', $objattr['fontfamily'], $objattr['fontsize'] / $k, $objattr['fontstyle'], $objattr['space-width'], $objattr['char-width'], (isset($objattr['divider']) ? $objattr['divider'] : ''));
  6651. }
  6652. }
  6653. $this->ResetSpacing();
  6654. // LIST MARKERS (Text or bullets) // mPDF 6 Lists
  6655. if ($objattr['type'] == 'listmarker') {
  6656. if (isset($objattr['fontfamily'])) {
  6657. $this->SetFont($objattr['fontfamily'], $objattr['fontstyle'], $objattr['fontsizept']);
  6658. }
  6659. $col = $this->ConvertColor(0);
  6660. if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
  6661. $col = $objattr['colorarray'];
  6662. }
  6663. if (isset($objattr['bullet']) && $objattr['bullet']) { // Used for position "outside" only
  6664. $type = $objattr['bullet'];
  6665. $size = $objattr['size'];
  6666. if ($objattr['listmarkerposition'] == 'inside') {
  6667. $adjx = $size / 2;
  6668. if ($objattr['dir'] == 'rtl') {
  6669. $adjx += $objattr['offset'];
  6670. }
  6671. $this->x += $adjx;
  6672. } else {
  6673. $adjx = $objattr['offset'];
  6674. $adjx += $size / 2;
  6675. if ($objattr['dir'] == 'rtl') {
  6676. $this->x += $adjx;
  6677. } else {
  6678. $this->x -= $adjx;
  6679. }
  6680. }
  6681. $yadj = $objattr['lineBox']['glyphYorigin'];
  6682. if (isset($this->CurrentFont['desc']['XHeight']) && $this->CurrentFont['desc']['XHeight']) {
  6683. $xh = $this->CurrentFont['desc']['XHeight'];
  6684. } else {
  6685. $xh = 500;
  6686. }
  6687. $yadj -= ($this->FontSize * $xh / 1000) * 0.625; // Vertical height of bullet (centre) from baseline= XHeight * 0.625
  6688. $this->y += $yadj;
  6689. $this->_printListBullet($this->x, $this->y, $size, $type, $col);
  6690. } else {
  6691. $this->SetTColor($col);
  6692. $w = $this->GetStringWidth($texto);
  6693. // NB If change marker-offset, also need to alter in function _getListMarkerWidth
  6694. $adjx = $this->ConvertSize($this->list_marker_offset, $this->FontSize);
  6695. if ($objattr['dir'] == 'rtl') {
  6696. $align = 'L';
  6697. $this->x += $adjx;
  6698. } else {
  6699. // Use these lines to set as marker-offset, right-aligned - default
  6700. $align = 'R';
  6701. $this->x -= $adjx;
  6702. $this->x -= $w;
  6703. }
  6704. $this->Cell($w, $this->FontSize, $texto, 0, 0, $align, 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']);
  6705. $this->SetTColor($this->ConvertColor(0));
  6706. }
  6707. }
  6708. // DOT-TAB
  6709. if ($objattr['type'] == 'dottab') {
  6710. if (isset($objattr['fontfamily'])) {
  6711. $this->SetFont($objattr['fontfamily'], '', $objattr['fontsize']);
  6712. }
  6713. $sp = $this->GetStringWidth(' ');
  6714. $nb = floor(($w - 2 * $sp) / $this->GetStringWidth('.'));
  6715. if ($nb > 0) {
  6716. $dots = ' ' . str_repeat('.', $nb) . ' ';
  6717. } else {
  6718. $dots = ' ';
  6719. }
  6720. $col = $this->ConvertColor(0);
  6721. if (isset($objattr['colorarray']) && ($objattr['colorarray'])) {
  6722. $col = $objattr['colorarray'];
  6723. }
  6724. $this->SetTColor($col);
  6725. $save_dh = $this->divheight;
  6726. $save_sbd = $this->spanborddet;
  6727. $save_textvar = $this->textvar; // mPDF 5.7.1
  6728. $this->spanborddet = '';
  6729. $this->divheight = 0;
  6730. $this->textvar = 0x00; // mPDF 5.7.1
  6731. $this->Cell($w, $h, $dots, 0, 0, 'C', 0, '', 0, 0, 0, 'T', 0, false, false, 0, $objattr['lineBox']); // mPDF 6 DOTTAB
  6732. $this->spanborddet = $save_sbd;
  6733. $this->textvar = $save_textvar; // mPDF 5.7.1
  6734. $this->divheight = $save_dh;
  6735. $this->SetTColor($this->ConvertColor(0));
  6736. }
  6737. /* -- FORMS -- */
  6738. // TEXT/PASSWORD INPUT
  6739. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD')) {
  6740. $this->mpdfform->print_ob_text($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6741. }
  6742. // TEXTAREA
  6743. if ($objattr['type'] == 'textarea') {
  6744. $this->mpdfform->print_ob_textarea($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6745. }
  6746. // SELECT
  6747. if ($objattr['type'] == 'select') {
  6748. $this->mpdfform->print_ob_select($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6749. }
  6750. // INPUT/BUTTON as IMAGE
  6751. if ($objattr['type'] == 'input' && $objattr['subtype'] == 'IMAGE') {
  6752. $this->mpdfform->print_ob_imageinput($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6753. }
  6754. // BUTTON
  6755. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'SUBMIT' || $objattr['subtype'] == 'RESET' || $objattr['subtype'] == 'BUTTON')) {
  6756. $this->mpdfform->print_ob_button($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir);
  6757. }
  6758. // CHECKBOX
  6759. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'CHECKBOX')) {
  6760. $this->mpdfform->print_ob_checkbox($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
  6761. }
  6762. // RADIO
  6763. if ($objattr['type'] == 'input' && ($objattr['subtype'] == 'RADIO')) {
  6764. $this->mpdfform->print_ob_radio($objattr, $w, $h, $texto, $rtlalign, $k, $blockdir, $x, $y);
  6765. }
  6766. /* -- END FORMS -- */
  6767. }
  6768. $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
  6769. $this->y = $save_y;
  6770. $this->x = $save_x;
  6771. unset($content);
  6772. }
  6773. function _printListBullet($x, $y, $size, $type, $color)
  6774. {
  6775. // x and y are the centre of the bullet; size is the width and/or height in mm
  6776. $fcol = $this->SetTColor($color, true);
  6777. $lcol = strtoupper($fcol); // change 0 0 0 rg to 0 0 0 RG
  6778. $this->_out(sprintf('q %s %s', $lcol, $fcol));
  6779. $this->_out('0 j 0 J [] 0 d');
  6780. if ($type == 'square') {
  6781. $size *= 0.85; // Smaller to appear the same size as circle/disc
  6782. $this->_out(sprintf('%.3F %.3F %.3F %.3F re f', ($x - $size / 2) * _MPDFK, ($this->h - $y + $size / 2) * _MPDFK, ($size) * _MPDFK, (-$size) * _MPDFK));
  6783. } elseif ($type == 'disc') {
  6784. $this->Circle($x, $y, $size / 2, 'F'); // Fill
  6785. } elseif ($type == 'circle') {
  6786. $lw = $size / 12; // Line width
  6787. $this->_out(sprintf('%.3F w ', $lw * _MPDFK));
  6788. $this->Circle($x, $y, $size / 2 - $lw / 2, 'S'); // Stroke
  6789. }
  6790. $this->_out('Q');
  6791. }
  6792. // mPDF 6
  6793. // Get previous character and move pointers
  6794. function _moveToPrevChar(&$contentctr, &$charctr, $content)
  6795. {
  6796. $lastchar = false;
  6797. $charctr--;
  6798. while ($charctr < 0) { // go back to previous $content[]
  6799. $contentctr--;
  6800. if ($contentctr < 0) {
  6801. return false;
  6802. }
  6803. if ($this->usingCoreFont) {
  6804. $charctr = strlen($content[$contentctr]) - 1;
  6805. } else {
  6806. $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
  6807. }
  6808. }
  6809. if ($this->usingCoreFont) {
  6810. $lastchar = $content[$contentctr][$charctr];
  6811. } else {
  6812. $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
  6813. }
  6814. return $lastchar;
  6815. }
  6816. // Get previous character
  6817. function _getPrevChar($contentctr, $charctr, $content)
  6818. {
  6819. $lastchar = false;
  6820. $charctr--;
  6821. while ($charctr < 0) { // go back to previous $content[]
  6822. $contentctr--;
  6823. if ($contentctr < 0) {
  6824. return false;
  6825. }
  6826. if ($this->usingCoreFont) {
  6827. $charctr = strlen($content[$contentctr]) - 1;
  6828. } else {
  6829. $charctr = mb_strlen($content[$contentctr], $this->mb_enc) - 1;
  6830. }
  6831. }
  6832. if ($this->usingCoreFont) {
  6833. $lastchar = $content[$contentctr][$charctr];
  6834. } else {
  6835. $lastchar = mb_substr($content[$contentctr], $charctr, 1, $this->mb_enc);
  6836. }
  6837. return $lastchar;
  6838. }
  6839. function WriteFlowingBlock($s, $sOTLdata)
  6840. { // mPDF 5.7.1
  6841. $currentx = $this->x;
  6842. $is_table = $this->flowingBlockAttr['is_table'];
  6843. $table_draft = $this->flowingBlockAttr['table_draft'];
  6844. // width of all the content so far in points
  6845. $contentWidth = & $this->flowingBlockAttr['contentWidth'];
  6846. // cell width in points
  6847. $maxWidth = & $this->flowingBlockAttr['width'];
  6848. $lineCount = & $this->flowingBlockAttr['lineCount'];
  6849. // line height in user units
  6850. $stackHeight = & $this->flowingBlockAttr['height'];
  6851. $align = & $this->flowingBlockAttr['align'];
  6852. $content = & $this->flowingBlockAttr['content'];
  6853. $contentB = & $this->flowingBlockAttr['contentB'];
  6854. $font = & $this->flowingBlockAttr['font'];
  6855. $valign = & $this->flowingBlockAttr['valign'];
  6856. $blockstate = $this->flowingBlockAttr['blockstate'];
  6857. $cOTLdata = & $this->flowingBlockAttr['cOTLdata']; // mPDF 5.7.1
  6858. $newblock = $this->flowingBlockAttr['newblock'];
  6859. $blockdir = $this->flowingBlockAttr['blockdir'];
  6860. // *********** BLOCK BACKGROUND COLOR ***************** //
  6861. if ($this->blk[$this->blklvl]['bgcolor'] && !$is_table) {
  6862. $fill = 0;
  6863. } else {
  6864. $this->SetFColor($this->ConvertColor(255));
  6865. $fill = 0;
  6866. }
  6867. $font[] = $this->saveFont();
  6868. $content[] = '';
  6869. $contentB[] = '';
  6870. $cOTLdata[] = $sOTLdata; // mPDF 5.7.1
  6871. $currContent = & $content[count($content) - 1];
  6872. $CJKoverflow = false;
  6873. $Oikomi = false; // mPDF 6
  6874. $hanger = '';
  6875. // COLS
  6876. $oldcolumn = $this->CurrCol;
  6877. if ($this->ColActive && !$is_table) {
  6878. $this->breakpoints[$this->CurrCol][] = $this->y;
  6879. } // *COLUMNS*
  6880. /* -- TABLES -- */
  6881. if ($is_table) {
  6882. $ipaddingL = 0;
  6883. $ipaddingR = 0;
  6884. $paddingL = 0;
  6885. $paddingR = 0;
  6886. $cpaddingadjustL = 0;
  6887. $cpaddingadjustR = 0;
  6888. // Added mPDF 3.0
  6889. $fpaddingR = 0;
  6890. $fpaddingL = 0;
  6891. } else {
  6892. /* -- END TABLES -- */
  6893. $ipaddingL = $this->blk[$this->blklvl]['padding_left'];
  6894. $ipaddingR = $this->blk[$this->blklvl]['padding_right'];
  6895. $paddingL = ($ipaddingL * _MPDFK);
  6896. $paddingR = ($ipaddingR * _MPDFK);
  6897. $this->cMarginL = $this->blk[$this->blklvl]['border_left']['w'];
  6898. $cpaddingadjustL = -$this->cMarginL;
  6899. $this->cMarginR = $this->blk[$this->blklvl]['border_right']['w'];
  6900. $cpaddingadjustR = -$this->cMarginR;
  6901. // Added mPDF 3.0 Float DIV
  6902. $fpaddingR = 0;
  6903. $fpaddingL = 0;
  6904. /* -- CSS-FLOAT -- */
  6905. if (count($this->floatDivs)) {
  6906. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  6907. if ($r_exists) {
  6908. $fpaddingR = $r_width;
  6909. }
  6910. if ($l_exists) {
  6911. $fpaddingL = $l_width;
  6912. }
  6913. }
  6914. /* -- END CSS-FLOAT -- */
  6915. $usey = $this->y + 0.002;
  6916. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  6917. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  6918. }
  6919. /* -- CSS-IMAGE-FLOAT -- */
  6920. // If float exists at this level
  6921. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  6922. $fpaddingR += $this->floatmargins['R']['w'];
  6923. }
  6924. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  6925. $fpaddingL += $this->floatmargins['L']['w'];
  6926. }
  6927. /* -- END CSS-IMAGE-FLOAT -- */
  6928. } // *TABLES*
  6929. //OBJECTS - IMAGES & FORM Elements (NB has already skipped line/page if required - in printbuffer)
  6930. if (substr($s, 0, 3) == "\xbb\xa4\xac") { //identifier has been identified!
  6931. $objattr = $this->_getObjAttr($s);
  6932. $h_corr = 0;
  6933. if ($is_table) { // *TABLES*
  6934. $maximumW = ($maxWidth / _MPDFK) - ($this->cellPaddingL + $this->cMarginL + $this->cellPaddingR + $this->cMarginR); // *TABLES*
  6935. } // *TABLES*
  6936. else { // *TABLES*
  6937. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0) && (!$is_table)) {
  6938. $h_corr = $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  6939. }
  6940. $maximumW = ($maxWidth / _MPDFK) - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w'] + $fpaddingL + $fpaddingR );
  6941. } // *TABLES*
  6942. $objattr = $this->inlineObject($objattr['type'], $this->lMargin + $fpaddingL + ($contentWidth / _MPDFK), ($this->y + $h_corr), $objattr, $this->lMargin, ($contentWidth / _MPDFK), $maximumW, $stackHeight, true, $is_table);
  6943. // SET LINEHEIGHT for this line ================ RESET AT END
  6944. $stackHeight = MAX($stackHeight, $objattr['OUTER-HEIGHT']);
  6945. $this->objectbuffer[count($content) - 1] = $objattr;
  6946. // if (isset($objattr['vertical-align'])) { $valign = $objattr['vertical-align']; }
  6947. // else { $valign = ''; }
  6948. // LIST MARKERS // mPDF 6 Lists
  6949. if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker'] && $objattr['listmarkerposition'] == 'outside') {
  6950. // do nothing
  6951. } else {
  6952. $contentWidth += ($objattr['OUTER-WIDTH'] * _MPDFK);
  6953. }
  6954. return;
  6955. }
  6956. $lbw = $rbw = 0; // Border widths
  6957. if (!empty($this->spanborddet)) {
  6958. if (isset($this->spanborddet['L']))
  6959. $lbw = $this->spanborddet['L']['w'];
  6960. if (isset($this->spanborddet['R']))
  6961. $rbw = $this->spanborddet['R']['w'];
  6962. }
  6963. if ($this->usingCoreFont) {
  6964. $clen = strlen($s);
  6965. } else {
  6966. $clen = mb_strlen($s, $this->mb_enc);
  6967. }
  6968. // for every character in the string
  6969. for ($i = 0; $i < $clen; $i++) {
  6970. // extract the current character
  6971. // get the width of the character in points
  6972. if ($this->usingCoreFont) {
  6973. $c = $s[$i];
  6974. // Soft Hyphens chr(173)
  6975. $cw = ($this->GetCharWidthCore($c) * _MPDFK);
  6976. if (($this->textvar & FC_KERNING) && $i > 0) { // mPDF 5.7.1
  6977. if (isset($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c])) {
  6978. $cw += ($this->CurrentFont['kerninfo'][$s[($i - 1)]][$c] * $this->FontSizePt / 1000 );
  6979. }
  6980. }
  6981. } else {
  6982. $c = mb_substr($s, $i, 1, $this->mb_enc);
  6983. $cw = ($this->GetCharWidthNonCore($c, false) * _MPDFK);
  6984. // mPDF 5.7.1
  6985. // Use OTL GPOS
  6986. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
  6987. // ...WriteFlowingBlock...
  6988. // Only add XAdvanceL (not sure at present whether RTL or LTR writing direction)
  6989. // At this point, XAdvanceL and XAdvanceR will balance
  6990. if (isset($sOTLdata['GPOSinfo'][$i]['XAdvanceL'])) {
  6991. $cw += $sOTLdata['GPOSinfo'][$i]['XAdvanceL'] * (1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000) * _MPDFK;
  6992. }
  6993. }
  6994. if (($this->textvar & FC_KERNING) && $i > 0) { // mPDF 5.7.1
  6995. $lastc = mb_substr($s, ($i - 1), 1, $this->mb_enc);
  6996. $ulastc = $this->UTF8StringToArray($lastc, false);
  6997. $uc = $this->UTF8StringToArray($c, false);
  6998. if (isset($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]])) {
  6999. $cw += ($this->CurrentFont['kerninfo'][$ulastc[0]][$uc[0]] * $this->FontSizePt / 1000 );
  7000. }
  7001. }
  7002. }
  7003. if ($i == 0) {
  7004. $cw += $lbw * _MPDFK;
  7005. $contentB[(count($contentB) - 1)] .= 'L';
  7006. }
  7007. if ($i == ($clen - 1)) {
  7008. $cw += $rbw * _MPDFK;
  7009. $contentB[(count($contentB) - 1)] .= 'R';
  7010. }
  7011. if ($c == ' ') {
  7012. $currContent .= $c;
  7013. $contentWidth += $cw;
  7014. continue;
  7015. }
  7016. // Paragraph INDENT
  7017. $WidthCorrection = 0;
  7018. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && isset($this->blk[$this->blklvl]['text_indent']) && ($lineCount == 0) && (!$is_table) && ($align != 'C')) {
  7019. $ti = $this->ConvertSize($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4
  7020. $WidthCorrection = ($ti * _MPDFK);
  7021. }
  7022. // OUTDENT
  7023. foreach ($this->objectbuffer AS $k => $objattr) { // mPDF 6 DOTTAB
  7024. if ($objattr['type'] == 'dottab') {
  7025. $WidthCorrection -= ($objattr['outdent'] * _MPDFK);
  7026. break;
  7027. }
  7028. }
  7029. // Added mPDF 3.0 Float DIV
  7030. $fpaddingR = 0;
  7031. $fpaddingL = 0;
  7032. /* -- CSS-FLOAT -- */
  7033. if (count($this->floatDivs)) {
  7034. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  7035. if ($r_exists) {
  7036. $fpaddingR = $r_width;
  7037. }
  7038. if ($l_exists) {
  7039. $fpaddingL = $l_width;
  7040. }
  7041. }
  7042. /* -- END CSS-FLOAT -- */
  7043. $usey = $this->y + 0.002;
  7044. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 0)) {
  7045. $usey += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  7046. }
  7047. /* -- CSS-IMAGE-FLOAT -- */
  7048. // If float exists at this level
  7049. if (isset($this->floatmargins['R']) && $usey <= $this->floatmargins['R']['y1'] && $usey >= $this->floatmargins['R']['y0'] && !$this->floatmargins['R']['skipline']) {
  7050. $fpaddingR += $this->floatmargins['R']['w'];
  7051. }
  7052. if (isset($this->floatmargins['L']) && $usey <= $this->floatmargins['L']['y1'] && $usey >= $this->floatmargins['L']['y0'] && !$this->floatmargins['L']['skipline']) {
  7053. $fpaddingL += $this->floatmargins['L']['w'];
  7054. }
  7055. /* -- END CSS-IMAGE-FLOAT -- */
  7056. // try adding another char
  7057. if (( $contentWidth + $cw > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) ) + 0.001)) {// 0.001 is to correct for deviations converting mm=>pts
  7058. // it won't fit, output what we already have
  7059. $lineCount++;
  7060. // contains any content that didn't make it into this print
  7061. $savedContent = '';
  7062. $savedContentB = '';
  7063. $savedOTLdata = array(); // mPDF 5.7.1
  7064. $savedFont = array();
  7065. $savedObj = array();
  7066. $savedPreOTLdata = array(); // mPDF 5.7.1
  7067. $savedPreContent = array();
  7068. $savedPreContentB = array();
  7069. $savedPreFont = array();
  7070. // mPDF 6
  7071. // New line-breaking algorithm
  7072. /////////////////////
  7073. // LINE BREAKING
  7074. /////////////////////
  7075. $breakfound = false;
  7076. $contentctr = count($content) - 1;
  7077. if ($this->usingCoreFont) {
  7078. $charctr = strlen($currContent);
  7079. } else {
  7080. $charctr = mb_strlen($currContent, $this->mb_enc);
  7081. }
  7082. $checkchar = $c;
  7083. $prevchar = $this->_getPrevChar($contentctr, $charctr, $content);
  7084. /* -- CJK-FONTS -- */
  7085. /////////////////////
  7086. // 1) CJK Overflowing a) punctuation or b) Oikomi
  7087. /////////////////////
  7088. // Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi
  7089. if ($CJKoverflow || $Oikomi) { // If flag already set
  7090. $CJKoverflow = false;
  7091. $Oikomi = false;
  7092. $breakfound = true;
  7093. }
  7094. if (!$this->usingCoreFont && !$breakfound && $this->checkCJK) {
  7095. // Get next/following character (in this chunk)
  7096. $followingchar = '';
  7097. if ($i < ($clen - 1)) {
  7098. if ($this->usingCoreFont) {
  7099. $followingchar = $s[$i + 1];
  7100. } else {
  7101. $followingchar = mb_substr($s, $i + 1, 1, $this->mb_enc);
  7102. }
  7103. }
  7104. /////////////////////
  7105. // 1a) Overflow punctuation
  7106. /////////////////////
  7107. if (preg_match("/[" . $this->pregCJKchars . "]/u", $prevchar) && preg_match("/[" . $this->CJKoverflow . "]/u", $checkchar) && $this->allowCJKorphans) {
  7108. // add character onto this line
  7109. $currContent .= $c;
  7110. $contentWidth += $cw;
  7111. $CJKoverflow = true; // Set flag
  7112. continue;
  7113. }
  7114. /////////////////////
  7115. // 1b) Try squeezing another character(s) onto this line = Oikomi, if character cannot end line
  7116. // or next character cannot start line (and not splitting CJK numerals)
  7117. /////////////////////
  7118. // NB otherwise it move lastchar(s) to next line to keep $c company = Oidashi, which is done below in standard way
  7119. elseif (preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) && $this->allowCJKorphans &&
  7120. (preg_match("/[" . $this->CJKleading . "]/u", $followingchar) || preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar)) &&
  7121. !preg_match("/[" . $this->CJKleading . "]/u", $checkchar) && !preg_match("/[" . $this->CJKfollowing . "]/u", $followingchar) &&
  7122. !(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $followingchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
  7123. // add character onto this line
  7124. $currContent .= $c;
  7125. $contentWidth += $cw;
  7126. $Oikomi = true; // Set flag
  7127. continue;
  7128. }
  7129. }
  7130. /* -- END CJK-FONTS -- */
  7131. /* -- HYPHENATION -- */
  7132. /////////////////////
  7133. // AUTOMATIC HYPHENATION
  7134. // 2) Automatic hyphen in current word (does not cross tags)
  7135. /////////////////////
  7136. if (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] == 1) {
  7137. $currWord = '';
  7138. // Look back and ahead to get current word
  7139. for ($ac = $charctr - 1; $ac >= 0; $ac--) {
  7140. if ($this->usingCoreFont) {
  7141. $addc = substr($currContent, $ac, 1);
  7142. } else {
  7143. $addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
  7144. }
  7145. if ($addc == ' ') {
  7146. break;
  7147. }
  7148. $currWord = $addc . $currWord;
  7149. }
  7150. $start = $ac + 1;
  7151. for ($ac = $i; $ac < ($clen - 1); $ac++) {
  7152. if ($this->usingCoreFont) {
  7153. $addc = substr($s, $ac, 1);
  7154. } else {
  7155. $addc = mb_substr($s, $ac, 1, $this->mb_enc);
  7156. }
  7157. if ($addc == ' ') {
  7158. break;
  7159. }
  7160. $currWord .= $addc;
  7161. }
  7162. $ptr = $this->hyphenateWord($currWord, $charctr - $start);
  7163. if ($ptr > -1) {
  7164. $breakfound = array($contentctr, $start + $ptr, $contentctr, $start + $ptr, 'hyphen');
  7165. }
  7166. }
  7167. /* -- END HYPHENATION -- */
  7168. // Search backwards to find first line-break opportunity
  7169. while ($breakfound == false && $prevchar !== false) {
  7170. $cutcontentctr = $contentctr;
  7171. $cutcharctr = $charctr;
  7172. $prevchar = $this->_moveToPrevChar($contentctr, $charctr, $content);
  7173. /////////////////////
  7174. // 3) Break at SPACE
  7175. /////////////////////
  7176. if ($prevchar == ' ') {
  7177. $breakfound = array($contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard');
  7178. }
  7179. /////////////////////
  7180. // 4) Break at U+200B in current word (Khmer, Lao & Thai Invisible word boundary, and Tibetan)
  7181. /////////////////////
  7182. elseif ($prevchar == "\xe2\x80\x8b") { // U+200B Zero-width Word Break
  7183. $breakfound = array($contentctr, $charctr, $cutcontentctr, $cutcharctr, 'discard');
  7184. }
  7185. /////////////////////
  7186. // 5) Break at Hard HYPHEN '-' or U+2010
  7187. /////////////////////
  7188. elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && ($prevchar == '-' || $prevchar == "\xe2\x80\x90")) {
  7189. // Don't break a URL
  7190. // Look back to get first part of current word
  7191. $checkw = '';
  7192. for ($ac = $charctr - 1; $ac >= 0; $ac--) {
  7193. if ($this->usingCoreFont) {
  7194. $addc = substr($currContent, $ac, 1);
  7195. } else {
  7196. $addc = mb_substr($currContent, $ac, 1, $this->mb_enc);
  7197. }
  7198. if ($addc == ' ') {
  7199. break;
  7200. }
  7201. $checkw = $addc . $checkw;
  7202. }
  7203. // Don't break if HyphenMinus AND (a URL or before a numeral or before a >)
  7204. if ((!preg_match('/(http:|ftp:|https:|www\.)/', $checkw) && $checkchar != '>' && !preg_match('/[0-9]/', $checkchar)) || $prevchar == "\xe2\x80\x90") {
  7205. $breakfound = array($cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut');
  7206. }
  7207. }
  7208. /////////////////////
  7209. // 6) Break at Soft HYPHEN (replace with hard hyphen)
  7210. /////////////////////
  7211. elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && !$this->usingCoreFont && $prevchar == "\xc2\xad") {
  7212. $breakfound = array($cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut');
  7213. $content[$contentctr] = mb_substr($content[$contentctr], 0, $charctr, $this->mb_enc) . '-' . mb_substr($content[$contentctr], $charctr + 1, mb_strlen($content[$contentctr]), $this->mb_enc);
  7214. if (!empty($cOTLdata[$contentctr])) {
  7215. $cOTLdata[$contentctr]['char_data'][$charctr] = array('bidi_class' => 9, 'uni' => 45);
  7216. $cOTLdata[$contentctr]['group'][$charctr] = 'C';
  7217. }
  7218. } elseif (isset($this->textparam['hyphens']) && $this->textparam['hyphens'] != 2 && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && $prevchar == chr(173)) {
  7219. $breakfound = array($cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut');
  7220. $content[$contentctr] = substr($content[$contentctr], 0, $charctr) . '-' . substr($content[$contentctr], $charctr + 1);
  7221. }
  7222. /* -- CJK-FONTS -- */
  7223. /////////////////////
  7224. // 7) Break at CJK characters (unless forbidden characters to end or start line)
  7225. // CJK Avoiding line break in the middle of numerals
  7226. /////////////////////
  7227. elseif (!$this->usingCoreFont && $this->checkCJK && preg_match("/[" . $this->pregCJKchars . "]/u", $checkchar) &&
  7228. !preg_match("/[" . $this->CJKfollowing . "]/u", $checkchar) && !preg_match("/[" . $this->CJKleading . "]/u", $prevchar) &&
  7229. !(preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $prevchar) && preg_match("/[0-9\x{ff10}-\x{ff19}]/u", $checkchar))) {
  7230. $breakfound = array($cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut');
  7231. }
  7232. /* -- END CJK-FONTS -- */
  7233. /////////////////////
  7234. // 8) Break at OBJECT (Break before all objects here - selected objects are moved forward to next line below e.g. dottab)
  7235. /////////////////////
  7236. if (isset($this->objectbuffer[$contentctr])) {
  7237. $breakfound = array($cutcontentctr, $cutcharctr, $cutcontentctr, $cutcharctr, 'cut');
  7238. }
  7239. $checkchar = $prevchar;
  7240. }
  7241. // If a line-break opportunity found:
  7242. if (is_array($breakfound)) {
  7243. $contentctr = $breakfound[0];
  7244. $charctr = $breakfound[1];
  7245. $cutcontentctr = $breakfound[2];
  7246. $cutcharctr = $breakfound[3];
  7247. $type = $breakfound[4];
  7248. // Cache chunks which are already processed, but now need to be passed on to the new line
  7249. for ($ix = count($content) - 1; $ix > $cutcontentctr; $ix--) {
  7250. // save and crop off any subsequent chunks
  7251. /* -- OTL -- */
  7252. if (!empty($sOTLdata)) {
  7253. $tmpOTL = array_pop($cOTLdata);
  7254. $savedPreOTLdata[] = $tmpOTL;
  7255. }
  7256. /* -- END OTL -- */
  7257. $savedPreContent[] = array_pop($content);
  7258. $savedPreContentB[] = array_pop($contentB);
  7259. $savedPreFont[] = array_pop($font);
  7260. }
  7261. // Next cache the part which will start the next line
  7262. if ($this->usingCoreFont) {
  7263. $savedPreContent[] = substr($content[$cutcontentctr], $cutcharctr);
  7264. } else {
  7265. $savedPreContent[] = mb_substr($content[$cutcontentctr], $cutcharctr, mb_strlen($content[$cutcontentctr]), $this->mb_enc);
  7266. }
  7267. $savedPreContentB[] = preg_replace('/L/', '', $contentB[$cutcontentctr]);
  7268. $savedPreFont[] = $font[$cutcontentctr];
  7269. /* -- OTL -- */
  7270. if (!empty($sOTLdata)) {
  7271. $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[$cutcontentctr], $cutcharctr, $cutcharctr);
  7272. }
  7273. /* -- END OTL -- */
  7274. // Finally adjust the Current content which ends this line
  7275. if ($cutcharctr == 0 && $type == 'discard') {
  7276. array_pop($content);
  7277. array_pop($contentB);
  7278. array_pop($font);
  7279. array_pop($cOTLdata);
  7280. }
  7281. $currContent = & $content[count($content) - 1];
  7282. if ($this->usingCoreFont) {
  7283. $currContent = substr($currContent, 0, $charctr);
  7284. } else {
  7285. $currContent = mb_substr($currContent, 0, $charctr, $this->mb_enc);
  7286. }
  7287. if (!empty($sOTLdata)) {
  7288. $savedPreOTLdata[] = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
  7289. }
  7290. if (strpos($contentB[(count($contentB) - 1)], 'R') !== false) { // ???
  7291. $contentB[count($content) - 1] = preg_replace('/R/', '', $contentB[count($content) - 1]); // ???
  7292. }
  7293. if ($type == 'hyphen') {
  7294. $currContent .= '-';
  7295. if (!empty($cOTLdata[(count($cOTLdata) - 1)])) {
  7296. $cOTLdata[(count($cOTLdata) - 1)]['char_data'][] = array('bidi_class' => 9, 'uni' => 45);
  7297. $cOTLdata[(count($cOTLdata) - 1)]['group'] .= 'C';
  7298. }
  7299. }
  7300. $savedContent = '';
  7301. $savedContentB = '';
  7302. $savedFont = array();
  7303. $savedOTLdata = array();
  7304. }
  7305. // If no line-break opportunity found - split at current position
  7306. // or - Next character ($c) is suitable to add as overhanging or squeezed punctuation, or Oikomi, as set above by:
  7307. // 1) CJK Overflowing a) punctuation or b) Oikomi
  7308. // in which case $breakfound==1 and NOT array
  7309. if (!is_array($breakfound)) {
  7310. $savedFont = $this->saveFont();
  7311. if (!empty($sOTLdata)) {
  7312. $savedOTLdata = $this->otl->splitOTLdata($cOTLdata[(count($cOTLdata) - 1)], mb_strlen($currContent, $this->mb_enc));
  7313. }
  7314. }
  7315. if ($content[count($content) - 1] == '' && !isset($this->objectbuffer[count($content) - 1])) {
  7316. array_pop($content);
  7317. array_pop($contentB);
  7318. array_pop($font);
  7319. array_pop($cOTLdata);
  7320. $currContent = & $content[count($content) - 1];
  7321. }
  7322. // Right Trim current content - including CJK space, and for OTLdata
  7323. // incl. CJK - strip CJK space at end of line &#x3000; = \xe3\x80\x80 = CJK space
  7324. $currContent = rtrim($currContent);
  7325. if ($this->checkCJK) {
  7326. $currContent = preg_replace("/\xe3\x80\x80$/", '', $currContent);
  7327. } // *CJK-FONTS*
  7328. /* -- OTL -- */
  7329. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  7330. $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], false, true); // NB also does U+3000
  7331. }
  7332. /* -- END OTL -- */
  7333. // Selected OBJECTS are moved forward to next line, unless they come before a space or U+200B (type='discard')
  7334. if (isset($this->objectbuffer[(count($content) - 1)]) && (!isset($type) || $type != 'discard')) {
  7335. $objtype = $this->objectbuffer[(count($content) - 1)]['type'];
  7336. if ($objtype == 'dottab' || $objtype == 'bookmark' || $objtype == 'indexentry' || $objtype == 'toc' || $objtype == 'annot') {
  7337. $savedObj = array_pop($this->objectbuffer);
  7338. }
  7339. }
  7340. // Decimal alignment (cancel if wraps to > 1 line)
  7341. if ($is_table && substr($align, 0, 1) == 'D') {
  7342. $align = substr($align, 2, 1);
  7343. }
  7344. $lineBox = array();
  7345. $this->_setInlineBlockHeights($lineBox, $stackHeight, $content, $font, $is_table);
  7346. // update $contentWidth since it has changed with cropping
  7347. $contentWidth = 0;
  7348. $inclCursive = false;
  7349. foreach ($content as $k => $chunk) {
  7350. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  7351. // LIST MARKERS
  7352. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker']) {
  7353. if ($this->objectbuffer[$k]['listmarkerposition'] != 'outside') {
  7354. $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * _MPDFK;
  7355. }
  7356. } else {
  7357. $contentWidth += $this->objectbuffer[$k]['OUTER-WIDTH'] * _MPDFK;
  7358. }
  7359. } elseif (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  7360. $this->restoreFont($font[$k], false);
  7361. if ($this->checkCJK && $k == count($content) - 1 && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $this->CJKforceend) {
  7362. // force-end overhang
  7363. $hanger = mb_substr($chunk, mb_strlen($chunk, $this->mb_enc) - 1, 1, $this->mb_enc);
  7364. // Probably ought to do something with char_data and GPOS in cOTLdata...
  7365. $content[$k] = $chunk = mb_substr($chunk, 0, mb_strlen($chunk, $this->mb_enc) - 1, $this->mb_enc);
  7366. }
  7367. // Soft Hyphens chr(173) + Replace NBSP with SPACE + Set inclcursive if includes CURSIVE TEXT
  7368. if (!$this->usingCoreFont) {
  7369. /* -- OTL -- */
  7370. if ((isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) || !empty($sOTLdata)) {
  7371. $this->otl->removeChar($chunk, $cOTLdata[$k], "\xc2\xad");
  7372. $this->otl->replaceSpace($chunk, $cOTLdata[$k]); // NBSP -> space
  7373. if (preg_match("/([" . $this->pregCURSchars . "])/u", $chunk)) {
  7374. $inclCursive = true;
  7375. }
  7376. $content[$k] = $chunk;
  7377. }
  7378. /* -- END OTL -- */ else { // *OTL*
  7379. $content[$k] = $chunk = str_replace("\xc2\xad", '', $chunk);
  7380. $content[$k] = $chunk = str_replace(chr(194) . chr(160), chr(32), $chunk);
  7381. } // *OTL*
  7382. } elseif ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  7383. $content[$k] = $chunk = str_replace(chr(173), '', $chunk);
  7384. $content[$k] = $chunk = str_replace(chr(160), chr(32), $chunk);
  7385. }
  7386. $contentWidth += $this->GetStringWidth($chunk, true, (isset($cOTLdata[$k]) ? $cOTLdata[$k] : false), $this->textvar) * _MPDFK; // mPDF 5.7.1
  7387. if (!empty($this->spanborddet)) {
  7388. if (isset($this->spanborddet['L']['w']) && strpos($contentB[$k], 'L') !== false)
  7389. $contentWidth += $this->spanborddet['L']['w'] * _MPDFK;
  7390. if (isset($this->spanborddet['R']['w']) && strpos($contentB[$k], 'R') !== false)
  7391. $contentWidth += $this->spanborddet['R']['w'] * _MPDFK;
  7392. }
  7393. }
  7394. }
  7395. $lastfontreqstyle = (isset($font[count($font) - 1]['ReqFontStyle']) ? $font[count($font) - 1]['ReqFontStyle'] : '');
  7396. $lastfontstyle = (isset($font[count($font) - 1]['style']) ? $font[count($font) - 1]['style'] : '');
  7397. if ($blockdir == 'ltr' && strpos($lastfontreqstyle, "I") !== false && strpos($lastfontstyle, "I") === false) { // Artificial italic
  7398. $lastitalic = $this->FontSize * 0.15 * _MPDFK;
  7399. } else {
  7400. $lastitalic = 0;
  7401. }
  7402. // NOW FORMAT THE LINE TO OUTPUT
  7403. if (!$table_draft) {
  7404. // DIRECTIONALITY RTL
  7405. $chunkorder = range(0, count($content) - 1); // mPDF 5.7
  7406. /* -- OTL -- */
  7407. // mPDF 6
  7408. if ($blockdir == 'rtl' || $this->biDirectional) {
  7409. $this->otl->_bidiReorder($chunkorder, $content, $cOTLdata, $blockdir);
  7410. // From this point on, $content and $cOTLdata may contain more elements (and re-ordered) compared to
  7411. // $this->objectbuffer and $font ($chunkorder contains the mapping)
  7412. }
  7413. /* -- END OTL -- */
  7414. // Remove any XAdvance from OTL data at end of line
  7415. foreach ($chunkorder AS $aord => $k) {
  7416. if (count($cOTLdata)) {
  7417. $this->restoreFont($font[$k], false);
  7418. // ...WriteFlowingBlock...
  7419. if ($aord == count($chunkorder) - 1 && isset($cOTLdata[$aord]['group'])) { // Last chunk on line
  7420. $nGPOS = strlen($cOTLdata[$aord]['group']) - 1; // Last character
  7421. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL']) || isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'])) {
  7422. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'])) {
  7423. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] * 1000 / $this->CurrentFont['unitsPerEm'];
  7424. } else {
  7425. $w = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] * 1000 / $this->CurrentFont['unitsPerEm'];
  7426. }
  7427. $w *= ($this->FontSize / 1000);
  7428. $contentWidth -= $w * _MPDFK;
  7429. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = 0;
  7430. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = 0;
  7431. }
  7432. // If last character has an XPlacement set, adjust width calculation, and add to XAdvance to account for it
  7433. if (isset($cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'])) {
  7434. $w = -$cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'] * 1000 / $this->CurrentFont['unitsPerEm'];
  7435. $w *= ($this->FontSize / 1000);
  7436. $contentWidth -= $w * _MPDFK;
  7437. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceL'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  7438. $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XAdvanceR'] = $cOTLdata[$aord]['GPOSinfo'][$nGPOS]['XPlacement'];
  7439. }
  7440. }
  7441. }
  7442. }
  7443. // JUSTIFICATION J
  7444. $jcharspacing = 0;
  7445. $jws = 0;
  7446. $nb_carac = 0;
  7447. $nb_spaces = 0;
  7448. $jkashida = 0;
  7449. // if it's justified, we need to find the char/word spacing (or if hanger $this->CJKforceend)
  7450. if (($align == 'J' && !$CJKoverflow) || (($contentWidth + $lastitalic > $maxWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) ) + 0.001) && (!$CJKoverflow || ($CJKoverflow && !$this->allowCJKoverflow))) || $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) { // 0.001 is to correct for deviations converting mm=>pts
  7451. // JUSTIFY J (Use character spacing)
  7452. // WORD SPACING
  7453. foreach ($chunkorder AS $aord => $k) { // mPDF 5.7
  7454. $chunk = $content[$aord];
  7455. if (!isset($this->objectbuffer[$k]) || (isset($this->objectbuffer[$k]) && !$this->objectbuffer[$k])) {
  7456. $nb_carac += mb_strlen($chunk, $this->mb_enc);
  7457. $nb_spaces += mb_substr_count($chunk, ' ', $this->mb_enc);
  7458. // Use GPOS OTL
  7459. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
  7460. if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
  7461. $nb_carac -= substr_count($cOTLdata[$aord]['group'], 'M');
  7462. }
  7463. }
  7464. } else {
  7465. $nb_carac ++;
  7466. } // mPDF 6 allow spacing for inline object
  7467. }
  7468. // GetJSpacing adds kashida spacing to GPOSinfo if appropriate for Font
  7469. list($jcharspacing, $jws, $jkashida) = $this->GetJspacing($nb_carac, $nb_spaces, ($maxWidth - $lastitalic - $contentWidth - $WidthCorrection - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) )), $inclCursive, $cOTLdata);
  7470. }
  7471. // WORD SPACING
  7472. $empty = $maxWidth - $lastitalic - $WidthCorrection - $contentWidth - (($this->cMarginL + $this->cMarginR) * _MPDFK) - ($paddingL + $paddingR + (($fpaddingL + $fpaddingR) * _MPDFK) );
  7473. $empty -= ($jcharspacing * ($nb_carac - 1)); // mPDF 6 nb_carac MINUS 1
  7474. $empty -= ($jws * $nb_spaces);
  7475. $empty -= ($jkashida);
  7476. $empty /= _MPDFK;
  7477. $b = ''; //do not use borders
  7478. // Get PAGEBREAK TO TEST for height including the top border/padding
  7479. $check_h = max($this->divheight, $stackHeight);
  7480. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blklvl > 0) && ($lineCount == 1) && (!$is_table)) {
  7481. $check_h += ($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['border_top']['w']);
  7482. }
  7483. if ($this->ColActive && $check_h > ($this->PageBreakTrigger - $this->y0)) {
  7484. $this->SetCol($this->NbCol - 1);
  7485. }
  7486. // PAGEBREAK
  7487. // 'If' below used in order to fix "first-line of other page with justify on" bug
  7488. if (!$is_table && ($this->y + $check_h) > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
  7489. $bak_x = $this->x; //Current X position
  7490. // WORD SPACING
  7491. $ws = $this->ws; //Word Spacing
  7492. $charspacing = $this->charspacing; //Character Spacing
  7493. $this->ResetSpacing();
  7494. $this->AddPage($this->CurOrientation);
  7495. $this->x = $bak_x;
  7496. // Added to correct for OddEven Margins
  7497. $currentx += $this->MarginCorrection;
  7498. $this->x += $this->MarginCorrection;
  7499. // WORD SPACING
  7500. $this->SetSpacing($charspacing, $ws);
  7501. }
  7502. if ($this->kwt && !$is_table) { // mPDF 5.7+
  7503. $this->printkwtbuffer();
  7504. $this->kwt = false;
  7505. }
  7506. /* -- COLUMNS -- */
  7507. // COLS
  7508. // COLUMN CHANGE
  7509. if ($this->CurrCol != $oldcolumn) {
  7510. $currentx += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7511. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7512. $oldcolumn = $this->CurrCol;
  7513. }
  7514. if ($this->ColActive && !$is_table) {
  7515. $this->breakpoints[$this->CurrCol][] = $this->y;
  7516. } // *COLUMNS*
  7517. /* -- END COLUMNS -- */
  7518. // TOP MARGIN
  7519. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($this->blk[$this->blklvl]['margin_top']) && ($lineCount == 1) && (!$is_table)) {
  7520. $this->DivLn($this->blk[$this->blklvl]['margin_top'], $this->blklvl - 1, true, $this->blk[$this->blklvl]['margin_collapse']);
  7521. if ($this->ColActive) {
  7522. $this->breakpoints[$this->CurrCol][] = $this->y;
  7523. } // *COLUMNS*
  7524. }
  7525. // Update y0 for top of block (used to paint border)
  7526. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table)) {
  7527. $this->blk[$this->blklvl]['y0'] = $this->y;
  7528. $this->blk[$this->blklvl]['startpage'] = $this->page;
  7529. if ($this->blk[$this->blklvl]['float']) {
  7530. $this->blk[$this->blklvl]['float_start_y'] = $this->y;
  7531. }
  7532. }
  7533. // TOP PADDING and BORDER spacing/fill
  7534. if (($newblock) && ($blockstate == 1 || $blockstate == 3) && (($this->blk[$this->blklvl]['padding_top']) || ($this->blk[$this->blklvl]['border_top'])) && ($lineCount == 1) && (!$is_table)) {
  7535. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  7536. $this->DivLn($this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'], -3, true, false, 1);
  7537. if ($this->ColActive) {
  7538. $this->breakpoints[$this->CurrCol][] = $this->y;
  7539. } // *COLUMNS*
  7540. }
  7541. $arraysize = count($chunkorder);
  7542. $margins = ($this->cMarginL + $this->cMarginR) + ($ipaddingL + $ipaddingR + $fpaddingR + $fpaddingR );
  7543. // PAINT BACKGROUND FOR THIS LINE
  7544. if (!$is_table) {
  7545. $this->DivLn($stackHeight, $this->blklvl, false);
  7546. } // false -> don't advance y
  7547. $this->x = $currentx + $this->cMarginL + $ipaddingL + $fpaddingL;
  7548. if ($align == 'R') {
  7549. $this->x += $empty;
  7550. } elseif ($align == 'C') {
  7551. $this->x += ($empty / 2);
  7552. }
  7553. // Paragraph INDENT
  7554. if (isset($this->blk[$this->blklvl]['text_indent']) && ($newblock) && ($blockstate == 1 || $blockstate == 3) && ($lineCount == 1) && (!$is_table) && ($blockdir != 'rtl') && ($align != 'C')) {
  7555. $ti = $this->ConvertSize($this->blk[$this->blklvl]['text_indent'], $this->blk[$this->blklvl]['inner_width'], $this->blk[$this->blklvl]['InlineProperties']['size'], false); // mPDF 5.7.4
  7556. $this->x += $ti;
  7557. }
  7558. // BIDI magic_reverse moved upwards from here
  7559. foreach ($chunkorder AS $aord => $k) { // mPDF 5.7
  7560. $chunk = $content[$aord];
  7561. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]) {
  7562. $xadj = $this->x - $this->objectbuffer[$k]['OUTER-X'];
  7563. $this->objectbuffer[$k]['OUTER-X'] += $xadj;
  7564. $this->objectbuffer[$k]['BORDER-X'] += $xadj;
  7565. $this->objectbuffer[$k]['INNER-X'] += $xadj;
  7566. if ($this->objectbuffer[$k]['type'] == 'listmarker') {
  7567. $this->objectbuffer[$k]['lineBox'] = $lineBox[-1]; // Block element details for glyph-origin
  7568. }
  7569. $yadj = $this->y - $this->objectbuffer[$k]['OUTER-Y'];
  7570. if ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  7571. $this->objectbuffer[$k]['lineBox'] = $lineBox[$k]; // element details for glyph-origin
  7572. }
  7573. if ($this->objectbuffer[$k]['type'] != 'dottab') { // mPDF 6 DOTTAB
  7574. $yadj += $lineBox[$k]['top'];
  7575. }
  7576. $this->objectbuffer[$k]['OUTER-Y'] += $yadj;
  7577. $this->objectbuffer[$k]['BORDER-Y'] += $yadj;
  7578. $this->objectbuffer[$k]['INNER-Y'] += $yadj;
  7579. }
  7580. $this->restoreFont($font[$k]); // mPDF 5.7
  7581. $this->SetSpacing(($this->fixedlSpacing * _MPDFK) + $jcharspacing, ($this->fixedlSpacing + $this->minwSpacing) * _MPDFK + $jws);
  7582. // Now unset these values so they don't influence GetStringwidth below or in fn. Cell
  7583. $this->fixedlSpacing = false;
  7584. $this->minwSpacing = 0;
  7585. $save_vis = $this->visibility;
  7586. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->textparam['visibility'] != $this->visibility) {
  7587. $this->SetVisibility($this->textparam['visibility']);
  7588. }
  7589. // *********** SPAN BACKGROUND COLOR ***************** //
  7590. if ($this->spanbgcolor) {
  7591. $cor = $this->spanbgcolorarray;
  7592. $this->SetFColor($cor);
  7593. $save_fill = $fill;
  7594. $spanfill = 1;
  7595. $fill = 1;
  7596. }
  7597. if (!empty($this->spanborddet)) {
  7598. if (strpos($contentB[$k], 'L') !== false)
  7599. $this->x += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  7600. if (strpos($contentB[$k], 'L') === false)
  7601. $this->spanborddet['L']['s'] = $this->spanborddet['L']['w'] = 0;
  7602. if (strpos($contentB[$k], 'R') === false)
  7603. $this->spanborddet['R']['s'] = $this->spanborddet['R']['w'] = 0;
  7604. }
  7605. // WORD SPACING
  7606. // StringWidth this time includes any kashida spacing
  7607. $stringWidth = $this->GetStringWidth($chunk, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, true);
  7608. $nch = mb_strlen($chunk, $this->mb_enc);
  7609. // Use GPOS OTL
  7610. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0xFF)) {
  7611. if (isset($cOTLdata[$aord]['group']) && $cOTLdata[$aord]['group']) {
  7612. $nch -= substr_count($cOTLdata[$aord]['group'], 'M');
  7613. }
  7614. }
  7615. $stringWidth += ( $this->charspacing * $nch / _MPDFK );
  7616. $stringWidth += ( $this->ws * mb_substr_count($chunk, ' ', $this->mb_enc) / _MPDFK );
  7617. if (isset($this->objectbuffer[$k])) {
  7618. // LIST MARKERS // mPDF 6 Lists
  7619. if ($this->objectbuffer[$k]['type'] == 'image' && isset($this->objectbuffer[$k]['listmarker']) && $this->objectbuffer[$k]['listmarker'] && $this->objectbuffer[$k]['listmarkerposition'] == 'outside') {
  7620. $stringWidth = 0;
  7621. } else {
  7622. $stringWidth = $this->objectbuffer[$k]['OUTER-WIDTH'];
  7623. }
  7624. }
  7625. if ($stringWidth == 0) {
  7626. $stringWidth = 0.000001;
  7627. }
  7628. if ($aord == $arraysize - 1) {
  7629. $stringWidth -= ( $this->charspacing / _MPDFK );
  7630. if ($this->checkCJK && $CJKoverflow && $align == 'J' && $this->allowCJKoverflow && $hanger && $this->CJKforceend) {
  7631. // force-end overhang
  7632. $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));
  7633. $this->Cell($this->GetStringWidth($hanger), $stackHeight, $hanger, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false));
  7634. } else {
  7635. $this->Cell($stringWidth, $stackHeight, $chunk, '', 1, '', $fill, $this->HREF, $currentx, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); //mono-style line or last part (skips line)
  7636. }
  7637. } else
  7638. $this->Cell($stringWidth, $stackHeight, $chunk, '', 0, '', $fill, $this->HREF, 0, 0, 0, 'M', $fill, true, (isset($cOTLdata[$aord]) ? $cOTLdata[$aord] : false), $this->textvar, (isset($lineBox[$k]) ? $lineBox[$k] : false)); //first or middle part
  7639. if (!empty($this->spanborddet)) {
  7640. if (strpos($contentB[$k], 'R') !== false && $aord != $arraysize - 1)
  7641. $this->x += $this->spanborddet['R']['w'];
  7642. }
  7643. // *********** SPAN BACKGROUND COLOR OFF - RESET BLOCK BGCOLOR ***************** //
  7644. if (isset($spanfill) && $spanfill) {
  7645. $fill = $save_fill;
  7646. $spanfill = 0;
  7647. if ($fill) {
  7648. $this->SetFColor($bcor);
  7649. }
  7650. }
  7651. if (isset($this->textparam['visibility']) && $this->textparam['visibility'] && $this->visibility != $save_vis) {
  7652. $this->SetVisibility($save_vis);
  7653. }
  7654. }
  7655. } elseif ($table_draft) {
  7656. $this->y += $stackHeight;
  7657. }
  7658. if (!$is_table) {
  7659. $this->maxPosR = max($this->maxPosR, ($this->w - $this->rMargin - $this->blk[$this->blklvl]['outer_right_margin']));
  7660. $this->maxPosL = min($this->maxPosL, ($this->lMargin + $this->blk[$this->blklvl]['outer_left_margin']));
  7661. }
  7662. // move on to the next line, reset variables, tack on saved content and current char
  7663. if (!$table_draft)
  7664. $this->printobjectbuffer($is_table, $blockdir);
  7665. $this->objectbuffer = array();
  7666. /* -- CSS-IMAGE-FLOAT -- */
  7667. // Update values if set to skipline
  7668. if ($this->floatmargins) {
  7669. $this->_advanceFloatMargins();
  7670. }
  7671. /* -- END CSS-IMAGE-FLOAT -- */
  7672. // Reset lineheight
  7673. $stackHeight = $this->divheight;
  7674. $valign = 'M';
  7675. $font = array();
  7676. $content = array();
  7677. $contentB = array();
  7678. $cOTLdata = array(); // mPDF 5.7.1
  7679. $contentWidth = 0;
  7680. if (!empty($savedObj)) {
  7681. $this->objectbuffer[] = $savedObj;
  7682. $font[] = $savedFont;
  7683. $content[] = '';
  7684. $contentB[] = '';
  7685. $cOTLdata[] = array(); // mPDF 5.7.1
  7686. $contentWidth += $savedObj['OUTER-WIDTH'] * _MPDFK;
  7687. }
  7688. if (count($savedPreContent) > 0) {
  7689. for ($ix = count($savedPreContent) - 1; $ix >= 0; $ix--) {
  7690. $font[] = $savedPreFont[$ix];
  7691. $content[] = $savedPreContent[$ix];
  7692. $contentB[] = $savedPreContentB[$ix];
  7693. if (!empty($sOTLdata)) {
  7694. $cOTLdata[] = $savedPreOTLdata[$ix];
  7695. }
  7696. $this->restoreFont($savedPreFont[$ix]);
  7697. $lbw = $rbw = 0; // Border widths
  7698. if (!empty($this->spanborddet)) {
  7699. $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  7700. $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  7701. }
  7702. if ($ix > 0) {
  7703. $contentWidth += $this->GetStringWidth($savedPreContent[$ix], true, (isset($savedPreOTLdata[$ix]) ? $savedPreOTLdata[$ix] : false), $this->textvar) * _MPDFK; // mPDF 5.7.1
  7704. if (strpos($savedPreContentB[$ix], 'L') !== false)
  7705. $contentWidth += $lbw;
  7706. if (strpos($savedPreContentB[$ix], 'R') !== false)
  7707. $contentWidth += $rbw;
  7708. }
  7709. }
  7710. $savedPreContent = array();
  7711. $savedPreContentB = array();
  7712. $savedPreOTLdata = array(); // mPDF 5.7.1
  7713. $savedPreFont = array();
  7714. $content[(count($content) - 1)] .= $c;
  7715. }
  7716. else {
  7717. $font[] = $savedFont;
  7718. $content[] = $savedContent . $c;
  7719. $contentB[] = $savedContentB;
  7720. $cOTLdata[] = $savedOTLdata; // mPDF 5.7.1
  7721. }
  7722. $currContent = & $content[(count($content) - 1)];
  7723. $this->restoreFont($font[(count($font) - 1)]); // mPDF 6.0
  7724. /* -- CJK-FONTS -- */
  7725. // CJK - strip CJK space at start of line
  7726. // &#x3000; = \xe3\x80\x80 = CJK space
  7727. if ($this->checkCJK && $currContent == "\xe3\x80\x80") {
  7728. $currContent = '';
  7729. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  7730. $this->otl->trimOTLdata($cOTLdata[count($cOTLdata) - 1], true, false); // left trim U+3000
  7731. }
  7732. }
  7733. /* -- END CJK-FONTS -- */
  7734. $lbw = $rbw = 0; // Border widths
  7735. if (!empty($this->spanborddet)) {
  7736. $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  7737. $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  7738. }
  7739. $contentWidth += $this->GetStringWidth($currContent, false, (isset($cOTLdata[(count($cOTLdata) - 1)]) ? $cOTLdata[(count($cOTLdata) - 1)] : false), $this->textvar) * _MPDFK; // mPDF 5.7.1
  7740. if (strpos($savedContentB, 'L') !== false)
  7741. $contentWidth += $lbw;
  7742. $CJKoverflow = false;
  7743. $hanger = '';
  7744. }
  7745. // another character will fit, so add it on
  7746. else {
  7747. $contentWidth += $cw;
  7748. $currContent .= $c;
  7749. }
  7750. }
  7751. unset($content);
  7752. unset($contentB);
  7753. }
  7754. //----------------------END OF FLOWING BLOCK------------------------------------//
  7755. /* -- CSS-IMAGE-FLOAT -- */
  7756. // Update values if set to skipline
  7757. function _advanceFloatMargins()
  7758. {
  7759. // Update floatmargins - L
  7760. if (isset($this->floatmargins['L']) && $this->floatmargins['L']['skipline'] && $this->floatmargins['L']['y0'] != $this->y) {
  7761. $yadj = $this->y - $this->floatmargins['L']['y0'];
  7762. $this->floatmargins['L']['y0'] = $this->y;
  7763. $this->floatmargins['L']['y1'] += $yadj;
  7764. // Update objattr in floatbuffer
  7765. if ($this->floatbuffer[$this->floatmargins['L']['id']]['border_left']['w']) {
  7766. $this->floatbuffer[$this->floatmargins['L']['id']]['BORDER-Y'] += $yadj;
  7767. }
  7768. $this->floatbuffer[$this->floatmargins['L']['id']]['INNER-Y'] += $yadj;
  7769. $this->floatbuffer[$this->floatmargins['L']['id']]['OUTER-Y'] += $yadj;
  7770. // Unset values
  7771. $this->floatbuffer[$this->floatmargins['L']['id']]['skipline'] = false;
  7772. $this->floatmargins['L']['skipline'] = false;
  7773. $this->floatmargins['L']['id'] = '';
  7774. }
  7775. // Update floatmargins - R
  7776. if (isset($this->floatmargins['R']) && $this->floatmargins['R']['skipline'] && $this->floatmargins['R']['y0'] != $this->y) {
  7777. $yadj = $this->y - $this->floatmargins['R']['y0'];
  7778. $this->floatmargins['R']['y0'] = $this->y;
  7779. $this->floatmargins['R']['y1'] += $yadj;
  7780. // Update objattr in floatbuffer
  7781. if ($this->floatbuffer[$this->floatmargins['R']['id']]['border_left']['w']) {
  7782. $this->floatbuffer[$this->floatmargins['R']['id']]['BORDER-Y'] += $yadj;
  7783. }
  7784. $this->floatbuffer[$this->floatmargins['R']['id']]['INNER-Y'] += $yadj;
  7785. $this->floatbuffer[$this->floatmargins['R']['id']]['OUTER-Y'] += $yadj;
  7786. // Unset values
  7787. $this->floatbuffer[$this->floatmargins['R']['id']]['skipline'] = false;
  7788. $this->floatmargins['R']['skipline'] = false;
  7789. $this->floatmargins['R']['id'] = '';
  7790. }
  7791. }
  7792. /* -- END CSS-IMAGE-FLOAT -- */
  7793. /* -- END HTML-CSS -- */
  7794. function _SetTextRendering($mode)
  7795. {
  7796. if (!(($mode == 0) || ($mode == 1) || ($mode == 2)))
  7797. throw new MpdfException("Text rendering mode should be 0, 1 or 2 (value : $mode)");
  7798. $tr = ($mode . ' Tr');
  7799. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
  7800. $this->_out($tr);
  7801. }
  7802. $this->pageoutput[$this->page]['TextRendering'] = $tr;
  7803. }
  7804. function SetTextOutline($params = array())
  7805. {
  7806. if (isset($params['outline-s']) && $params['outline-s']) {
  7807. $this->SetLineWidth($params['outline-WIDTH']);
  7808. $this->SetDColor($params['outline-COLOR']);
  7809. $tr = ('2 Tr');
  7810. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
  7811. $this->_out($tr);
  7812. }
  7813. $this->pageoutput[$this->page]['TextRendering'] = $tr;
  7814. } else { //Now resets all values
  7815. $this->SetLineWidth(0.2);
  7816. $this->SetDColor($this->ConvertColor(0));
  7817. $this->_SetTextRendering(0);
  7818. $tr = ('0 Tr');
  7819. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['TextRendering']) && $this->pageoutput[$this->page]['TextRendering'] != $tr) || !isset($this->pageoutput[$this->page]['TextRendering']))) {
  7820. $this->_out($tr);
  7821. }
  7822. $this->pageoutput[$this->page]['TextRendering'] = $tr;
  7823. }
  7824. }
  7825. function Image($file, $x, $y, $w = 0, $h = 0, $type = '', $link = '', $paint = true, $constrain = true, $watermark = false, $shownoimg = true, $allowvector = true)
  7826. {
  7827. $orig_srcpath = $file;
  7828. $this->GetFullPath($file);
  7829. $info = $this->_getImage($file, true, $allowvector, $orig_srcpath);
  7830. if (!$info && $paint) {
  7831. $info = $this->_getImage($this->noImageFile);
  7832. if ($info) {
  7833. $file = $this->noImageFile;
  7834. $w = ($info['w'] * (25.4 / $this->dpi)); // 14 x 16px
  7835. $h = ($info['h'] * (25.4 / $this->dpi)); // 14 x 16px
  7836. }
  7837. }
  7838. if (!$info)
  7839. return false;
  7840. //Automatic width and height calculation if needed
  7841. if ($w == 0 and $h == 0) {
  7842. /* -- IMAGES-WMF -- */
  7843. if ($info['type'] == 'wmf') {
  7844. // WMF units are twips (1/20pt)
  7845. // divide by 20 to get points
  7846. // divide by k to get user units
  7847. $w = abs($info['w']) / (20 * _MPDFK);
  7848. $h = abs($info['h']) / (20 * _MPDFK);
  7849. } else
  7850. /* -- END IMAGES-WMF -- */
  7851. if ($info['type'] == 'svg') {
  7852. // returned SVG units are pts
  7853. // divide by k to get user units (mm)
  7854. $w = abs($info['w']) / _MPDFK;
  7855. $h = abs($info['h']) / _MPDFK;
  7856. } else {
  7857. //Put image at default image dpi
  7858. $w = ($info['w'] / _MPDFK) * (72 / $this->img_dpi);
  7859. $h = ($info['h'] / _MPDFK) * (72 / $this->img_dpi);
  7860. }
  7861. }
  7862. if ($w == 0)
  7863. $w = abs($h * $info['w'] / $info['h']);
  7864. if ($h == 0)
  7865. $h = abs($w * $info['h'] / $info['w']);
  7866. /* -- WATERMARK -- */
  7867. if ($watermark) {
  7868. $maxw = $this->w;
  7869. $maxh = $this->h;
  7870. // Size = D PF or array
  7871. if (is_array($this->watermark_size)) {
  7872. $w = $this->watermark_size[0];
  7873. $h = $this->watermark_size[1];
  7874. } elseif (!is_string($this->watermark_size)) {
  7875. $maxw -= $this->watermark_size * 2;
  7876. $maxh -= $this->watermark_size * 2;
  7877. $w = $maxw;
  7878. $h = abs($w * $info['h'] / $info['w']);
  7879. if ($h > $maxh) {
  7880. $h = $maxh;
  7881. $w = abs($h * $info['w'] / $info['h']);
  7882. }
  7883. } elseif ($this->watermark_size == 'F') {
  7884. if ($this->ColActive) {
  7885. $maxw = $this->w - ($this->DeflMargin + $this->DefrMargin);
  7886. } else {
  7887. $maxw = $this->pgwidth;
  7888. }
  7889. $maxh = $this->h - ($this->tMargin + $this->bMargin);
  7890. $w = $maxw;
  7891. $h = abs($w * $info['h'] / $info['w']);
  7892. if ($h > $maxh) {
  7893. $h = $maxh;
  7894. $w = abs($h * $info['w'] / $info['h']);
  7895. }
  7896. } elseif ($this->watermark_size == 'P') { // Default P
  7897. $w = $maxw;
  7898. $h = abs($w * $info['h'] / $info['w']);
  7899. if ($h > $maxh) {
  7900. $h = $maxh;
  7901. $w = abs($h * $info['w'] / $info['h']);
  7902. }
  7903. }
  7904. // Automatically resize to maximum dimensions of page if too large
  7905. if ($w > $maxw) {
  7906. $w = $maxw;
  7907. $h = abs($w * $info['h'] / $info['w']);
  7908. }
  7909. if ($h > $maxh) {
  7910. $h = $maxh;
  7911. $w = abs($h * $info['w'] / $info['h']);
  7912. }
  7913. // Position
  7914. if (is_array($this->watermark_pos)) {
  7915. $x = $this->watermark_pos[0];
  7916. $y = $this->watermark_pos[1];
  7917. } elseif ($this->watermark_pos == 'F') { // centred on printable area
  7918. if ($this->ColActive) { // *COLUMNS*
  7919. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) {
  7920. $xadj = $this->DeflMargin - $this->DefrMargin;
  7921. } // *COLUMNS*
  7922. else {
  7923. $xadj = 0;
  7924. } // *COLUMNS*
  7925. $x = ($this->DeflMargin - $xadj + ($this->w - ($this->DeflMargin + $this->DefrMargin)) / 2) - ($w / 2); // *COLUMNS*
  7926. } // *COLUMNS*
  7927. else { // *COLUMNS*
  7928. $x = ($this->lMargin + ($this->pgwidth) / 2) - ($w / 2);
  7929. } // *COLUMNS*
  7930. $y = ($this->tMargin + ($this->h - ($this->tMargin + $this->bMargin)) / 2) - ($h / 2);
  7931. } else { // default P - centred on whole page
  7932. $x = ($this->w / 2) - ($w / 2);
  7933. $y = ($this->h / 2) - ($h / 2);
  7934. }
  7935. /* -- IMAGES-WMF -- */
  7936. if ($info['type'] == 'wmf') {
  7937. $sx = $w * _MPDFK / $info['w'];
  7938. $sy = -$h * _MPDFK / $info['h'];
  7939. $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * _MPDFK - $sx * $info['x'], (($this->h - $y) * _MPDFK) - $sy * $info['y'], $info['i']);
  7940. } else
  7941. /* -- END IMAGES-WMF -- */
  7942. if ($info['type'] == 'svg') {
  7943. $sx = $w * _MPDFK / $info['w'];
  7944. $sy = -$h * _MPDFK / $info['h'];
  7945. $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * _MPDFK - $sx * $info['x'], (($this->h - $y) * _MPDFK) - $sy * $info['y'], $info['i']);
  7946. } else {
  7947. $outstring = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $w * _MPDFK, $h * _MPDFK, $x * _MPDFK, ($this->h - ($y + $h)) * _MPDFK, $info['i']);
  7948. }
  7949. if ($this->watermarkImgBehind) {
  7950. $outstring = $this->watermarkImgAlpha . "\n" . $outstring . "\n" . $this->SetAlpha(1, 'Normal', true) . "\n";
  7951. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', "\n" . $outstring . "\n" . '\\1', $this->pages[$this->page]);
  7952. } else {
  7953. $this->_out($outstring);
  7954. }
  7955. return 0;
  7956. } // end of IF watermark
  7957. /* -- END WATERMARK -- */
  7958. if ($constrain) {
  7959. // Automatically resize to maximum dimensions of page if too large
  7960. if (isset($this->blk[$this->blklvl]['inner_width']) && $this->blk[$this->blklvl]['inner_width']) {
  7961. $maxw = $this->blk[$this->blklvl]['inner_width'];
  7962. } else {
  7963. $maxw = $this->pgwidth;
  7964. }
  7965. if ($w > $maxw) {
  7966. $w = $maxw;
  7967. $h = abs($w * $info['h'] / $info['w']);
  7968. }
  7969. if ($h > $this->h - ($this->tMargin + $this->bMargin + 1)) { // see below - +10 to avoid drawing too close to border of page
  7970. $h = $this->h - ($this->tMargin + $this->bMargin + 1);
  7971. if ($this->fullImageHeight) {
  7972. $h = $this->fullImageHeight;
  7973. }
  7974. $w = abs($h * $info['w'] / $info['h']);
  7975. }
  7976. //Avoid drawing out of the paper(exceeding width limits).
  7977. //if ( ($x + $w) > $this->fw ) {
  7978. if (($x + $w) > $this->w) {
  7979. $x = $this->lMargin;
  7980. $y += 5;
  7981. }
  7982. $changedpage = false;
  7983. $oldcolumn = $this->CurrCol;
  7984. //Avoid drawing out of the page.
  7985. if ($y + $h > $this->PageBreakTrigger and ! $this->InFooter and $this->AcceptPageBreak()) {
  7986. $this->AddPage($this->CurOrientation);
  7987. // Added to correct for OddEven Margins
  7988. $x = $x + $this->MarginCorrection;
  7989. $y = $this->tMargin; // mPDF 5.7.3
  7990. $changedpage = true;
  7991. }
  7992. /* -- COLUMNS -- */
  7993. // COLS
  7994. // COLUMN CHANGE
  7995. if ($this->CurrCol != $oldcolumn) {
  7996. $y = $this->y0;
  7997. $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7998. $this->x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  7999. }
  8000. /* -- END COLUMNS -- */
  8001. } // end of IF constrain
  8002. /* -- IMAGES-WMF -- */
  8003. if ($info['type'] == 'wmf') {
  8004. $sx = $w * _MPDFK / $info['w'];
  8005. $sy = -$h * _MPDFK / $info['h'];
  8006. $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * _MPDFK - $sx * $info['x'], (($this->h - $y) * _MPDFK) - $sy * $info['y'], $info['i']);
  8007. } else
  8008. /* -- END IMAGES-WMF -- */
  8009. if ($info['type'] == 'svg') {
  8010. $sx = $w * _MPDFK / $info['w'];
  8011. $sy = -$h * _MPDFK / $info['h'];
  8012. $outstring = sprintf('q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q', $sx, $sy, $x * _MPDFK - $sx * $info['x'], (($this->h - $y) * _MPDFK) - $sy * $info['y'], $info['i']);
  8013. } else {
  8014. $outstring = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /I%d Do Q", $w * _MPDFK, $h * _MPDFK, $x * _MPDFK, ($this->h - ($y + $h)) * _MPDFK, $info['i']);
  8015. }
  8016. if ($paint) {
  8017. $this->_out($outstring);
  8018. if ($link)
  8019. $this->Link($x, $y, $w, $h, $link);
  8020. // Avoid writing text on top of the image. // THIS WAS OUTSIDE THE if ($paint) bit!!!!!!!!!!!!!!!!
  8021. $this->y = $y + $h;
  8022. }
  8023. //Return width-height array
  8024. $sizesarray['WIDTH'] = $w;
  8025. $sizesarray['HEIGHT'] = $h;
  8026. $sizesarray['X'] = $x; //Position before painting image
  8027. $sizesarray['Y'] = $y; //Position before painting image
  8028. $sizesarray['OUTPUT'] = $outstring;
  8029. $sizesarray['IMAGE_ID'] = $info['i'];
  8030. $sizesarray['itype'] = $info['type'];
  8031. $sizesarray['set-dpi'] = (isset($info['set-dpi']) ? $info['set-dpi'] : 0);
  8032. return $sizesarray;
  8033. }
  8034. //=============================================================
  8035. //=============================================================
  8036. //=============================================================
  8037. //=============================================================
  8038. //=============================================================
  8039. /* -- HTML-CSS -- */
  8040. function _getObjAttr($t)
  8041. {
  8042. $c = explode("\xbb\xa4\xac", $t, 2);
  8043. $c = explode(",", $c[1], 2);
  8044. foreach ($c as $v) {
  8045. $v = explode("=", $v, 2);
  8046. $sp[$v[0]] = $v[1];
  8047. }
  8048. return (unserialize($sp['objattr']));
  8049. }
  8050. function inlineObject($type, $x, $y, $objattr, $Lmargin, $widthUsed, $maxWidth, $lineHeight, $paint = false, $is_table = false)
  8051. {
  8052. if ($is_table) {
  8053. $k = $this->shrin_k;
  8054. } else {
  8055. $k = 1;
  8056. }
  8057. // NB $x is only used when paint=true
  8058. // Lmargin not used
  8059. $w = 0;
  8060. if (isset($objattr['width'])) {
  8061. $w = $objattr['width'] / $k;
  8062. }
  8063. $h = 0;
  8064. if (isset($objattr['height'])) {
  8065. $h = abs($objattr['height'] / $k);
  8066. }
  8067. $widthLeft = $maxWidth - $widthUsed;
  8068. $maxHeight = $this->h - ($this->tMargin + $this->bMargin + 10);
  8069. if ($this->fullImageHeight) {
  8070. $maxHeight = $this->fullImageHeight;
  8071. }
  8072. // For Images
  8073. if (isset($objattr['border_left'])) {
  8074. $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']) / $k;
  8075. $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']) / $k;
  8076. if ($type == 'image' || $type == 'barcode' || $type == 'textcircle') {
  8077. $extraWidth += ($objattr['padding_left'] + $objattr['padding_right']) / $k;
  8078. $extraHeight += ($objattr['padding_top'] + $objattr['padding_bottom']) / $k;
  8079. }
  8080. }
  8081. if (!isset($objattr['vertical-align'])) {
  8082. if ($objattr['type'] == 'select') {
  8083. $objattr['vertical-align'] = 'M';
  8084. } else {
  8085. $objattr['vertical-align'] = 'BS';
  8086. }
  8087. } // mPDF 6
  8088. if ($type == 'image' || (isset($objattr['subtype']) && $objattr['subtype'] == 'IMAGE')) {
  8089. if (isset($objattr['itype']) && ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg')) {
  8090. $file = $objattr['file'];
  8091. $info = $this->formobjects[$file];
  8092. } elseif (isset($objattr['file'])) {
  8093. $file = $objattr['file'];
  8094. $info = $this->images[$file];
  8095. }
  8096. }
  8097. if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
  8098. $w = 0.00001;
  8099. $h = 0.00001;
  8100. }
  8101. // TEST whether need to skipline
  8102. if (!$paint) {
  8103. if ($type == 'hr') { // always force new line
  8104. if (($y + $h + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
  8105. return array(-2, $w, $h);
  8106. } // New page + new line
  8107. else {
  8108. return array(1, $w, $h);
  8109. } // new line
  8110. } else {
  8111. // LIST MARKERS // mPDF 6 Lists
  8112. $displayheight = $h;
  8113. $displaywidth = $w;
  8114. if ($objattr['type'] == 'image' && isset($objattr['listmarker']) && $objattr['listmarker']) {
  8115. $displayheight = 0;
  8116. if ($objattr['listmarkerposition'] == 'outside') {
  8117. $displaywidth = 0;
  8118. }
  8119. }
  8120. if ($widthUsed > 0 && $displaywidth > $widthLeft && (!$is_table || $type != 'image')) { // New line needed
  8121. // mPDF 6 Lists
  8122. if (($y + $displayheight + $lineHeight > $this->PageBreakTrigger) && !$this->InFooter) {
  8123. return array(-2, $w, $h);
  8124. } // New page + new line
  8125. return array(1, $w, $h); // new line
  8126. } elseif ($widthUsed > 0 && $displaywidth > $widthLeft && $is_table) { // New line needed in TABLE
  8127. return array(1, $w, $h); // new line
  8128. }
  8129. // Will fit on line but NEW PAGE REQUIRED
  8130. elseif (($y + $displayheight > $this->PageBreakTrigger) && !$this->InFooter && !$is_table) {
  8131. return array(-1, $w, $h);
  8132. } // mPDF 6 Lists
  8133. else {
  8134. return array(0, $w, $h);
  8135. }
  8136. }
  8137. }
  8138. if ($type == 'annot' || $type == 'bookmark' || $type == 'indexentry' || $type == 'toc') {
  8139. $w = 0.00001;
  8140. $h = 0.00001;
  8141. $objattr['BORDER-WIDTH'] = 0;
  8142. $objattr['BORDER-HEIGHT'] = 0;
  8143. $objattr['BORDER-X'] = $x;
  8144. $objattr['BORDER-Y'] = $y;
  8145. $objattr['INNER-WIDTH'] = 0;
  8146. $objattr['INNER-HEIGHT'] = 0;
  8147. $objattr['INNER-X'] = $x;
  8148. $objattr['INNER-Y'] = $y;
  8149. }
  8150. if ($type == 'image') {
  8151. // Automatically resize to width remaining
  8152. if ($w > ($widthLeft + 0.0001) && !$is_table) { // mPDF 5.7.4 0.0001 to allow for rounding errors when w==maxWidth
  8153. $w = $widthLeft;
  8154. $h = abs($w * $info['h'] / $info['w']);
  8155. }
  8156. $img_w = $w - $extraWidth;
  8157. $img_h = $h - $extraHeight;
  8158. $objattr['BORDER-WIDTH'] = $img_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
  8159. $objattr['BORDER-HEIGHT'] = $img_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
  8160. $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
  8161. $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
  8162. $objattr['INNER-WIDTH'] = $img_w;
  8163. $objattr['INNER-HEIGHT'] = $img_h;
  8164. $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
  8165. $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
  8166. $objattr['ID'] = $info['i'];
  8167. }
  8168. if ($type == 'input' && $objattr['subtype'] == 'IMAGE') {
  8169. $img_w = $w - $extraWidth;
  8170. $img_h = $h - $extraHeight;
  8171. $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
  8172. $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
  8173. $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
  8174. $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
  8175. $objattr['INNER-WIDTH'] = $img_w;
  8176. $objattr['INNER-HEIGHT'] = $img_h;
  8177. $objattr['INNER-X'] = $x + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
  8178. $objattr['INNER-Y'] = $y + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
  8179. $objattr['ID'] = $info['i'];
  8180. }
  8181. if ($type == 'barcode' || $type == 'textcircle') {
  8182. $b_w = $w - $extraWidth;
  8183. $b_h = $h - $extraHeight;
  8184. $objattr['BORDER-WIDTH'] = $b_w + $objattr['padding_left'] / $k + $objattr['padding_right'] / $k + (($objattr['border_left']['w'] / $k + $objattr['border_right']['w'] / $k) / 2);
  8185. $objattr['BORDER-HEIGHT'] = $b_h + $objattr['padding_top'] / $k + $objattr['padding_bottom'] / $k + (($objattr['border_top']['w'] / $k + $objattr['border_bottom']['w'] / $k) / 2);
  8186. $objattr['BORDER-X'] = $x + $objattr['margin_left'] / $k + (($objattr['border_left']['w'] / $k) / 2);
  8187. $objattr['BORDER-Y'] = $y + $objattr['margin_top'] / $k + (($objattr['border_top']['w'] / $k) / 2);
  8188. $objattr['INNER-X'] = $x + $objattr['padding_left'] / $k + $objattr['margin_left'] / $k + ($objattr['border_left']['w'] / $k);
  8189. $objattr['INNER-Y'] = $y + $objattr['padding_top'] / $k + $objattr['margin_top'] / $k + ($objattr['border_top']['w'] / $k);
  8190. $objattr['INNER-WIDTH'] = $b_w;
  8191. $objattr['INNER-HEIGHT'] = $b_h;
  8192. }
  8193. if ($type == 'textarea') {
  8194. // Automatically resize to width remaining
  8195. if ($w > $widthLeft && !$is_table) {
  8196. $w = $widthLeft;
  8197. }
  8198. // This used to resize height to maximum remaining on page ? why. Causes problems when in table and causing a new column
  8199. // if (($y + $h > $this->PageBreakTrigger) && !$this->InFooter) {
  8200. // $h=$this->h - $y - $this->bMargin;
  8201. // }
  8202. }
  8203. if ($type == 'hr') {
  8204. if ($is_table) {
  8205. $objattr['INNER-WIDTH'] = $maxWidth * $objattr['W-PERCENT'] / 100;
  8206. $objattr['width'] = $objattr['INNER-WIDTH'];
  8207. $w = $maxWidth;
  8208. } else {
  8209. if ($w > $maxWidth) {
  8210. $w = $maxWidth;
  8211. }
  8212. $objattr['INNER-WIDTH'] = $w;
  8213. $w = $maxWidth;
  8214. }
  8215. }
  8216. if (($type == 'select') || ($type == 'input' && ($objattr['subtype'] == 'TEXT' || $objattr['subtype'] == 'PASSWORD'))) {
  8217. // Automatically resize to width remaining
  8218. if ($w > $widthLeft && !$is_table) {
  8219. $w = $widthLeft;
  8220. }
  8221. }
  8222. if ($type == 'textarea' || $type == 'select' || $type == 'input') {
  8223. if (isset($objattr['fontsize']))
  8224. $objattr['fontsize'] /= $k;
  8225. if (isset($objattr['linewidth']))
  8226. $objattr['linewidth'] /= $k;
  8227. }
  8228. if (!isset($objattr['BORDER-Y'])) {
  8229. $objattr['BORDER-Y'] = 0;
  8230. }
  8231. if (!isset($objattr['BORDER-X'])) {
  8232. $objattr['BORDER-X'] = 0;
  8233. }
  8234. if (!isset($objattr['INNER-Y'])) {
  8235. $objattr['INNER-Y'] = 0;
  8236. }
  8237. if (!isset($objattr['INNER-X'])) {
  8238. $objattr['INNER-X'] = 0;
  8239. }
  8240. //Return width-height array
  8241. $objattr['OUTER-WIDTH'] = $w;
  8242. $objattr['OUTER-HEIGHT'] = $h;
  8243. $objattr['OUTER-X'] = $x;
  8244. $objattr['OUTER-Y'] = $y;
  8245. return $objattr;
  8246. }
  8247. /* -- END HTML-CSS -- */
  8248. //=============================================================
  8249. //=============================================================
  8250. //=============================================================
  8251. //=============================================================
  8252. //=============================================================
  8253. function SetLineJoin($mode = 0)
  8254. {
  8255. $s = sprintf('%d j', $mode);
  8256. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineJoin']) && $this->pageoutput[$this->page]['LineJoin'] != $s) || !isset($this->pageoutput[$this->page]['LineJoin']))) {
  8257. $this->_out($s);
  8258. }
  8259. $this->pageoutput[$this->page]['LineJoin'] = $s;
  8260. }
  8261. function SetLineCap($mode = 2)
  8262. {
  8263. $s = sprintf('%d J', $mode);
  8264. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['LineCap']) && $this->pageoutput[$this->page]['LineCap'] != $s) || !isset($this->pageoutput[$this->page]['LineCap']))) {
  8265. $this->_out($s);
  8266. }
  8267. $this->pageoutput[$this->page]['LineCap'] = $s;
  8268. }
  8269. function SetDash($black = false, $white = false)
  8270. {
  8271. if ($black and $white)
  8272. $s = sprintf('[%.3F %.3F] 0 d', $black * _MPDFK, $white * _MPDFK);
  8273. else
  8274. $s = '[] 0 d';
  8275. if ($this->page > 0 && ((isset($this->pageoutput[$this->page]['Dash']) && $this->pageoutput[$this->page]['Dash'] != $s) || !isset($this->pageoutput[$this->page]['Dash']))) {
  8276. $this->_out($s);
  8277. }
  8278. $this->pageoutput[$this->page]['Dash'] = $s;
  8279. }
  8280. function SetDisplayPreferences($preferences)
  8281. {
  8282. // String containing any or none of /HideMenubar/HideToolbar/HideWindowUI/DisplayDocTitle/CenterWindow/FitWindow
  8283. $this->DisplayPreferences .= $preferences;
  8284. }
  8285. function Ln($h = '', $collapsible = 0)
  8286. {
  8287. // Added collapsible to allow collapsible top-margin on new page
  8288. //Line feed; default value is last cell height
  8289. $this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'];
  8290. if ($collapsible && ($this->y == $this->tMargin) && (!$this->ColActive)) {
  8291. $h = 0;
  8292. }
  8293. if (is_string($h))
  8294. $this->y+=$this->lasth;
  8295. else
  8296. $this->y+=$h;
  8297. }
  8298. /* -- HTML-CSS -- */
  8299. function DivLn($h, $level = -3, $move_y = true, $collapsible = false, $state = 0)
  8300. {
  8301. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  8302. // Used in Columns and keep-with-table i.e. "kwt"
  8303. // writes background block by block so it can be repositioned
  8304. // and also used in writingFlowingBlock at top and bottom of blocks to move y (not to draw/paint anything)
  8305. // adds lines (y) where DIV bgcolors are filled in
  8306. // this->x is returned as it was
  8307. // allows .00001 as nominal height used for bookmarks/annotations etc.
  8308. if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)) && (!$this->ColActive)) {
  8309. return;
  8310. }
  8311. // mPDF 6 Columns
  8312. // if ($collapsible && (sprintf("%0.4f", $this->y)==sprintf("%0.4f", $this->y0)) && ($this->ColActive) && $this->CurrCol == 0) { return; } // *COLUMNS*
  8313. if ($collapsible && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->y0)) && ($this->ColActive)) {
  8314. return;
  8315. } // *COLUMNS*
  8316. // Still use this method if columns or keep-with-table, as it allows repositioning later
  8317. // otherwise, now uses PaintDivBB()
  8318. if (!$this->ColActive && !$this->kwt) {
  8319. if ($move_y && !$this->ColActive) {
  8320. $this->y += $h;
  8321. }
  8322. return;
  8323. }
  8324. if ($level == -3) {
  8325. $level = $this->blklvl;
  8326. }
  8327. $firstblockfill = $this->GetFirstBlockFill();
  8328. if ($firstblockfill && $this->blklvl > 0 && $this->blklvl >= $firstblockfill) {
  8329. $last_x = 0;
  8330. $last_w = 0;
  8331. $last_fc = $this->FillColor;
  8332. $bak_x = $this->x;
  8333. $bak_h = $this->divheight;
  8334. $this->divheight = 0; // Temporarily turn off divheight - as Cell() uses it to check for PageBreak
  8335. for ($blvl = $firstblockfill; $blvl <= $level; $blvl++) {
  8336. $this->x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
  8337. // mPDF 6
  8338. if ($this->blk[$blvl]['bgcolor']) {
  8339. $this->SetFColor($this->blk[$blvl]['bgcolorarray']);
  8340. }
  8341. if ($last_x != ($this->lMargin + $this->blk[$blvl]['outer_left_margin']) || ($last_w != $this->blk[$blvl]['width']) || $last_fc != $this->FillColor || (isset($this->blk[$blvl]['border_top']['s']) && $this->blk[$blvl]['border_top']['s']) || (isset($this->blk[$blvl]['border_bottom']['s']) && $this->blk[$blvl]['border_bottom']['s']) || (isset($this->blk[$blvl]['border_left']['s']) && $this->blk[$blvl]['border_left']['s']) || (isset($this->blk[$blvl]['border_right']['s']) && $this->blk[$blvl]['border_right']['s'])) {
  8342. $x = $this->x;
  8343. $this->Cell(($this->blk[$blvl]['width']), $h, '', '', 0, '', 1);
  8344. $this->x = $x;
  8345. if (!$this->keep_block_together && !$this->writingHTMLheader && !$this->writingHTMLfooter) {
  8346. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  8347. if ($blvl == $this->blklvl) {
  8348. $this->PaintDivLnBorder($state, $blvl, $h);
  8349. } else {
  8350. $this->PaintDivLnBorder(0, $blvl, $h);
  8351. }
  8352. }
  8353. }
  8354. $last_x = $this->lMargin + $this->blk[$blvl]['outer_left_margin'];
  8355. $last_w = $this->blk[$blvl]['width'];
  8356. $last_fc = $this->FillColor;
  8357. }
  8358. // Reset current block fill
  8359. if (isset($this->blk[$this->blklvl]['bgcolorarray'])) {
  8360. $bcor = $this->blk[$this->blklvl]['bgcolorarray'];
  8361. $this->SetFColor($bcor);
  8362. }
  8363. $this->x = $bak_x;
  8364. $this->divheight = $bak_h;
  8365. }
  8366. if ($move_y) {
  8367. $this->y += $h;
  8368. }
  8369. }
  8370. /* -- END HTML-CSS -- */
  8371. function SetX($x)
  8372. {
  8373. //Set x position
  8374. if ($x >= 0)
  8375. $this->x = $x;
  8376. else
  8377. $this->x = $this->w + $x;
  8378. }
  8379. function SetY($y)
  8380. {
  8381. //Set y position and reset x
  8382. $this->x = $this->lMargin;
  8383. if ($y >= 0)
  8384. $this->y = $y;
  8385. else
  8386. $this->y = $this->h + $y;
  8387. }
  8388. function SetXY($x, $y)
  8389. {
  8390. //Set x and y positions
  8391. $this->SetY($y);
  8392. $this->SetX($x);
  8393. }
  8394. function Output($name = '', $dest = '')
  8395. {
  8396. //Output PDF to some destination
  8397. if ($this->showStats) {
  8398. echo '<div>Generated in ' . sprintf('%.2F', (microtime(true) - $this->time0)) . ' seconds</div>';
  8399. }
  8400. //Finish document if necessary
  8401. if ($this->progressBar) {
  8402. $this->UpdateProgressBar(1, '100', 'Finished');
  8403. } // *PROGRESS-BAR*
  8404. if ($this->state < 3)
  8405. $this->Close();
  8406. if ($this->progressBar) {
  8407. $this->UpdateProgressBar(2, '100', 'Finished');
  8408. } // *PROGRESS-BAR*
  8409. // fn. error_get_last is only in PHP>=5.2
  8410. if ($this->debug && function_exists('error_get_last') && error_get_last()) {
  8411. $e = error_get_last();
  8412. if (($e['type'] < 2048 && $e['type'] != 8) || (intval($e['type']) & intval(ini_get("error_reporting")))) {
  8413. echo "<p>Error message detected - PDF file generation aborted.</p>";
  8414. echo $e['message'] . '<br />';
  8415. echo 'File: ' . $e['file'] . '<br />';
  8416. echo 'Line: ' . $e['line'] . '<br />';
  8417. exit;
  8418. }
  8419. }
  8420. if (($this->PDFA || $this->PDFX) && $this->encrypted) {
  8421. throw new MpdfException("PDFA1-b or PDFX/1-a does not permit encryption of documents.");
  8422. }
  8423. if (count($this->PDFAXwarnings) && (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto))) {
  8424. if ($this->PDFA) {
  8425. echo '<div>WARNING - This file could not be generated as it stands as a PDFA1-b compliant file.</div>';
  8426. echo '<div>These issues can be automatically fixed by mPDF using <i>$mpdf-&gt;PDFAauto=true;</i></div>';
  8427. echo '<div>Action that mPDF will take to automatically force PDFA1-b compliance are shown in brackets.</div>';
  8428. } else {
  8429. echo '<div>WARNING - This file could not be generated as it stands as a PDFX/1-a compliant file.</div>';
  8430. echo '<div>These issues can be automatically fixed by mPDF using <i>$mpdf-&gt;PDFXauto=true;</i></div>';
  8431. echo '<div>Action that mPDF will take to automatically force PDFX/1-a compliance are shown in brackets.</div>';
  8432. }
  8433. echo '<div>Warning(s) generated:</div><ul>';
  8434. $this->PDFAXwarnings = array_unique($this->PDFAXwarnings);
  8435. foreach ($this->PDFAXwarnings AS $w) {
  8436. echo '<li>' . $w . '</li>';
  8437. }
  8438. echo '</ul>';
  8439. exit;
  8440. }
  8441. if ($this->showStats) {
  8442. echo '<div>Compiled in ' . sprintf('%.2F', (microtime(true) - $this->time0)) . ' seconds (total)</div>';
  8443. echo '<div>Peak Memory usage ' . number_format((memory_get_peak_usage(true) / (1024 * 1024)), 2) . ' MB</div>';
  8444. echo '<div>PDF file size ' . number_format((strlen($this->buffer) / 1024)) . ' kB</div>';
  8445. echo '<div>Number of fonts ' . count($this->fonts) . '</div>';
  8446. exit;
  8447. }
  8448. if (is_bool($dest))
  8449. $dest = $dest ? 'D' : 'F';
  8450. $dest = strtoupper($dest);
  8451. if ($dest == '') {
  8452. if ($name == '') {
  8453. $name = 'mpdf.pdf';
  8454. $dest = 'I';
  8455. } else {
  8456. $dest = 'F';
  8457. }
  8458. }
  8459. /* -- PROGRESS-BAR -- */
  8460. if ($this->progressBar && ($dest == 'D' || $dest == 'I')) {
  8461. if ($name == '') {
  8462. $name = 'mpdf.pdf';
  8463. }
  8464. $tempfile = '_tempPDF' . uniqid(rand(1, 100000), true);
  8465. //Save to local file
  8466. $f = fopen(_MPDF_TEMP_PATH . $tempfile . '.pdf', 'wb');
  8467. if (!$f)
  8468. throw new MpdfException('Unable to create temporary output file: ' . $tempfile . '.pdf');
  8469. fwrite($f, $this->buffer, strlen($this->buffer));
  8470. fclose($f);
  8471. $this->UpdateProgressBar(3, '', 'Finished');
  8472. echo '<script type="text/javascript">
  8473. var form = document.createElement("form");
  8474. form.setAttribute("method", "post");
  8475. form.setAttribute("action", "' . _MPDF_URI . 'includes/out.php");
  8476. var hiddenField = document.createElement("input");
  8477. hiddenField.setAttribute("type", "hidden");
  8478. hiddenField.setAttribute("name", "filename");
  8479. hiddenField.setAttribute("value", "' . $tempfile . '");
  8480. form.appendChild(hiddenField);
  8481. var hiddenField = document.createElement("input");
  8482. hiddenField.setAttribute("type", "hidden");
  8483. hiddenField.setAttribute("name", "dest");
  8484. hiddenField.setAttribute("value", "' . $dest . '");
  8485. form.appendChild(hiddenField);
  8486. var hiddenField = document.createElement("input");
  8487. hiddenField.setAttribute("type", "hidden");
  8488. hiddenField.setAttribute("name", "opname");
  8489. hiddenField.setAttribute("value", "' . $name . '");
  8490. form.appendChild(hiddenField);
  8491. var hiddenField = document.createElement("input");
  8492. hiddenField.setAttribute("type", "hidden");
  8493. hiddenField.setAttribute("name", "path");
  8494. hiddenField.setAttribute("value", "' . urlencode(_MPDF_TEMP_PATH) . '");
  8495. form.appendChild(hiddenField);
  8496. document.body.appendChild(form);
  8497. form.submit();
  8498. </script>
  8499. </div>
  8500. </body>
  8501. </html>';
  8502. exit;
  8503. }
  8504. else {
  8505. if ($this->progressBar) {
  8506. $this->UpdateProgressBar(3, '', 'Finished');
  8507. }
  8508. /* -- END PROGRESS-BAR -- */
  8509. switch ($dest) {
  8510. case 'I':
  8511. if ($this->debug && !$this->allow_output_buffering && ob_get_contents()) {
  8512. echo "<p>Output has already been sent from the script - PDF file generation aborted.</p>";
  8513. exit;
  8514. }
  8515. //Send to standard output
  8516. if (PHP_SAPI != 'cli') {
  8517. //We send to a browser
  8518. header('Content-Type: application/pdf');
  8519. if (headers_sent())
  8520. throw new MpdfException('Some data has already been output to browser, can\'t send PDF file');
  8521. if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  8522. // don't use length if server using compression
  8523. header('Content-Length: ' . strlen($this->buffer));
  8524. }
  8525. header('Content-disposition: inline; filename="' . $name . '"');
  8526. header('Cache-Control: public, must-revalidate, max-age=0');
  8527. header('Pragma: public');
  8528. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
  8529. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  8530. }
  8531. echo $this->buffer;
  8532. break;
  8533. case 'D':
  8534. //Download file
  8535. header('Content-Description: File Transfer');
  8536. if (headers_sent())
  8537. throw new MpdfException('Some data has already been output to browser, can\'t send PDF file');
  8538. header('Content-Transfer-Encoding: binary');
  8539. header('Cache-Control: public, must-revalidate, max-age=0');
  8540. header('Pragma: public');
  8541. header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
  8542. header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
  8543. header('Content-Type: application/force-download');
  8544. header('Content-Type: application/octet-stream', false);
  8545. header('Content-Type: application/download', false);
  8546. header('Content-Type: application/pdf', false);
  8547. if (!isset($_SERVER['HTTP_ACCEPT_ENCODING']) OR empty($_SERVER['HTTP_ACCEPT_ENCODING'])) {
  8548. // don't use length if server using compression
  8549. header('Content-Length: ' . strlen($this->buffer));
  8550. }
  8551. header('Content-disposition: attachment; filename="' . $name . '"');
  8552. echo $this->buffer;
  8553. break;
  8554. case 'F':
  8555. //Save to local file
  8556. $f = fopen($name, 'wb');
  8557. if (!$f)
  8558. throw new MpdfException('Unable to create output file: ' . $name);
  8559. fwrite($f, $this->buffer, strlen($this->buffer));
  8560. fclose($f);
  8561. break;
  8562. case 'S':
  8563. //Return as a string
  8564. return $this->buffer;
  8565. default:
  8566. throw new MpdfException('Incorrect output destination: ' . $dest);
  8567. }
  8568. } // *PROGRESS-BAR*
  8569. //======================================================================================================
  8570. // DELETE OLD TMP FILES - Housekeeping
  8571. // Delete any files in tmp/ directory that are >1 hrs old
  8572. $interval = 3600;
  8573. if ($handle = @opendir(preg_replace('/\/$/', '', _MPDF_TEMP_PATH))) { // mPDF 5.7.3
  8574. while (false !== ($file = readdir($handle))) {
  8575. if (($file != "..") && ($file != ".") && !is_dir($file) && ((filemtime(_MPDF_TEMP_PATH . $file) + $interval) < time()) && (substr($file, 0, 1) !== '.') && ($file != 'dummy.txt')) { // mPDF 5.7.3
  8576. unlink(_MPDF_TEMP_PATH . $file);
  8577. }
  8578. }
  8579. closedir($handle);
  8580. }
  8581. //==============================================================================================================
  8582. return '';
  8583. }
  8584. // *****************************************************************************
  8585. // *
  8586. // Protected methods *
  8587. // *
  8588. // *****************************************************************************
  8589. function _dochecks()
  8590. {
  8591. //Check for locale-related bug
  8592. if (1.1 == 1)
  8593. throw new MpdfException('Don\'t alter the locale before including mPDF');
  8594. //Check for decimal separator
  8595. if (sprintf('%.1f', 1.0) != '1.0')
  8596. setlocale(LC_NUMERIC, 'C');
  8597. $mqr = ini_get("magic_quotes_runtime");
  8598. if ($mqr) {
  8599. throw new MpdfException('mPDF requires magic_quotes_runtime to be turned off e.g. by using ini_set("magic_quotes_runtime", 0);');
  8600. }
  8601. }
  8602. function _puthtmlheaders()
  8603. {
  8604. $this->state = 2;
  8605. $nb = $this->page;
  8606. for ($n = 1; $n <= $nb; $n++) {
  8607. if ($this->mirrorMargins && $n % 2 == 0) {
  8608. $OE = 'E';
  8609. } // EVEN
  8610. else {
  8611. $OE = 'O';
  8612. }
  8613. $this->page = $n;
  8614. $pn = $this->docPageNum($n);
  8615. if ($pn)
  8616. $pnstr = $this->pagenumPrefix . $pn . $this->pagenumSuffix;
  8617. else {
  8618. $pnstr = '';
  8619. }
  8620. $pnt = $this->docPageNumTotal($n);
  8621. if ($pnt)
  8622. $pntstr = $this->nbpgPrefix . $pnt . $this->nbpgSuffix;
  8623. else {
  8624. $pntstr = '';
  8625. }
  8626. if (isset($this->saveHTMLHeader[$n][$OE])) {
  8627. $html = $this->saveHTMLHeader[$n][$OE]['html'];
  8628. $this->lMargin = $this->saveHTMLHeader[$n][$OE]['ml'];
  8629. $this->rMargin = $this->saveHTMLHeader[$n][$OE]['mr'];
  8630. $this->tMargin = $this->saveHTMLHeader[$n][$OE]['mh'];
  8631. $this->bMargin = $this->saveHTMLHeader[$n][$OE]['mf'];
  8632. $this->margin_header = $this->saveHTMLHeader[$n][$OE]['mh'];
  8633. $this->margin_footer = $this->saveHTMLHeader[$n][$OE]['mf'];
  8634. $this->w = $this->saveHTMLHeader[$n][$OE]['pw'];
  8635. $this->h = $this->saveHTMLHeader[$n][$OE]['ph'];
  8636. $rotate = (isset($this->saveHTMLHeader[$n][$OE]['rotate']) ? $this->saveHTMLHeader[$n][$OE]['rotate'] : null);
  8637. $this->Reset();
  8638. $this->pageoutput[$n] = array();
  8639. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8640. $this->x = $this->lMargin;
  8641. $this->y = $this->margin_header;
  8642. $html = str_replace('{PAGENO}', $pnstr, $html);
  8643. $html = str_replace($this->aliasNbPgGp, $pntstr, $html); // {nbpg}
  8644. $html = str_replace($this->aliasNbPg, $nb, $html); // {nb}
  8645. $html = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback'), $html); // mPDF 5.7
  8646. $this->HTMLheaderPageLinks = array();
  8647. $this->HTMLheaderPageAnnots = array();
  8648. $this->HTMLheaderPageForms = array();
  8649. $this->pageBackgrounds = array();
  8650. $this->writingHTMLheader = true;
  8651. $this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
  8652. $this->writingHTMLheader = false;
  8653. $this->Reset();
  8654. $this->pageoutput[$n] = array();
  8655. $s = $this->PrintPageBackgrounds();
  8656. $this->headerbuffer = $s . $this->headerbuffer;
  8657. $os = '';
  8658. if ($rotate) {
  8659. $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * _MPDFK));
  8660. // To rotate the other way i.e. Header to left of page:
  8661. //$os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*_MPDFK), (($this->rMargin - $this->lMargin )*_MPDFK));
  8662. }
  8663. $os .= $this->headerbuffer;
  8664. if ($rotate) {
  8665. $os .= ' Q' . "\n";
  8666. }
  8667. // Writes over the page background but behind any other output on page
  8668. $os = preg_replace('/\\\\/', '\\\\\\\\', $os);
  8669. $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
  8670. $lks = $this->HTMLheaderPageLinks;
  8671. foreach ($lks AS $lk) {
  8672. if ($rotate) {
  8673. $lw = $lk[2];
  8674. $lh = $lk[3];
  8675. $lk[2] = $lh;
  8676. $lk[3] = $lw; // swap width and height
  8677. $ax = $lk[0] / _MPDFK;
  8678. $ay = $lk[1] / _MPDFK;
  8679. $bx = $ay - ($lh / _MPDFK);
  8680. $by = $this->w - $ax;
  8681. $lk[0] = $bx * _MPDFK;
  8682. $lk[1] = ($this->h - $by) * _MPDFK - $lw;
  8683. }
  8684. $this->PageLinks[$n][] = $lk;
  8685. }
  8686. /* -- FORMS -- */
  8687. foreach ($this->HTMLheaderPageForms AS $f) {
  8688. $this->mpdfform->forms[$f['n']] = $f;
  8689. }
  8690. /* -- END FORMS -- */
  8691. }
  8692. if (isset($this->saveHTMLFooter[$n][$OE])) {
  8693. $html = $this->saveHTMLFooter[$this->page][$OE]['html'];
  8694. $this->lMargin = $this->saveHTMLFooter[$n][$OE]['ml'];
  8695. $this->rMargin = $this->saveHTMLFooter[$n][$OE]['mr'];
  8696. $this->tMargin = $this->saveHTMLFooter[$n][$OE]['mh'];
  8697. $this->bMargin = $this->saveHTMLFooter[$n][$OE]['mf'];
  8698. $this->margin_header = $this->saveHTMLFooter[$n][$OE]['mh'];
  8699. $this->margin_footer = $this->saveHTMLFooter[$n][$OE]['mf'];
  8700. $this->w = $this->saveHTMLFooter[$n][$OE]['pw'];
  8701. $this->h = $this->saveHTMLFooter[$n][$OE]['ph'];
  8702. $rotate = (isset($this->saveHTMLFooter[$n][$OE]['rotate']) ? $this->saveHTMLFooter[$n][$OE]['rotate'] : null);
  8703. $this->Reset();
  8704. $this->pageoutput[$n] = array();
  8705. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  8706. $this->x = $this->lMargin;
  8707. $top_y = $this->y = $this->h - $this->margin_footer;
  8708. // if bottom-margin==0, corrects to avoid division by zero
  8709. if ($this->y == $this->h) {
  8710. $top_y = $this->y = ($this->h - 0.1);
  8711. }
  8712. $html = str_replace('{PAGENO}', $pnstr, $html);
  8713. $html = str_replace($this->aliasNbPgGp, $pntstr, $html); // {nbpg}
  8714. $html = str_replace($this->aliasNbPg, $nb, $html); // {nb}
  8715. $html = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback'), $html); // mPDF 5.7
  8716. $this->HTMLheaderPageLinks = array();
  8717. $this->HTMLheaderPageAnnots = array();
  8718. $this->HTMLheaderPageForms = array();
  8719. $this->pageBackgrounds = array();
  8720. $this->writingHTMLfooter = true;
  8721. $this->InFooter = true;
  8722. $this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
  8723. $this->InFooter = false;
  8724. $this->Reset();
  8725. $this->pageoutput[$n] = array();
  8726. $fheight = $this->y - $top_y;
  8727. $adj = -$fheight;
  8728. $s = $this->PrintPageBackgrounds(-$adj);
  8729. $this->headerbuffer = $s . $this->headerbuffer;
  8730. $this->writingHTMLfooter = false; // mPDF 5.7.3 (moved after PrintPageBackgrounds so can adjust position of images in footer)
  8731. $os = '';
  8732. $os .= $this->StartTransform(true) . "\n";
  8733. if ($rotate) {
  8734. $os .= sprintf('q 0 -1 1 0 0 %.3F cm ', ($this->w * _MPDFK));
  8735. // To rotate the other way i.e. Header to left of page:
  8736. //$os .= sprintf('q 0 1 -1 0 %.3F %.3F cm ',($this->h*_MPDFK), (($this->rMargin - $this->lMargin )*_MPDFK));
  8737. }
  8738. $os .= $this->transformTranslate(0, $adj, true) . "\n";
  8739. $os .= $this->headerbuffer;
  8740. if ($rotate) {
  8741. $os .= ' Q' . "\n";
  8742. }
  8743. $os .= $this->StopTransform(true) . "\n";
  8744. // Writes over the page background but behind any other output on page
  8745. $os = preg_replace('/\\\\/', '\\\\\\\\', $os);
  8746. $this->pages[$n] = preg_replace('/(___HEADER___MARKER' . $this->uniqstr . ')/', "\n" . $os . "\n" . '\\1', $this->pages[$n]);
  8747. $lks = $this->HTMLheaderPageLinks;
  8748. foreach ($lks AS $lk) {
  8749. $lk[1] -= $adj * _MPDFK;
  8750. if ($rotate) {
  8751. $lw = $lk[2];
  8752. $lh = $lk[3];
  8753. $lk[2] = $lh;
  8754. $lk[3] = $lw; // swap width and height
  8755. $ax = $lk[0] / _MPDFK;
  8756. $ay = $lk[1] / _MPDFK;
  8757. $bx = $ay - ($lh / _MPDFK);
  8758. $by = $this->w - $ax;
  8759. $lk[0] = $bx * _MPDFK;
  8760. $lk[1] = ($this->h - $by) * _MPDFK - $lw;
  8761. }
  8762. $this->PageLinks[$n][] = $lk;
  8763. }
  8764. /* -- FORMS -- */
  8765. foreach ($this->HTMLheaderPageForms AS $f) {
  8766. $f['y'] += $adj;
  8767. $this->mpdfform->forms[$f['n']] = $f;
  8768. }
  8769. /* -- END FORMS -- */
  8770. }
  8771. }
  8772. $this->page = $nb;
  8773. $this->state = 1;
  8774. }
  8775. function _putpages()
  8776. {
  8777. $nb = $this->page;
  8778. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  8779. if ($this->DefOrientation == 'P') {
  8780. $defwPt = $this->fwPt;
  8781. $defhPt = $this->fhPt;
  8782. } else {
  8783. $defwPt = $this->fhPt;
  8784. $defhPt = $this->fwPt;
  8785. }
  8786. $annotid = (3 + 2 * $nb);
  8787. // Active Forms
  8788. $totaladdnum = 0;
  8789. for ($n = 1; $n <= $nb; $n++) {
  8790. if (isset($this->PageLinks[$n])) {
  8791. $totaladdnum += count($this->PageLinks[$n]);
  8792. }
  8793. /* -- ANNOTATIONS -- */
  8794. if (isset($this->PageAnnots[$n])) {
  8795. foreach ($this->PageAnnots[$n] as $k => $pl) {
  8796. if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) {
  8797. $totaladdnum += 2;
  8798. } else {
  8799. $totaladdnum++;
  8800. }
  8801. }
  8802. }
  8803. /* -- END ANNOTATIONS -- */
  8804. /* -- FORMS -- */
  8805. if (count($this->mpdfform->forms) > 0) {
  8806. $this->mpdfform->countPageForms($n, $totaladdnum);
  8807. }
  8808. /* -- END FORMS -- */
  8809. }
  8810. /* -- FORMS -- */
  8811. // Make a note in the radio button group of the obj_id it will have
  8812. $ctr = 0;
  8813. if (count($this->mpdfform->form_radio_groups)) {
  8814. foreach ($this->mpdfform->form_radio_groups AS $name => $frg) {
  8815. $this->mpdfform->form_radio_groups[$name]['obj_id'] = $annotid + $totaladdnum + $ctr;
  8816. $ctr++;
  8817. }
  8818. }
  8819. /* -- END FORMS -- */
  8820. // Select unused fonts (usually default font)
  8821. $unused = array();
  8822. foreach ($this->fonts as $fk => $font) {
  8823. if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
  8824. $unused[] = $fk;
  8825. }
  8826. }
  8827. for ($n = 1; $n <= $nb; $n++) {
  8828. $thispage = $this->pages[$n];
  8829. if (isset($this->OrientationChanges[$n])) {
  8830. $hPt = $this->pageDim[$n]['w'] * _MPDFK;
  8831. $wPt = $this->pageDim[$n]['h'] * _MPDFK;
  8832. $owidthPt_LR = $this->pageDim[$n]['outer_width_TB'] * _MPDFK;
  8833. $owidthPt_TB = $this->pageDim[$n]['outer_width_LR'] * _MPDFK;
  8834. } else {
  8835. $wPt = $this->pageDim[$n]['w'] * _MPDFK;
  8836. $hPt = $this->pageDim[$n]['h'] * _MPDFK;
  8837. $owidthPt_LR = $this->pageDim[$n]['outer_width_LR'] * _MPDFK;
  8838. $owidthPt_TB = $this->pageDim[$n]['outer_width_TB'] * _MPDFK;
  8839. }
  8840. // Remove references to unused fonts (usually default font)
  8841. foreach ($unused as $fk) {
  8842. if ($this->fonts[$fk]['sip'] || $this->fonts[$fk]['smp']) {
  8843. foreach ($this->fonts[$fk]['subsetfontids'] AS $k => $fid) {
  8844. $thispage = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $thispage);
  8845. }
  8846. } else {
  8847. $thispage = preg_replace('/\s\/F' . $this->fonts[$fk]['i'] . ' \d[\d.]* Tf\s/is', ' ', $thispage);
  8848. }
  8849. }
  8850. // Clean up repeated /GS1 gs statements
  8851. // For some reason using + for repetition instead of {2,20} crashes PHP Script Interpreter ???
  8852. $thispage = preg_replace('/(\/GS1 gs\n){2,20}/', "/GS1 gs\n", $thispage);
  8853. $thispage = preg_replace('/(\s*___BACKGROUND___PATTERNS' . $this->uniqstr . '\s*)/', " ", $thispage);
  8854. $thispage = preg_replace('/(\s*___HEADER___MARKER' . $this->uniqstr . '\s*)/', " ", $thispage);
  8855. $thispage = preg_replace('/(\s*___PAGE___START' . $this->uniqstr . '\s*)/', " ", $thispage);
  8856. $thispage = preg_replace('/(\s*___TABLE___BACKGROUNDS' . $this->uniqstr . '\s*)/', " ", $thispage);
  8857. // mPDF 5.7.3 TRANSFORMS
  8858. while (preg_match('/(\% BTR(.*?)\% ETR)/is', $thispage, $m)) {
  8859. $thispage = preg_replace('/(\% BTR.*?\% ETR)/is', '', $thispage, 1) . "\n" . $m[2];
  8860. }
  8861. //Page
  8862. $this->_newobj();
  8863. $this->_out('<</Type /Page');
  8864. $this->_out('/Parent 1 0 R');
  8865. if (isset($this->OrientationChanges[$n])) {
  8866. $this->_out(sprintf('/MediaBox [0 0 %.3F %.3F]', $hPt, $wPt));
  8867. //If BleedBox is defined, it must be larger than the TrimBox, but smaller than the MediaBox
  8868. $bleedMargin = $this->pageDim[$n]['bleedMargin'] * _MPDFK;
  8869. if ($bleedMargin && ($owidthPt_TB || $owidthPt_LR)) {
  8870. $x0 = $owidthPt_TB - $bleedMargin;
  8871. $y0 = $owidthPt_LR - $bleedMargin;
  8872. $x1 = $hPt - $owidthPt_TB + $bleedMargin;
  8873. $y1 = $wPt - $owidthPt_LR + $bleedMargin;
  8874. $this->_out(sprintf('/BleedBox [%.3F %.3F %.3F %.3F]', $x0, $y0, $x1, $y1));
  8875. }
  8876. $this->_out(sprintf('/TrimBox [%.3F %.3F %.3F %.3F]', $owidthPt_TB, $owidthPt_LR, ($hPt - $owidthPt_TB), ($wPt - $owidthPt_LR)));
  8877. if (isset($this->OrientationChanges[$n]) && $this->displayDefaultOrientation) {
  8878. if ($this->DefOrientation == 'P') {
  8879. $this->_out('/Rotate 270');
  8880. } else {
  8881. $this->_out('/Rotate 90');
  8882. }
  8883. }
  8884. }
  8885. //elseif($wPt != $defwPt || $hPt != $defhPt) {
  8886. else {
  8887. $this->_out(sprintf('/MediaBox [0 0 %.3F %.3F]', $wPt, $hPt));
  8888. $bleedMargin = $this->pageDim[$n]['bleedMargin'] * _MPDFK;
  8889. if ($bleedMargin && ($owidthPt_TB || $owidthPt_LR)) {
  8890. $x0 = $owidthPt_LR - $bleedMargin;
  8891. $y0 = $owidthPt_TB - $bleedMargin;
  8892. $x1 = $wPt - $owidthPt_LR + $bleedMargin;
  8893. $y1 = $hPt - $owidthPt_TB + $bleedMargin;
  8894. $this->_out(sprintf('/BleedBox [%.3F %.3F %.3F %.3F]', $x0, $y0, $x1, $y1));
  8895. }
  8896. $this->_out(sprintf('/TrimBox [%.3F %.3F %.3F %.3F]', $owidthPt_LR, $owidthPt_TB, ($wPt - $owidthPt_LR), ($hPt - $owidthPt_TB)));
  8897. }
  8898. $this->_out('/Resources 2 0 R');
  8899. // Important to keep in RGB colorSpace when using transparency
  8900. if (!$this->PDFA && !$this->PDFX) {
  8901. if ($this->restrictColorSpace == 3)
  8902. $this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceCMYK >> ');
  8903. elseif ($this->restrictColorSpace == 1)
  8904. $this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceGray >> ');
  8905. else
  8906. $this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceRGB >> ');
  8907. }
  8908. $annotsnum = 0;
  8909. $embeddedfiles = array(); // mPDF 5.7.2 /EmbeddedFiles
  8910. if (isset($this->PageLinks[$n])) {
  8911. $annotsnum += count($this->PageLinks[$n]);
  8912. }
  8913. /* -- ANNOTATIONS -- */
  8914. if (isset($this->PageAnnots[$n])) {
  8915. foreach ($this->PageAnnots[$n] as $k => $pl) {
  8916. if (!empty($pl['opt']['file'])) {
  8917. $embeddedfiles[$annotsnum + 1] = true;
  8918. } // mPDF 5.7.2 /EmbeddedFiles
  8919. if (!empty($pl['opt']['popup']) || !empty($pl['opt']['file'])) {
  8920. $annotsnum += 2;
  8921. } else {
  8922. $annotsnum++;
  8923. }
  8924. $this->PageAnnots[$n][$k]['pageobj'] = $this->n;
  8925. }
  8926. }
  8927. /* -- END ANNOTATIONS -- */
  8928. /* -- FORMS -- */
  8929. // Active Forms
  8930. $formsnum = 0;
  8931. if (count($this->mpdfform->forms) > 0) {
  8932. foreach ($this->mpdfform->forms as $val) {
  8933. if ($val['page'] == $n)
  8934. $formsnum++;
  8935. }
  8936. }
  8937. /* -- END FORMS -- */
  8938. if ($annotsnum || $formsnum) {
  8939. $s = '/Annots [ ';
  8940. for ($i = 0; $i < $annotsnum; $i++) {
  8941. if (!isset($embeddedfiles[$i])) {
  8942. $s .= ($annotid + $i) . ' 0 R ';
  8943. } // mPDF 5.7.2 /EmbeddedFiles
  8944. }
  8945. $annotid += $annotsnum;
  8946. /* -- FORMS -- */
  8947. if (count($this->mpdfform->forms) > 0) {
  8948. $this->mpdfform->addFormIds($n, $s, $annotid);
  8949. }
  8950. /* -- END FORMS -- */
  8951. $s .= '] ';
  8952. $this->_out($s);
  8953. }
  8954. $this->_out('/Contents ' . ($this->n + 1) . ' 0 R>>');
  8955. $this->_out('endobj');
  8956. //Page content
  8957. $this->_newobj();
  8958. $p = ($this->compress) ? gzcompress($thispage) : $thispage;
  8959. $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
  8960. $this->_putstream($p);
  8961. $this->_out('endobj');
  8962. }
  8963. $this->_putannots(); // mPDF 5.7.2
  8964. //Pages root
  8965. $this->offsets[1] = strlen($this->buffer);
  8966. $this->_out('1 0 obj');
  8967. $this->_out('<</Type /Pages');
  8968. $kids = '/Kids [';
  8969. for ($i = 0; $i < $nb; $i++)
  8970. $kids.=(3 + 2 * $i) . ' 0 R ';
  8971. $this->_out($kids . ']');
  8972. $this->_out('/Count ' . $nb);
  8973. $this->_out(sprintf('/MediaBox [0 0 %.3F %.3F]', $defwPt, $defhPt));
  8974. $this->_out('>>');
  8975. $this->_out('endobj');
  8976. }
  8977. function _putannots()
  8978. { // mPDF 5.7.2
  8979. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  8980. $nb = $this->page;
  8981. for ($n = 1; $n <= $nb; $n++) {
  8982. $annotobjs = array();
  8983. if (isset($this->PageLinks[$n]) || isset($this->PageAnnots[$n]) || count($this->mpdfform->forms) > 0) {
  8984. $wPt = $this->pageDim[$n]['w'] * _MPDFK;
  8985. $hPt = $this->pageDim[$n]['h'] * _MPDFK;
  8986. //Links
  8987. if (isset($this->PageLinks[$n])) {
  8988. foreach ($this->PageLinks[$n] as $key => $pl) {
  8989. $this->_newobj();
  8990. $annot = '';
  8991. $rect = sprintf('%.3F %.3F %.3F %.3F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
  8992. $annot .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . ']';
  8993. $annot .= ' /Contents ' . $this->_UTF16BEtextstring($pl[4]);
  8994. $annot .= ' /NM ' . $this->_textstring(sprintf('%04u-%04u', $n, $key));
  8995. $annot .= ' /M ' . $this->_textstring('D:' . date('YmdHis'));
  8996. $annot .= ' /Border [0 0 0]';
  8997. // Use this (instead of /Border) to specify border around link
  8998. // $annot .= ' /BS <</W 1'; // Width on points; 0 = no line
  8999. // $annot .= ' /S /D'; // style - [S]olid, [D]ashed, [B]eveled, [I]nset, [U]nderline
  9000. // $annot .= ' /D [3 2]'; // Dash array - if dashed
  9001. // $annot .= ' >>';
  9002. // $annot .= ' /C [1 0 0]'; // Color RGB
  9003. if ($this->PDFA || $this->PDFX) {
  9004. $annot .= ' /F 28';
  9005. }
  9006. if (strpos($pl[4], '@') === 0) {
  9007. $p = substr($pl[4], 1);
  9008. // $h=isset($this->OrientationChanges[$p]) ? $wPt : $hPt;
  9009. $htarg = $this->pageDim[$p]['h'] * _MPDFK;
  9010. $annot.=sprintf(' /Dest [%d 0 R /XYZ 0 %.3F null]>>', 1 + 2 * $p, $htarg);
  9011. } elseif (is_string($pl[4])) {
  9012. $annot .= ' /A <</S /URI /URI ' . $this->_textstring($pl[4]) . '>> >>';
  9013. } else {
  9014. $l = $this->links[$pl[4]];
  9015. // may not be set if #link points to non-existent target
  9016. if (isset($this->pageDim[$l[0]]['h'])) {
  9017. $htarg = $this->pageDim[$l[0]]['h'] * _MPDFK;
  9018. } else {
  9019. $htarg = $this->h * _MPDFK;
  9020. } // doesn't really matter
  9021. $annot.=sprintf(' /Dest [%d 0 R /XYZ 0 %.3F null]>>', 1 + 2 * $l[0], $htarg - $l[1] * _MPDFK);
  9022. }
  9023. $this->_out($annot);
  9024. $this->_out('endobj');
  9025. }
  9026. }
  9027. /* -- ANNOTATIONS -- */
  9028. if (isset($this->PageAnnots[$n])) {
  9029. foreach ($this->PageAnnots[$n] as $key => $pl) {
  9030. if ($pl['opt']['file']) {
  9031. $FileAttachment = true;
  9032. } else {
  9033. $FileAttachment = false;
  9034. }
  9035. $this->_newobj();
  9036. $annot = '';
  9037. $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
  9038. $x = $pl['x'];
  9039. if ($this->annotMargin <> 0 || $x == 0 || $x < 0) { // Odd page
  9040. $x = ($wPt / _MPDFK) - $this->annotMargin;
  9041. }
  9042. $w = $h = 0;
  9043. $a = $x * _MPDFK;
  9044. $b = $hPt - ($pl['y'] * _MPDFK);
  9045. $annot .= '<</Type /Annot ';
  9046. if ($FileAttachment) {
  9047. $annot .= '/Subtype /FileAttachment ';
  9048. // Need to set a size for FileAttachment icons
  9049. if ($pl['opt']['icon'] == 'Paperclip') {
  9050. $w = 8.235;
  9051. $h = 20;
  9052. } // 7,17
  9053. elseif ($pl['opt']['icon'] == 'Tag') {
  9054. $w = 20;
  9055. $h = 16;
  9056. } elseif ($pl['opt']['icon'] == 'Graph') {
  9057. $w = 20;
  9058. $h = 20;
  9059. } else {
  9060. $w = 14;
  9061. $h = 20;
  9062. } // PushPin
  9063. $f = $pl['opt']['file'];
  9064. $f = preg_replace('/^.*\//', '', $f);
  9065. $f = preg_replace('/[^a-zA-Z0-9._]/', '', $f);
  9066. $annot .= '/FS <</Type /Filespec /F (' . $f . ')';
  9067. $annot .= '/EF <</F ' . ($this->n + 1) . ' 0 R>>';
  9068. $annot .= '>>';
  9069. } else {
  9070. $annot .= '/Subtype /Text';
  9071. $w = 20;
  9072. $h = 20; // mPDF 6
  9073. }
  9074. $rect = sprintf('%.3F %.3F %.3F %.3F', $a, $b - $h, $a + $w, $b);
  9075. $annot .= ' /Rect [' . $rect . ']';
  9076. // contents = description of file in free text
  9077. $annot .= ' /Contents ' . $this->_UTF16BEtextstring($pl['txt']);
  9078. $annot .= ' /NM ' . $this->_textstring(sprintf('%04u-%04u', $n, (2000 + $key)));
  9079. $annot .= ' /M ' . $this->_textstring('D:' . date('YmdHis'));
  9080. $annot .= ' /CreationDate ' . $this->_textstring('D:' . date('YmdHis'));
  9081. $annot .= ' /Border [0 0 0]';
  9082. if ($this->PDFA || $this->PDFX) {
  9083. $annot .= ' /F 28';
  9084. $annot .= ' /CA 1';
  9085. } elseif ($pl['opt']['ca'] > 0) {
  9086. $annot .= ' /CA ' . $pl['opt']['ca'];
  9087. }
  9088. $annotcolor = ' /C [';
  9089. if (isset($pl['opt']['c']) AND $pl['opt']['c']) {
  9090. $col = $pl['opt']['c'];
  9091. if ($col{0} == 3 || $col{0} == 5) {
  9092. $annotcolor .= sprintf("%.3F %.3F %.3F", ord($col{1}) / 255, ord($col{2}) / 255, ord($col{3}) / 255);
  9093. } elseif ($col{0} == 1) {
  9094. $annotcolor .= sprintf("%.3F", ord($col{1}) / 255);
  9095. } elseif ($col{0} == 4 || $col{0} == 6) {
  9096. $annotcolor .= sprintf("%.3F %.3F %.3F %.3F", ord($col{1}) / 100, ord($col{2}) / 100, ord($col{3}) / 100, ord($col{4}) / 100);
  9097. } else {
  9098. $annotcolor .= '1 1 0';
  9099. }
  9100. } else {
  9101. $annotcolor .= '1 1 0';
  9102. }
  9103. $annotcolor .= ']';
  9104. $annot .= $annotcolor;
  9105. // Usually Author
  9106. // Use as Title for fileattachment
  9107. if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
  9108. $annot .= ' /T ' . $this->_UTF16BEtextstring($pl['opt']['t']);
  9109. }
  9110. if ($FileAttachment) {
  9111. $iconsapp = array('Paperclip', 'Graph', 'PushPin', 'Tag');
  9112. } else {
  9113. $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
  9114. }
  9115. if (isset($pl['opt']['icon']) AND in_array($pl['opt']['icon'], $iconsapp)) {
  9116. $annot .= ' /Name /' . $pl['opt']['icon'];
  9117. } elseif ($FileAttachment) {
  9118. $annot .= ' /Name /PushPin';
  9119. } else {
  9120. $annot .= ' /Name /Note';
  9121. }
  9122. if (!$FileAttachment) {
  9123. // /Subj is PDF 1.5 spec.
  9124. if (isset($pl['opt']['subj']) && !$this->PDFA && !$this->PDFX) {
  9125. $annot .= ' /Subj ' . $this->_UTF16BEtextstring($pl['opt']['subj']);
  9126. }
  9127. if (!empty($pl['opt']['popup'])) {
  9128. $annot .= ' /Open true';
  9129. $annot .= ' /Popup ' . ($this->n + 1) . ' 0 R';
  9130. } else {
  9131. $annot .= ' /Open false';
  9132. }
  9133. }
  9134. $annot .= ' /P ' . $pl['pageobj'] . ' 0 R';
  9135. $annot .= '>>';
  9136. $this->_out($annot);
  9137. $this->_out('endobj');
  9138. if ($FileAttachment) {
  9139. $file = @file_get_contents($pl['opt']['file']);
  9140. if (!$file) {
  9141. throw new MpdfException('mPDF Error: Cannot access file attachment - ' . $pl['opt']['file']);
  9142. }
  9143. $filestream = gzcompress($file);
  9144. $this->_newobj();
  9145. $this->_out('<</Type /EmbeddedFile');
  9146. $this->_out('/Length ' . strlen($filestream));
  9147. $this->_out('/Filter /FlateDecode');
  9148. $this->_out('>>');
  9149. $this->_putstream($filestream);
  9150. $this->_out('endobj');
  9151. } elseif (!empty($pl['opt']['popup'])) {
  9152. $this->_newobj();
  9153. $annot = '';
  9154. if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][0])) {
  9155. $x = $pl['opt']['popup'][0] * _MPDFK;
  9156. } else {
  9157. $x = $pl['x'] * _MPDFK;
  9158. }
  9159. if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][1])) {
  9160. $y = $hPt - ($pl['opt']['popup'][1] * _MPDFK);
  9161. } else {
  9162. $y = $hPt - ($pl['y'] * _MPDFK);
  9163. }
  9164. if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][2])) {
  9165. $w = $pl['opt']['popup'][2] * _MPDFK;
  9166. } else {
  9167. $w = 180;
  9168. }
  9169. if (is_array($pl['opt']['popup']) && isset($pl['opt']['popup'][3])) {
  9170. $h = $pl['opt']['popup'][3] * _MPDFK;
  9171. } else {
  9172. $h = 120;
  9173. }
  9174. $rect = sprintf('%.3F %.3F %.3F %.3F', $x, $y - $h, $x + $w, $y);
  9175. $annot .= '<</Type /Annot /Subtype /Popup /Rect [' . $rect . ']';
  9176. $annot .= ' /M ' . $this->_textstring('D:' . date('YmdHis'));
  9177. if ($this->PDFA || $this->PDFX) {
  9178. $annot .= ' /F 28';
  9179. }
  9180. $annot .= ' /Parent ' . ($this->n - 1) . ' 0 R';
  9181. $annot .= '>>';
  9182. $this->_out($annot);
  9183. $this->_out('endobj');
  9184. }
  9185. }
  9186. }
  9187. /* -- END ANNOTATIONS -- */
  9188. /* -- FORMS -- */
  9189. // Active Forms
  9190. if (count($this->mpdfform->forms) > 0) {
  9191. $this->mpdfform->_putFormItems($n, $hPt);
  9192. }
  9193. /* -- END FORMS -- */
  9194. }
  9195. }
  9196. /* -- FORMS -- */
  9197. // Active Forms - Radio Button Group entries
  9198. // Output Radio Button Group form entries (radio_on_obj_id already determined)
  9199. if (count($this->mpdfform->form_radio_groups)) {
  9200. $this->mpdfform->_putRadioItems($n);
  9201. }
  9202. /* -- END FORMS -- */
  9203. }
  9204. /* -- ANNOTATIONS -- */
  9205. function Annotation($text, $x = 0, $y = 0, $icon = 'Note', $author = '', $subject = '', $opacity = 0, $colarray = false, $popup = '', $file = '')
  9206. {
  9207. if (is_array($colarray) && count($colarray) == 3) {
  9208. $colarray = $this->ConvertColor('rgb(' . $colarray[0] . ',' . $colarray[1] . ',' . $colarray[2] . ')');
  9209. }
  9210. if ($colarray === false) {
  9211. $colarray = $this->ConvertColor('yellow');
  9212. }
  9213. if ($x == 0) {
  9214. $x = $this->x;
  9215. }
  9216. if ($y == 0) {
  9217. $y = $this->y;
  9218. }
  9219. $page = $this->page;
  9220. if ($page < 1) { // Document has not been started - assume it's for first page
  9221. $page = 1;
  9222. if ($x == 0) {
  9223. $x = $this->lMargin;
  9224. }
  9225. if ($y == 0) {
  9226. $y = $this->tMargin;
  9227. }
  9228. }
  9229. if ($this->PDFA || $this->PDFX) {
  9230. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  9231. $this->PDFAXwarnings[] = "Annotation markers cannot be semi-transparent in PDFA1-b or PDFX/1-a, so they may make underlying text unreadable. (Annotation markers moved to right margin)";
  9232. }
  9233. $x = ($this->w) - $this->rMargin * 0.66;
  9234. }
  9235. if (!$this->annotMargin) {
  9236. $y -= $this->FontSize / 2;
  9237. }
  9238. if (!$opacity && $this->annotMargin) {
  9239. $opacity = 1;
  9240. } elseif (!$opacity) {
  9241. $opacity = $this->annotOpacity;
  9242. }
  9243. $an = array('txt' => $text, 'x' => $x, 'y' => $y, 'opt' => array('Icon' => $icon, 'T' => $author, 'Subj' => $subject, 'C' => $colarray, 'CA' => $opacity, 'popup' => $popup, 'file' => $file));
  9244. if ($this->keep_block_together) { // don't write yet
  9245. return;
  9246. } elseif ($this->table_rotate) {
  9247. $this->tbrot_Annots[$this->page][] = $an;
  9248. return;
  9249. } elseif ($this->kwt) {
  9250. $this->kwt_Annots[$this->page][] = $an;
  9251. return;
  9252. }
  9253. if ($this->writingHTMLheader || $this->writingHTMLfooter) {
  9254. $this->HTMLheaderPageAnnots[] = $an;
  9255. return;
  9256. }
  9257. //Put an Annotation on the page
  9258. $this->PageAnnots[$page][] = $an;
  9259. /* -- COLUMNS -- */
  9260. // Save cross-reference to Column buffer
  9261. $ref = count($this->PageAnnots[$this->page]) - 1;
  9262. $this->columnAnnots[$this->CurrCol][INTVAL($this->x)][INTVAL($this->y)] = $ref;
  9263. /* -- END COLUMNS -- */
  9264. }
  9265. /* -- END ANNOTATIONS -- */
  9266. function _putfonts()
  9267. {
  9268. $nf = $this->n;
  9269. foreach ($this->FontFiles as $fontkey => $info) {
  9270. // TrueType embedded
  9271. if (isset($info['type']) && $info['type'] == 'TTF' && !$info['sip'] && !$info['smp']) {
  9272. $used = true;
  9273. $asSubset = false;
  9274. foreach ($this->fonts AS $k => $f) {
  9275. if (isset($f['fontkey']) && $f['fontkey'] == $fontkey && $f['type'] == 'TTF') {
  9276. $used = $f['used'];
  9277. if ($used) {
  9278. $nChars = (ord($f['cw'][0]) << 8) + ord($f['cw'][1]);
  9279. $usage = intval(count($f['subset']) * 100 / $nChars);
  9280. $fsize = $info['length1'];
  9281. // Always subset the very large TTF files
  9282. if ($fsize > ($this->maxTTFFilesize * 1024)) {
  9283. $asSubset = true;
  9284. } elseif ($usage < $this->percentSubset) {
  9285. $asSubset = true;
  9286. }
  9287. }
  9288. if ($this->PDFA || $this->PDFX)
  9289. $asSubset = false;
  9290. $this->fonts[$k]['asSubset'] = $asSubset;
  9291. break;
  9292. }
  9293. }
  9294. if ($used && !$asSubset) {
  9295. //Font file embedding
  9296. $this->_newobj();
  9297. $this->FontFiles[$fontkey]['n'] = $this->n;
  9298. $font = '';
  9299. $originalsize = $info['length1'];
  9300. if ($this->repackageTTF || $this->fonts[$fontkey]['TTCfontID'] > 0 || $this->fonts[$fontkey]['useOTL'] > 0) { // mPDF 5.7.1
  9301. // First see if there is a cached compressed file
  9302. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.ps.z')) {
  9303. $f = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.ps.z', 'rb');
  9304. if (!$f) {
  9305. throw new MpdfException('Font file .ps.z not found');
  9306. }
  9307. while (!feof($f)) {
  9308. $font .= fread($f, 2048);
  9309. }
  9310. fclose($f);
  9311. include(_MPDF_TTFONTDATAPATH . $fontkey . '.ps.php'); // sets $originalsize (of repackaged font)
  9312. } else {
  9313. if (!class_exists('TTFontFile', false)) {
  9314. include(_MPDF_PATH . 'classes/ttfontsuni.php');
  9315. }
  9316. $ttf = new TTFontFile();
  9317. $font = $ttf->repackageTTF($this->FontFiles[$fontkey]['ttffile'], $this->fonts[$fontkey]['TTCfontID'], $this->debugfonts, $this->fonts[$fontkey]['useOTL']); // mPDF 5.7.1
  9318. $originalsize = strlen($font);
  9319. $font = gzcompress($font);
  9320. unset($ttf);
  9321. if (is_writable(dirname(_MPDF_TTFONTDATAPATH . 'x'))) {
  9322. $fh = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.ps.z', "wb");
  9323. fwrite($fh, $font, strlen($font));
  9324. fclose($fh);
  9325. $fh = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.ps.php', "wb");
  9326. $len = "<?php \n";
  9327. $len.='$originalsize=' . $originalsize . ";\n";
  9328. $len.="?>";
  9329. fwrite($fh, $len, strlen($len));
  9330. fclose($fh);
  9331. }
  9332. }
  9333. } else {
  9334. // First see if there is a cached compressed file
  9335. if (file_exists(_MPDF_TTFONTDATAPATH . $fontkey . '.z')) {
  9336. $f = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.z', 'rb');
  9337. if (!$f) {
  9338. throw new MpdfException('Font file not found');
  9339. }
  9340. while (!feof($f)) {
  9341. $font .= fread($f, 2048);
  9342. }
  9343. fclose($f);
  9344. } else {
  9345. $f = fopen($this->FontFiles[$fontkey]['ttffile'], 'rb');
  9346. if (!$f) {
  9347. throw new MpdfException('Font file not found');
  9348. }
  9349. while (!feof($f)) {
  9350. $font .= fread($f, 2048);
  9351. }
  9352. fclose($f);
  9353. $font = gzcompress($font);
  9354. if (is_writable(dirname(_MPDF_TTFONTDATAPATH . 'x'))) {
  9355. $fh = fopen(_MPDF_TTFONTDATAPATH . $fontkey . '.z', "wb");
  9356. fwrite($fh, $font, strlen($font));
  9357. fclose($fh);
  9358. }
  9359. }
  9360. }
  9361. $this->_out('<</Length ' . strlen($font));
  9362. $this->_out('/Filter /FlateDecode');
  9363. $this->_out('/Length1 ' . $originalsize);
  9364. $this->_out('>>');
  9365. $this->_putstream($font);
  9366. $this->_out('endobj');
  9367. }
  9368. }
  9369. }
  9370. $nfonts = count($this->fonts);
  9371. $fctr = 1;
  9372. foreach ($this->fonts as $k => $font) {
  9373. //Font objects
  9374. $type = $font['type'];
  9375. $name = $font['name'];
  9376. if ((!isset($font['used']) || !$font['used']) && $type == 'TTF') {
  9377. continue;
  9378. }
  9379. if ($this->progressBar) {
  9380. $this->UpdateProgressBar(2, intval($fctr * 100 / $nfonts), 'Writing Fonts');
  9381. $fctr++;
  9382. } // *PROGRESS-BAR*
  9383. if (isset($font['asSubset'])) {
  9384. $asSubset = $font['asSubset'];
  9385. } else {
  9386. $asSubset = '';
  9387. }
  9388. /* -- CJK-FONTS -- */
  9389. if ($type == 'Type0') { // = Adobe CJK Fonts
  9390. $this->fonts[$k]['n'] = $this->n + 1;
  9391. $this->_newobj();
  9392. $this->_out('<</Type /Font');
  9393. $this->_putType0($font);
  9394. } else
  9395. /* -- END CJK-FONTS -- */
  9396. if ($type == 'core') {
  9397. //Standard font
  9398. $this->fonts[$k]['n'] = $this->n + 1;
  9399. if ($this->PDFA || $this->PDFX) {
  9400. throw new MpdfException('Core fonts are not allowed in PDF/A1-b or PDFX/1-a files (Times, Helvetica, Courier etc.)');
  9401. }
  9402. $this->_newobj();
  9403. $this->_out('<</Type /Font');
  9404. $this->_out('/BaseFont /' . $name);
  9405. $this->_out('/Subtype /Type1');
  9406. if ($name != 'Symbol' && $name != 'ZapfDingbats') {
  9407. $this->_out('/Encoding /WinAnsiEncoding');
  9408. }
  9409. $this->_out('>>');
  9410. $this->_out('endobj');
  9411. }
  9412. // TrueType embedded SUBSETS for SIP (CJK extB containing Supplementary Ideographic Plane 2)
  9413. // Or Unicode Plane 1 - Supplementary Multilingual Plane
  9414. elseif ($type == 'TTF' && ($font['sip'] || $font['smp'])) {
  9415. if (!$font['used']) {
  9416. continue;
  9417. }
  9418. $ssfaid = "AA";
  9419. if (!class_exists('TTFontFile', false)) {
  9420. include(_MPDF_PATH . 'classes/ttfontsuni.php');
  9421. }
  9422. $ttf = new TTFontFile();
  9423. for ($sfid = 0; $sfid < count($font['subsetfontids']); $sfid++) {
  9424. $this->fonts[$k]['n'][$sfid] = $this->n + 1; // NB an array for subset
  9425. $subsetname = 'MPDF' . $ssfaid . '+' . $font['name'];
  9426. $ssfaid++;
  9427. /* For some strange reason a subset ($sfid > 0) containing less than 97 characters causes an error
  9428. so fill up the array */
  9429. for ($j = count($font['subsets'][$sfid]); $j < 98; $j++) {
  9430. $font['subsets'][$sfid][$j] = 0;
  9431. }
  9432. $subset = $font['subsets'][$sfid];
  9433. unset($subset[0]);
  9434. $ttfontstream = $ttf->makeSubsetSIP($font['ttffile'], $subset, $font['TTCfontID'], $this->debugfonts, $font['useOTL']); // mPDF 5.7.1
  9435. $ttfontsize = strlen($ttfontstream);
  9436. $fontstream = gzcompress($ttfontstream);
  9437. $widthstring = '';
  9438. $toUnistring = '';
  9439. foreach ($font['subsets'][$sfid] AS $cp => $u) {
  9440. $w = $this->_getCharWidth($font['cw'], $u);
  9441. if ($w !== false) {
  9442. $widthstring .= $w . ' ';
  9443. } else {
  9444. $widthstring .= round($ttf->defaultWidth) . ' ';
  9445. }
  9446. if ($u > 65535) {
  9447. $utf8 = chr(($u >> 18) + 240) . chr((($u >> 12) & 63) + 128) . chr((($u >> 6) & 63) + 128) . chr(($u & 63) + 128);
  9448. $utf16 = mb_convert_encoding($utf8, 'UTF-16BE', 'UTF-8');
  9449. $l1 = ord($utf16[0]);
  9450. $h1 = ord($utf16[1]);
  9451. $l2 = ord($utf16[2]);
  9452. $h2 = ord($utf16[3]);
  9453. $toUnistring .= sprintf("<%02s> <%02s%02s%02s%02s>\n", strtoupper(dechex($cp)), strtoupper(dechex($l1)), strtoupper(dechex($h1)), strtoupper(dechex($l2)), strtoupper(dechex($h2)));
  9454. } else {
  9455. $toUnistring .= sprintf("<%02s> <%04s>\n", strtoupper(dechex($cp)), strtoupper(dechex($u)));
  9456. }
  9457. }
  9458. //Additional Type1 or TrueType font
  9459. $this->_newobj();
  9460. $this->_out('<</Type /Font');
  9461. $this->_out('/BaseFont /' . $subsetname);
  9462. $this->_out('/Subtype /TrueType');
  9463. $this->_out('/FirstChar 0 /LastChar ' . (count($font['subsets'][$sfid]) - 1));
  9464. $this->_out('/Widths ' . ($this->n + 1) . ' 0 R');
  9465. $this->_out('/FontDescriptor ' . ($this->n + 2) . ' 0 R');
  9466. $this->_out('/ToUnicode ' . ($this->n + 3) . ' 0 R');
  9467. $this->_out('>>');
  9468. $this->_out('endobj');
  9469. //Widths
  9470. $this->_newobj();
  9471. $this->_out('[' . $widthstring . ']');
  9472. $this->_out('endobj');
  9473. //Descriptor
  9474. $this->_newobj();
  9475. $s = '<</Type /FontDescriptor /FontName /' . $subsetname . "\n";
  9476. foreach ($font['desc'] as $kd => $v) {
  9477. if ($kd == 'Flags') {
  9478. $v = $v | 4;
  9479. $v = $v & ~32;
  9480. } // SYMBOLIC font flag
  9481. $s.=' /' . $kd . ' ' . $v . "\n";
  9482. }
  9483. $s.='/FontFile2 ' . ($this->n + 2) . ' 0 R';
  9484. $this->_out($s . '>>');
  9485. $this->_out('endobj');
  9486. // ToUnicode
  9487. $this->_newobj();
  9488. $toUni = "/CIDInit /ProcSet findresource begin\n";
  9489. $toUni .= "12 dict begin\n";
  9490. $toUni .= "begincmap\n";
  9491. $toUni .= "/CIDSystemInfo\n";
  9492. $toUni .= "<</Registry (Adobe)\n";
  9493. $toUni .= "/Ordering (UCS)\n";
  9494. $toUni .= "/Supplement 0\n";
  9495. $toUni .= ">> def\n";
  9496. $toUni .= "/CMapName /Adobe-Identity-UCS def\n";
  9497. $toUni .= "/CMapType 2 def\n";
  9498. $toUni .= "1 begincodespacerange\n";
  9499. $toUni .= "<00> <FF>\n";
  9500. //$toUni .= sprintf("<00> <%02s>\n", strtoupper(dechex(count($font['subsets'][$sfid])-1)));
  9501. $toUni .= "endcodespacerange\n";
  9502. $toUni .= count($font['subsets'][$sfid]) . " beginbfchar\n";
  9503. $toUni .= $toUnistring;
  9504. $toUni .= "endbfchar\n";
  9505. $toUni .= "endcmap\n";
  9506. $toUni .= "CMapName currentdict /CMap defineresource pop\n";
  9507. $toUni .= "end\n";
  9508. $toUni .= "end\n";
  9509. $this->_out('<</Length ' . (strlen($toUni)) . '>>');
  9510. $this->_putstream($toUni);
  9511. $this->_out('endobj');
  9512. //Font file
  9513. $this->_newobj();
  9514. $this->_out('<</Length ' . strlen($fontstream));
  9515. $this->_out('/Filter /FlateDecode');
  9516. $this->_out('/Length1 ' . $ttfontsize);
  9517. $this->_out('>>');
  9518. $this->_putstream($fontstream);
  9519. $this->_out('endobj');
  9520. } // foreach subset
  9521. unset($ttf);
  9522. }
  9523. // TrueType embedded SUBSETS or FULL
  9524. elseif ($type == 'TTF') {
  9525. $this->fonts[$k]['n'] = $this->n + 1;
  9526. if ($asSubset) {
  9527. $ssfaid = "A";
  9528. if (!class_exists('TTFontFile', false)) {
  9529. include(_MPDF_PATH . 'classes/ttfontsuni.php');
  9530. }
  9531. $ttf = new TTFontFile();
  9532. $fontname = 'MPDFA' . $ssfaid . '+' . $font['name'];
  9533. $subset = $font['subset'];
  9534. unset($subset[0]);
  9535. $ttfontstream = $ttf->makeSubset($font['ttffile'], $subset, $font['TTCfontID'], $this->debugfonts, $font['useOTL']);
  9536. $ttfontsize = strlen($ttfontstream);
  9537. $fontstream = gzcompress($ttfontstream);
  9538. $codeToGlyph = $ttf->codeToGlyph;
  9539. unset($codeToGlyph[0]);
  9540. } else {
  9541. $fontname = $font['name'];
  9542. }
  9543. // Type0 Font
  9544. // A composite font - a font composed of other fonts, organized hierarchically
  9545. $this->_newobj();
  9546. $this->_out('<</Type /Font');
  9547. $this->_out('/Subtype /Type0');
  9548. $this->_out('/BaseFont /' . $fontname . '');
  9549. $this->_out('/Encoding /Identity-H');
  9550. $this->_out('/DescendantFonts [' . ($this->n + 1) . ' 0 R]');
  9551. $this->_out('/ToUnicode ' . ($this->n + 2) . ' 0 R');
  9552. $this->_out('>>');
  9553. $this->_out('endobj');
  9554. // CIDFontType2
  9555. // A CIDFont whose glyph descriptions are based on TrueType font technology
  9556. $this->_newobj();
  9557. $this->_out('<</Type /Font');
  9558. $this->_out('/Subtype /CIDFontType2');
  9559. $this->_out('/BaseFont /' . $fontname . '');
  9560. $this->_out('/CIDSystemInfo ' . ($this->n + 2) . ' 0 R');
  9561. $this->_out('/FontDescriptor ' . ($this->n + 3) . ' 0 R');
  9562. if (isset($font['desc']['MissingWidth'])) {
  9563. $this->_out('/DW ' . $font['desc']['MissingWidth'] . '');
  9564. }
  9565. if (!$asSubset && file_exists(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw')) {
  9566. $w = '';
  9567. $w = file_get_contents(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw');
  9568. $this->_out($w);
  9569. } else {
  9570. $this->_putTTfontwidths($font, $asSubset, ($asSubset ? $ttf->maxUni : 0));
  9571. }
  9572. $this->_out('/CIDToGIDMap ' . ($this->n + 4) . ' 0 R');
  9573. $this->_out('>>');
  9574. $this->_out('endobj');
  9575. // ToUnicode
  9576. $this->_newobj();
  9577. $toUni = "/CIDInit /ProcSet findresource begin\n";
  9578. $toUni .= "12 dict begin\n";
  9579. $toUni .= "begincmap\n";
  9580. $toUni .= "/CIDSystemInfo\n";
  9581. $toUni .= "<</Registry (Adobe)\n";
  9582. $toUni .= "/Ordering (UCS)\n";
  9583. $toUni .= "/Supplement 0\n";
  9584. $toUni .= ">> def\n";
  9585. $toUni .= "/CMapName /Adobe-Identity-UCS def\n";
  9586. $toUni .= "/CMapType 2 def\n";
  9587. $toUni .= "1 begincodespacerange\n";
  9588. $toUni .= "<0000> <FFFF>\n";
  9589. $toUni .= "endcodespacerange\n";
  9590. $toUni .= "1 beginbfrange\n";
  9591. $toUni .= "<0000> <FFFF> <0000>\n";
  9592. $toUni .= "endbfrange\n";
  9593. $toUni .= "endcmap\n";
  9594. $toUni .= "CMapName currentdict /CMap defineresource pop\n";
  9595. $toUni .= "end\n";
  9596. $toUni .= "end\n";
  9597. $this->_out('<</Length ' . (strlen($toUni)) . '>>');
  9598. $this->_putstream($toUni);
  9599. $this->_out('endobj');
  9600. // CIDSystemInfo dictionary
  9601. $this->_newobj();
  9602. $this->_out('<</Registry (Adobe)');
  9603. $this->_out('/Ordering (UCS)');
  9604. $this->_out('/Supplement 0');
  9605. $this->_out('>>');
  9606. $this->_out('endobj');
  9607. // Font descriptor
  9608. $this->_newobj();
  9609. $this->_out('<</Type /FontDescriptor');
  9610. $this->_out('/FontName /' . $fontname);
  9611. foreach ($font['desc'] as $kd => $v) {
  9612. if ($asSubset && $kd == 'Flags') {
  9613. $v = $v | 4;
  9614. $v = $v & ~32;
  9615. } // SYMBOLIC font flag
  9616. $this->_out(' /' . $kd . ' ' . $v);
  9617. }
  9618. if ($font['panose']) {
  9619. $this->_out(' /Style << /Panose <' . $font['panose'] . '> >>');
  9620. }
  9621. if ($asSubset) {
  9622. $this->_out('/FontFile2 ' . ($this->n + 2) . ' 0 R');
  9623. } elseif ($font['fontkey']) {
  9624. // obj ID of a stream containing a TrueType font program
  9625. $this->_out('/FontFile2 ' . $this->FontFiles[$font['fontkey']]['n'] . ' 0 R');
  9626. }
  9627. $this->_out('>>');
  9628. $this->_out('endobj');
  9629. // Embed CIDToGIDMap
  9630. // A specification of the mapping from CIDs to glyph indices
  9631. if ($asSubset) {
  9632. $cidtogidmap = '';
  9633. $cidtogidmap = str_pad('', 256 * 256 * 2, "\x00");
  9634. foreach ($codeToGlyph as $cc => $glyph) {
  9635. $cidtogidmap[$cc * 2] = chr($glyph >> 8);
  9636. $cidtogidmap[$cc * 2 + 1] = chr($glyph & 0xFF);
  9637. }
  9638. $cidtogidmap = gzcompress($cidtogidmap);
  9639. } else {
  9640. // First see if there is a cached CIDToGIDMapfile
  9641. $cidtogidmap = '';
  9642. if (file_exists(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cgm')) {
  9643. $f = fopen(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cgm', 'rb');
  9644. while (!feof($f)) {
  9645. $cidtogidmap .= fread($f, 2048);
  9646. }
  9647. fclose($f);
  9648. } else {
  9649. if (!class_exists('TTFontFile', false)) {
  9650. include(_MPDF_PATH . 'classes/ttfontsuni.php');
  9651. }
  9652. $ttf = new TTFontFile();
  9653. $charToGlyph = $ttf->getCTG($font['ttffile'], $font['TTCfontID'], $this->debugfonts, $font['useOTL']);
  9654. $cidtogidmap = str_pad('', 256 * 256 * 2, "\x00");
  9655. foreach ($charToGlyph as $cc => $glyph) {
  9656. $cidtogidmap[$cc * 2] = chr($glyph >> 8);
  9657. $cidtogidmap[$cc * 2 + 1] = chr($glyph & 0xFF);
  9658. }
  9659. unset($ttf);
  9660. $cidtogidmap = gzcompress($cidtogidmap);
  9661. if (is_writable(dirname(_MPDF_TTFONTDATAPATH . 'x'))) {
  9662. $fh = fopen(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cgm', "wb");
  9663. fwrite($fh, $cidtogidmap, strlen($cidtogidmap));
  9664. fclose($fh);
  9665. }
  9666. }
  9667. }
  9668. $this->_newobj();
  9669. $this->_out('<</Length ' . strlen($cidtogidmap) . '');
  9670. $this->_out('/Filter /FlateDecode');
  9671. $this->_out('>>');
  9672. $this->_putstream($cidtogidmap);
  9673. $this->_out('endobj');
  9674. //Font file
  9675. if ($asSubset) {
  9676. $this->_newobj();
  9677. $this->_out('<</Length ' . strlen($fontstream));
  9678. $this->_out('/Filter /FlateDecode');
  9679. $this->_out('/Length1 ' . $ttfontsize);
  9680. $this->_out('>>');
  9681. $this->_putstream($fontstream);
  9682. $this->_out('endobj');
  9683. unset($ttf);
  9684. }
  9685. } else {
  9686. throw new MpdfException('Unsupported font type: ' . $type . ' (' . $name . ')');
  9687. }
  9688. }
  9689. }
  9690. function _putTTfontwidths(&$font, $asSubset, $maxUni)
  9691. {
  9692. if ($asSubset && file_exists(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw127.php')) {
  9693. include(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw127.php');
  9694. $startcid = 128;
  9695. } else {
  9696. $rangeid = 0;
  9697. $range = array();
  9698. $prevcid = -2;
  9699. $prevwidth = -1;
  9700. $interval = false;
  9701. $startcid = 1;
  9702. }
  9703. if ($asSubset) {
  9704. $cwlen = $maxUni + 1;
  9705. } else {
  9706. $cwlen = (strlen($font['cw']) / 2);
  9707. }
  9708. // for each character
  9709. for ($cid = $startcid; $cid < $cwlen; $cid++) {
  9710. if ($cid == 128 && $asSubset && (!file_exists(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw127.php'))) {
  9711. if (is_writable(dirname(_MPDF_TTFONTDATAPATH . 'x'))) {
  9712. $fh = fopen(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw127.php', "wb");
  9713. $cw127 = '<?php' . "\n";
  9714. $cw127.='$rangeid=' . $rangeid . ";\n";
  9715. $cw127.='$prevcid=' . $prevcid . ";\n";
  9716. $cw127.='$prevwidth=' . $prevwidth . ";\n";
  9717. if ($interval) {
  9718. $cw127.='$interval=true' . ";\n";
  9719. } else {
  9720. $cw127.='$interval=false' . ";\n";
  9721. }
  9722. $cw127.='$range=' . var_export($range, true) . ";\n";
  9723. $cw127.="?>";
  9724. fwrite($fh, $cw127, strlen($cw127));
  9725. fclose($fh);
  9726. }
  9727. }
  9728. if ($font['cw'][$cid * 2] == "\00" && $font['cw'][$cid * 2 + 1] == "\00") {
  9729. continue;
  9730. }
  9731. $width = (ord($font['cw'][$cid * 2]) << 8) + ord($font['cw'][$cid * 2 + 1]);
  9732. if ($width == 65535) {
  9733. $width = 0;
  9734. }
  9735. if ($asSubset && $cid > 255 && (!isset($font['subset'][$cid]) || !$font['subset'][$cid])) {
  9736. continue;
  9737. }
  9738. if ($asSubset && $cid > 0xFFFF) {
  9739. continue;
  9740. } // mPDF 6
  9741. if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
  9742. if ($cid == ($prevcid + 1)) {
  9743. // consecutive CID
  9744. if ($width == $prevwidth) {
  9745. if ($width == $range[$rangeid][0]) {
  9746. $range[$rangeid][] = $width;
  9747. } else {
  9748. array_pop($range[$rangeid]);
  9749. // new range
  9750. $rangeid = $prevcid;
  9751. $range[$rangeid] = array();
  9752. $range[$rangeid][] = $prevwidth;
  9753. $range[$rangeid][] = $width;
  9754. }
  9755. $interval = true;
  9756. $range[$rangeid]['interval'] = true;
  9757. } else {
  9758. if ($interval) {
  9759. // new range
  9760. $rangeid = $cid;
  9761. $range[$rangeid] = array();
  9762. $range[$rangeid][] = $width;
  9763. } else {
  9764. $range[$rangeid][] = $width;
  9765. }
  9766. $interval = false;
  9767. }
  9768. } else {
  9769. // new range
  9770. $rangeid = $cid;
  9771. $range[$rangeid] = array();
  9772. $range[$rangeid][] = $width;
  9773. $interval = false;
  9774. }
  9775. $prevcid = $cid;
  9776. $prevwidth = $width;
  9777. }
  9778. }
  9779. $w = $this->_putfontranges($range);
  9780. $this->_out($w);
  9781. if (!$asSubset) {
  9782. if (is_writable(dirname(_MPDF_TTFONTDATAPATH . 'x'))) {
  9783. $fh = fopen(_MPDF_TTFONTDATAPATH . $font['fontkey'] . '.cw', "wb");
  9784. fwrite($fh, $w, strlen($w));
  9785. fclose($fh);
  9786. }
  9787. }
  9788. }
  9789. function _putfontranges(&$range)
  9790. {
  9791. // optimize ranges
  9792. $prevk = -1;
  9793. $nextk = -1;
  9794. $prevint = false;
  9795. foreach ($range as $k => $ws) {
  9796. $cws = count($ws);
  9797. if (($k == $nextk) AND ( !$prevint) AND ( (!isset($ws['interval'])) OR ( $cws < 4))) {
  9798. if (isset($range[$k]['interval'])) {
  9799. unset($range[$k]['interval']);
  9800. }
  9801. $range[$prevk] = array_merge($range[$prevk], $range[$k]);
  9802. unset($range[$k]);
  9803. } else {
  9804. $prevk = $k;
  9805. }
  9806. $nextk = $k + $cws;
  9807. if (isset($ws['interval'])) {
  9808. if ($cws > 3) {
  9809. $prevint = true;
  9810. } else {
  9811. $prevint = false;
  9812. }
  9813. unset($range[$k]['interval']);
  9814. --$nextk;
  9815. } else {
  9816. $prevint = false;
  9817. }
  9818. }
  9819. // output data
  9820. $w = '';
  9821. foreach ($range as $k => $ws) {
  9822. if (count(array_count_values($ws)) == 1) {
  9823. // interval mode is more compact
  9824. $w .= ' ' . $k . ' ' . ($k + count($ws) - 1) . ' ' . $ws[0];
  9825. } else {
  9826. // range mode
  9827. $w .= ' ' . $k . ' [ ' . implode(' ', $ws) . ' ]' . "\n";
  9828. }
  9829. }
  9830. return '/W [' . $w . ' ]';
  9831. }
  9832. function _putfontwidths(&$font, $cidoffset = 0)
  9833. {
  9834. ksort($font['cw']);
  9835. unset($font['cw'][65535]);
  9836. $rangeid = 0;
  9837. $range = array();
  9838. $prevcid = -2;
  9839. $prevwidth = -1;
  9840. $interval = false;
  9841. // for each character
  9842. foreach ($font['cw'] as $cid => $width) {
  9843. $cid -= $cidoffset;
  9844. if (!isset($font['dw']) || (isset($font['dw']) && $width != $font['dw'])) {
  9845. if ($cid == ($prevcid + 1)) {
  9846. // consecutive CID
  9847. if ($width == $prevwidth) {
  9848. if ($width == $range[$rangeid][0]) {
  9849. $range[$rangeid][] = $width;
  9850. } else {
  9851. array_pop($range[$rangeid]);
  9852. // new range
  9853. $rangeid = $prevcid;
  9854. $range[$rangeid] = array();
  9855. $range[$rangeid][] = $prevwidth;
  9856. $range[$rangeid][] = $width;
  9857. }
  9858. $interval = true;
  9859. $range[$rangeid]['interval'] = true;
  9860. } else {
  9861. if ($interval) {
  9862. // new range
  9863. $rangeid = $cid;
  9864. $range[$rangeid] = array();
  9865. $range[$rangeid][] = $width;
  9866. } else {
  9867. $range[$rangeid][] = $width;
  9868. }
  9869. $interval = false;
  9870. }
  9871. } else {
  9872. // new range
  9873. $rangeid = $cid;
  9874. $range[$rangeid] = array();
  9875. $range[$rangeid][] = $width;
  9876. $interval = false;
  9877. }
  9878. $prevcid = $cid;
  9879. $prevwidth = $width;
  9880. }
  9881. }
  9882. $this->_out($this->_putfontranges($range));
  9883. }
  9884. /* -- CJK-FONTS -- */
  9885. // from class PDF_Chinese CJK EXTENSIONS
  9886. function _putType0(&$font)
  9887. {
  9888. //Type0
  9889. $this->_out('/Subtype /Type0');
  9890. $this->_out('/BaseFont /' . $font['name'] . '-' . $font['CMap']);
  9891. $this->_out('/Encoding /' . $font['CMap']);
  9892. $this->_out('/DescendantFonts [' . ($this->n + 1) . ' 0 R]');
  9893. $this->_out('>>');
  9894. $this->_out('endobj');
  9895. //CIDFont
  9896. $this->_newobj();
  9897. $this->_out('<</Type /Font');
  9898. $this->_out('/Subtype /CIDFontType0');
  9899. $this->_out('/BaseFont /' . $font['name']);
  9900. $cidinfo = '/Registry ' . $this->_textstring('Adobe');
  9901. $cidinfo .= ' /Ordering ' . $this->_textstring($font['registry']['ordering']);
  9902. $cidinfo .= ' /Supplement ' . $font['registry']['supplement'];
  9903. $this->_out('/CIDSystemInfo <<' . $cidinfo . '>>');
  9904. $this->_out('/FontDescriptor ' . ($this->n + 1) . ' 0 R');
  9905. if (isset($font['MissingWidth'])) {
  9906. $this->_out('/DW ' . $font['MissingWidth'] . '');
  9907. }
  9908. $this->_putfontwidths($font, 31);
  9909. $this->_out('>>');
  9910. $this->_out('endobj');
  9911. //Font descriptor
  9912. $this->_newobj();
  9913. $s = '<</Type /FontDescriptor /FontName /' . $font['name'];
  9914. foreach ($font['desc'] as $k => $v) {
  9915. if ($k != 'Style') {
  9916. $s .= ' /' . $k . ' ' . $v . '';
  9917. }
  9918. }
  9919. $this->_out($s . '>>');
  9920. $this->_out('endobj');
  9921. }
  9922. /* -- END CJK-FONTS -- */
  9923. function _putimages()
  9924. {
  9925. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  9926. reset($this->images);
  9927. while (list($file, $info) = each($this->images)) {
  9928. $this->_newobj();
  9929. $this->images[$file]['n'] = $this->n;
  9930. $this->_out('<</Type /XObject');
  9931. $this->_out('/Subtype /Image');
  9932. $this->_out('/Width ' . $info['w']);
  9933. $this->_out('/Height ' . $info['h']);
  9934. if (isset($info['interpolation']) && $info['interpolation']) {
  9935. $this->_out('/Interpolate true'); // mPDF 6 - image interpolation shall be performed by a conforming reader
  9936. }
  9937. if (isset($info['masked'])) {
  9938. $this->_out('/SMask ' . ($this->n - 1) . ' 0 R');
  9939. }
  9940. // set color space
  9941. $icc = false;
  9942. if (isset($info['icc']) AND ( $info['icc'] !== false)) {
  9943. // ICC Colour Space
  9944. $icc = true;
  9945. $this->_out('/ColorSpace [/ICCBased ' . ($this->n + 1) . ' 0 R]');
  9946. } elseif ($info['cs'] == 'Indexed') {
  9947. if ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
  9948. throw new MpdfException("PDFA1-b and PDFX/1-a files do not permit using mixed colour space (" . $file . ").");
  9949. }
  9950. $this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal']) / 3 - 1) . ' ' . ($this->n + 1) . ' 0 R]');
  9951. } else {
  9952. $this->_out('/ColorSpace /' . $info['cs']);
  9953. if ($info['cs'] == 'DeviceCMYK') {
  9954. if ($this->PDFA && $this->restrictColorSpace != 3) {
  9955. throw new MpdfException("PDFA1-b does not permit Images using mixed colour space (" . $file . ").");
  9956. }
  9957. if ($info['type'] == 'jpg') {
  9958. $this->_out('/Decode [1 0 1 0 1 0 1 0]');
  9959. }
  9960. } elseif ($info['cs'] == 'DeviceRGB' && ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3))) {
  9961. throw new MpdfException("PDFA1-b and PDFX/1-a files do not permit using mixed colour space (" . $file . ").");
  9962. }
  9963. }
  9964. $this->_out('/BitsPerComponent ' . $info['bpc']);
  9965. if (isset($info['f']) && $info['f']) {
  9966. $this->_out('/Filter /' . $info['f']);
  9967. }
  9968. if (isset($info['parms'])) {
  9969. $this->_out($info['parms']);
  9970. }
  9971. if (isset($info['trns']) and is_array($info['trns'])) {
  9972. $trns = '';
  9973. for ($i = 0; $i < count($info['trns']); $i++)
  9974. $trns.=$info['trns'][$i] . ' ' . $info['trns'][$i] . ' ';
  9975. $this->_out('/Mask [' . $trns . ']');
  9976. }
  9977. $this->_out('/Length ' . strlen($info['data']) . '>>');
  9978. $this->_putstream($info['data']);
  9979. unset($this->images[$file]['data']);
  9980. $this->_out('endobj');
  9981. // ICC colour profile
  9982. if ($icc) {
  9983. $this->_newobj();
  9984. $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
  9985. $this->_out('<</N ' . $info['ch'] . ' ' . $filter . '/Length ' . strlen($icc) . '>>');
  9986. $this->_putstream($icc);
  9987. $this->_out('endobj');
  9988. }
  9989. //Palette
  9990. elseif ($info['cs'] == 'Indexed') {
  9991. $this->_newobj();
  9992. $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
  9993. $this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
  9994. $this->_putstream($pal);
  9995. $this->_out('endobj');
  9996. }
  9997. }
  9998. }
  9999. function _putinfo()
  10000. {
  10001. $this->_out('/Producer ' . $this->_UTF16BEtextstring('mPDF ' . mPDF_VERSION));
  10002. if (!empty($this->title))
  10003. $this->_out('/Title ' . $this->_UTF16BEtextstring($this->title));
  10004. if (!empty($this->subject))
  10005. $this->_out('/Subject ' . $this->_UTF16BEtextstring($this->subject));
  10006. if (!empty($this->author))
  10007. $this->_out('/Author ' . $this->_UTF16BEtextstring($this->author));
  10008. if (!empty($this->keywords))
  10009. $this->_out('/Keywords ' . $this->_UTF16BEtextstring($this->keywords));
  10010. if (!empty($this->creator))
  10011. $this->_out('/Creator ' . $this->_UTF16BEtextstring($this->creator));
  10012. $z = date('O'); // +0200
  10013. $offset = substr($z, 0, 3) . "'" . substr($z, 3, 2) . "'";
  10014. $this->_out('/CreationDate ' . $this->_textstring(date('YmdHis') . $offset));
  10015. $this->_out('/ModDate ' . $this->_textstring(date('YmdHis') . $offset));
  10016. if ($this->PDFX) {
  10017. $this->_out('/Trapped/False');
  10018. $this->_out('/GTS_PDFXVersion(PDF/X-1a:2003)');
  10019. }
  10020. }
  10021. function _putmetadata()
  10022. {
  10023. $this->_newobj();
  10024. $this->MetadataRoot = $this->n;
  10025. $Producer = 'mPDF ' . mPDF_VERSION;
  10026. $z = date('O'); // +0200
  10027. $offset = substr($z, 0, 3) . ':' . substr($z, 3, 2);
  10028. $CreationDate = date('Y-m-d\TH:i:s') . $offset; // 2006-03-10T10:47:26-05:00 2006-06-19T09:05:17Z
  10029. $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
  10030. $m = '<?xpacket begin="' . chr(239) . chr(187) . chr(191) . '" id="W5M0MpCehiHzreSzNTczkc9d"?>' . "\n"; // begin = FEFF BOM
  10031. $m .= ' <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="3.1-701">' . "\n";
  10032. $m .= ' <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' . "\n";
  10033. $m .= ' <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">' . "\n";
  10034. $m .= ' <pdf:Producer>' . $Producer . '</pdf:Producer>' . "\n";
  10035. if (!empty($this->keywords)) {
  10036. $m .= ' <pdf:Keywords>' . $this->keywords . '</pdf:Keywords>' . "\n";
  10037. }
  10038. $m .= ' </rdf:Description>' . "\n";
  10039. $m .= ' <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:xmp="http://ns.adobe.com/xap/1.0/">' . "\n";
  10040. $m .= ' <xmp:CreateDate>' . $CreationDate . '</xmp:CreateDate>' . "\n";
  10041. $m .= ' <xmp:ModifyDate>' . $CreationDate . '</xmp:ModifyDate>' . "\n";
  10042. $m .= ' <xmp:MetadataDate>' . $CreationDate . '</xmp:MetadataDate>' . "\n";
  10043. if (!empty($this->creator)) {
  10044. $m .= ' <xmp:CreatorTool>' . $this->creator . '</xmp:CreatorTool>' . "\n";
  10045. }
  10046. $m .= ' </rdf:Description>' . "\n";
  10047. // DC elements
  10048. $m .= ' <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:dc="http://purl.org/dc/elements/1.1/">' . "\n";
  10049. $m .= ' <dc:format>application/pdf</dc:format>' . "\n";
  10050. if (!empty($this->title)) {
  10051. $m .= ' <dc:title>
  10052. <rdf:Alt>
  10053. <rdf:li xml:lang="x-default">' . $this->title . '</rdf:li>
  10054. </rdf:Alt>
  10055. </dc:title>' . "\n";
  10056. }
  10057. if (!empty($this->keywords)) {
  10058. $m .= ' <dc:subject>
  10059. <rdf:Bag>
  10060. <rdf:li>' . $this->keywords . '</rdf:li>
  10061. </rdf:Bag>
  10062. </dc:subject>' . "\n";
  10063. }
  10064. if (!empty($this->subject)) {
  10065. $m .= ' <dc:description>
  10066. <rdf:Alt>
  10067. <rdf:li xml:lang="x-default">' . $this->subject . '</rdf:li>
  10068. </rdf:Alt>
  10069. </dc:description>' . "\n";
  10070. }
  10071. if (!empty($this->author)) {
  10072. $m .= ' <dc:creator>
  10073. <rdf:Seq>
  10074. <rdf:li>' . $this->author . '</rdf:li>
  10075. </rdf:Seq>
  10076. </dc:creator>' . "\n";
  10077. }
  10078. $m .= ' </rdf:Description>' . "\n";
  10079. // This bit is specific to PDFX-1a
  10080. if ($this->PDFX) {
  10081. $m .= ' <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:pdfx="http://ns.adobe.com/pdfx/1.3/" pdfx:Apag_PDFX_Checkup="1.3" pdfx:GTS_PDFXConformance="PDF/X-1a:2003" pdfx:GTS_PDFXVersion="PDF/X-1:2003"/>' . "\n";
  10082. }
  10083. // This bit is specific to PDFA-1b
  10084. elseif ($this->PDFA) {
  10085. $m .= ' <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/" >' . "\n";
  10086. $m .= ' <pdfaid:part>1</pdfaid:part>' . "\n";
  10087. $m .= ' <pdfaid:conformance>B</pdfaid:conformance>' . "\n";
  10088. $m .= ' <pdfaid:amd>2005</pdfaid:amd>' . "\n";
  10089. $m .= ' </rdf:Description>' . "\n";
  10090. }
  10091. $m .= ' <rdf:Description rdf:about="uuid:' . $uuid . '" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">' . "\n";
  10092. $m .= ' <xmpMM:DocumentID>uuid:' . $uuid . '</xmpMM:DocumentID>' . "\n";
  10093. $m .= ' </rdf:Description>' . "\n";
  10094. $m .= ' </rdf:RDF>' . "\n";
  10095. $m .= ' </x:xmpmeta>' . "\n";
  10096. $m .= str_repeat(str_repeat(' ', 100) . "\n", 20); // 2-4kB whitespace padding required
  10097. $m .= '<?xpacket end="w"?>'; // "r" read only
  10098. $this->_out('<</Type/Metadata/Subtype/XML/Length ' . strlen($m) . '>>');
  10099. $this->_putstream($m);
  10100. $this->_out('endobj');
  10101. }
  10102. function _putoutputintent()
  10103. {
  10104. $this->_newobj();
  10105. $this->OutputIntentRoot = $this->n;
  10106. $this->_out('<</Type /OutputIntent');
  10107. if ($this->PDFA) {
  10108. $this->_out('/S /GTS_PDFA1');
  10109. if ($this->ICCProfile) {
  10110. $this->_out('/Info (' . preg_replace('/_/', ' ', $this->ICCProfile) . ')');
  10111. $this->_out('/OutputConditionIdentifier (Custom)');
  10112. $this->_out('/OutputCondition ()');
  10113. } else {
  10114. $this->_out('/Info (sRGB IEC61966-2.1)');
  10115. $this->_out('/OutputConditionIdentifier (sRGB IEC61966-2.1)');
  10116. $this->_out('/OutputCondition ()');
  10117. }
  10118. $this->_out('/DestOutputProfile ' . ($this->n + 1) . ' 0 R');
  10119. } elseif ($this->PDFX) { // always a CMYK profile
  10120. $this->_out('/S /GTS_PDFX');
  10121. if ($this->ICCProfile) {
  10122. $this->_out('/Info (' . preg_replace('/_/', ' ', $this->ICCProfile) . ')');
  10123. $this->_out('/OutputConditionIdentifier (Custom)');
  10124. $this->_out('/OutputCondition ()');
  10125. $this->_out('/DestOutputProfile ' . ($this->n + 1) . ' 0 R');
  10126. } else {
  10127. $this->_out('/Info (CGATS TR 001)');
  10128. $this->_out('/OutputConditionIdentifier (CGATS TR 001)');
  10129. $this->_out('/OutputCondition (CGATS TR 001 (SWOP))');
  10130. $this->_out('/RegistryName (http://www.color.org)');
  10131. }
  10132. }
  10133. $this->_out('>>');
  10134. $this->_out('endobj');
  10135. if ($this->PDFX && !$this->ICCProfile) {
  10136. return;
  10137. } // no ICCProfile embedded
  10138. $this->_newobj();
  10139. if ($this->ICCProfile)
  10140. $s = file_get_contents(_MPDF_PATH . 'iccprofiles/' . $this->ICCProfile . '.icc');
  10141. else
  10142. $s = file_get_contents(_MPDF_PATH . 'iccprofiles/sRGB_IEC61966-2-1.icc');
  10143. if ($this->compress) {
  10144. $s = gzcompress($s);
  10145. }
  10146. $this->_out('<<');
  10147. if ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
  10148. $this->_out('/N 4');
  10149. } else {
  10150. $this->_out('/N 3');
  10151. }
  10152. if ($this->compress)
  10153. $this->_out('/Filter /FlateDecode ');
  10154. $this->_out('/Length ' . strlen($s) . '>>');
  10155. $this->_putstream($s);
  10156. $this->_out('endobj');
  10157. }
  10158. function _putcatalog()
  10159. {
  10160. $this->_out('/Type /Catalog');
  10161. $this->_out('/Pages 1 0 R');
  10162. if ($this->ZoomMode == 'fullpage')
  10163. $this->_out('/OpenAction [3 0 R /Fit]');
  10164. elseif ($this->ZoomMode == 'fullwidth')
  10165. $this->_out('/OpenAction [3 0 R /FitH null]');
  10166. elseif ($this->ZoomMode == 'real')
  10167. $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
  10168. elseif (!is_string($this->ZoomMode))
  10169. $this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->ZoomMode / 100) . ']');
  10170. else
  10171. $this->_out('/OpenAction [3 0 R /XYZ null null null]');
  10172. if ($this->LayoutMode == 'single')
  10173. $this->_out('/PageLayout /SinglePage');
  10174. elseif ($this->LayoutMode == 'continuous')
  10175. $this->_out('/PageLayout /OneColumn');
  10176. elseif ($this->LayoutMode == 'twoleft')
  10177. $this->_out('/PageLayout /TwoColumnLeft');
  10178. elseif ($this->LayoutMode == 'tworight')
  10179. $this->_out('/PageLayout /TwoColumnRight');
  10180. elseif ($this->LayoutMode == 'two') {
  10181. if ($this->mirrorMargins) {
  10182. $this->_out('/PageLayout /TwoColumnRight');
  10183. } else {
  10184. $this->_out('/PageLayout /TwoColumnLeft');
  10185. }
  10186. }
  10187. /* -- BOOKMARKS -- */
  10188. if (count($this->BMoutlines) > 0) {
  10189. $this->_out('/Outlines ' . $this->OutlineRoot . ' 0 R');
  10190. $this->_out('/PageMode /UseOutlines');
  10191. }
  10192. /* -- END BOOKMARKS -- */
  10193. if (is_int(strpos($this->DisplayPreferences, 'FullScreen')))
  10194. $this->_out('/PageMode /FullScreen');
  10195. // Metadata
  10196. if ($this->PDFA || $this->PDFX) {
  10197. $this->_out('/Metadata ' . $this->MetadataRoot . ' 0 R');
  10198. }
  10199. // OutputIntents
  10200. if ($this->PDFA || $this->PDFX || $this->ICCProfile) {
  10201. $this->_out('/OutputIntents [' . $this->OutputIntentRoot . ' 0 R]');
  10202. }
  10203. /* -- FORMS -- */
  10204. if (count($this->mpdfform->forms) > 0) {
  10205. $this->mpdfform->_putFormsCatalog();
  10206. }
  10207. /* -- END FORMS -- */
  10208. if (isset($this->js)) {
  10209. $this->_out('/Names << /JavaScript ' . ($this->n_js) . ' 0 R >> ');
  10210. }
  10211. if ($this->DisplayPreferences || $this->directionality == 'rtl' || $this->mirrorMargins) {
  10212. $this->_out('/ViewerPreferences<<');
  10213. if (is_int(strpos($this->DisplayPreferences, 'HideMenubar')))
  10214. $this->_out('/HideMenubar true');
  10215. if (is_int(strpos($this->DisplayPreferences, 'HideToolbar')))
  10216. $this->_out('/HideToolbar true');
  10217. if (is_int(strpos($this->DisplayPreferences, 'HideWindowUI')))
  10218. $this->_out('/HideWindowUI true');
  10219. if (is_int(strpos($this->DisplayPreferences, 'DisplayDocTitle')))
  10220. $this->_out('/DisplayDocTitle true');
  10221. if (is_int(strpos($this->DisplayPreferences, 'CenterWindow')))
  10222. $this->_out('/CenterWindow true');
  10223. if (is_int(strpos($this->DisplayPreferences, 'FitWindow')))
  10224. $this->_out('/FitWindow true');
  10225. // /PrintScaling is PDF 1.6 spec.
  10226. if (is_int(strpos($this->DisplayPreferences, 'NoPrintScaling')) && !$this->PDFA && !$this->PDFX)
  10227. $this->_out('/PrintScaling /None');
  10228. if ($this->directionality == 'rtl')
  10229. $this->_out('/Direction /R2L');
  10230. // /Duplex is PDF 1.7 spec.
  10231. if ($this->mirrorMargins && !$this->PDFA && !$this->PDFX) {
  10232. // if ($this->DefOrientation=='P') $this->_out('/Duplex /DuplexFlipShortEdge');
  10233. $this->_out('/Duplex /DuplexFlipLongEdge'); // PDF v1.7+
  10234. }
  10235. $this->_out('>>');
  10236. }
  10237. if ($this->open_layer_pane && ($this->hasOC || count($this->layers)))
  10238. $this->_out('/PageMode /UseOC');
  10239. if ($this->hasOC || count($this->layers)) {
  10240. $p = $v = $h = $l = $loff = $lall = $as = '';
  10241. if ($this->hasOC) {
  10242. if (($this->hasOC & 1) == 1)
  10243. $p = $this->n_ocg_print . ' 0 R';
  10244. if (($this->hasOC & 2) == 2)
  10245. $v = $this->n_ocg_view . ' 0 R';
  10246. if (($this->hasOC & 4) == 4)
  10247. $h = $this->n_ocg_hidden . ' 0 R';
  10248. $as = "<</Event /Print /OCGs [$p $v $h] /Category [/Print]>> <</Event /View /OCGs [$p $v $h] /Category [/View]>>";
  10249. }
  10250. if (count($this->layers)) {
  10251. foreach ($this->layers as $k => $layer) {
  10252. if (strtolower($this->layerDetails[$k]['state']) == 'hidden') {
  10253. $loff .= $layer['n'] . ' 0 R ';
  10254. } else {
  10255. $l .= $layer['n'] . ' 0 R ';
  10256. }
  10257. $lall .= $layer['n'] . ' 0 R ';
  10258. }
  10259. }
  10260. $this->_out("/OCProperties <</OCGs [$p $v $h $lall] /D <</ON [$p $l] /OFF [$v $h $loff] ");
  10261. $this->_out("/Order [$v $p $h $lall] ");
  10262. if ($as)
  10263. $this->_out("/AS [$as] ");
  10264. $this->_out(">>>>");
  10265. }
  10266. }
  10267. // Inactive function left for backwards compatability
  10268. function SetUserRights($enable = true, $annots = "", $form = "", $signature = "")
  10269. {
  10270. // Does nothing
  10271. }
  10272. function _enddoc()
  10273. {
  10274. if ($this->progressBar) {
  10275. $this->UpdateProgressBar(2, '10', 'Writing Headers & Footers');
  10276. } // *PROGRESS-BAR*
  10277. $this->_puthtmlheaders();
  10278. if ($this->progressBar) {
  10279. $this->UpdateProgressBar(2, '20', 'Writing Pages');
  10280. } // *PROGRESS-BAR*
  10281. // Remove references to unused fonts (usually default font)
  10282. foreach ($this->fonts as $fk => $font) {
  10283. if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
  10284. if ($font['sip'] || $font['smp']) {
  10285. foreach ($font['subsetfontids'] AS $k => $fid) {
  10286. foreach ($this->pages AS $pn => $page) {
  10287. $this->pages[$pn] = preg_replace('/\s\/F' . $fid . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
  10288. }
  10289. }
  10290. } else {
  10291. foreach ($this->pages AS $pn => $page) {
  10292. $this->pages[$pn] = preg_replace('/\s\/F' . $font['i'] . ' \d[\d.]* Tf\s/is', ' ', $this->pages[$pn]);
  10293. }
  10294. }
  10295. }
  10296. }
  10297. if (count($this->layers)) {
  10298. foreach ($this->pages AS $pn => $page) {
  10299. preg_match_all('/\/OCZ-index \/ZI(\d+) BDC(.*?)(EMCZ)-index/is', $this->pages[$pn], $m1);
  10300. preg_match_all('/\/OCBZ-index \/ZI(\d+) BDC(.*?)(EMCBZ)-index/is', $this->pages[$pn], $m2);
  10301. preg_match_all('/\/OCGZ-index \/ZI(\d+) BDC(.*?)(EMCGZ)-index/is', $this->pages[$pn], $m3);
  10302. $m = array();
  10303. for ($i = 0; $i < 4; $i++) {
  10304. $m[$i] = array_merge($m1[$i], $m2[$i], $m3[$i]);
  10305. }
  10306. if (count($m[0])) {
  10307. $sortarr = array();
  10308. for ($i = 0; $i < count($m[0]); $i++) {
  10309. $key = $m[1][$i] * 2;
  10310. if ($m[3][$i] == 'EMCZ')
  10311. $key +=2; // background first then gradient then normal
  10312. elseif ($m[3][$i] == 'EMCGZ')
  10313. $key +=1;
  10314. $sortarr[$i] = $key;
  10315. }
  10316. asort($sortarr);
  10317. foreach ($sortarr AS $i => $k) {
  10318. $this->pages[$pn] = str_replace($m[0][$i], '', $this->pages[$pn]);
  10319. $this->pages[$pn] .= "\n" . $m[0][$i] . "\n";
  10320. }
  10321. $this->pages[$pn] = preg_replace('/\/OC[BG]{0,1}Z-index \/ZI(\d+) BDC/is', '/OC /ZI\\1 BDC ', $this->pages[$pn]);
  10322. $this->pages[$pn] = preg_replace('/EMC[BG]{0,1}Z-index/is', 'EMC', $this->pages[$pn]);
  10323. }
  10324. }
  10325. }
  10326. $this->_putpages();
  10327. if ($this->progressBar) {
  10328. $this->UpdateProgressBar(2, '30', 'Writing document resources');
  10329. } // *PROGRESS-BAR*
  10330. $this->_putresources();
  10331. //Info
  10332. $this->_newobj();
  10333. $this->InfoRoot = $this->n;
  10334. $this->_out('<<');
  10335. if ($this->progressBar) {
  10336. $this->UpdateProgressBar(2, '80', 'Writing document info');
  10337. } // *PROGRESS-BAR*
  10338. $this->_putinfo();
  10339. $this->_out('>>');
  10340. $this->_out('endobj');
  10341. // METADATA
  10342. if ($this->PDFA || $this->PDFX) {
  10343. $this->_putmetadata();
  10344. }
  10345. // OUTPUTINTENT
  10346. if ($this->PDFA || $this->PDFX || $this->ICCProfile) {
  10347. $this->_putoutputintent();
  10348. }
  10349. //Catalog
  10350. $this->_newobj();
  10351. $this->_out('<<');
  10352. if ($this->progressBar) {
  10353. $this->UpdateProgressBar(2, '90', 'Writing document catalog');
  10354. } // *PROGRESS-BAR*
  10355. $this->_putcatalog();
  10356. $this->_out('>>');
  10357. $this->_out('endobj');
  10358. //Cross-ref
  10359. $o = strlen($this->buffer);
  10360. $this->_out('xref');
  10361. $this->_out('0 ' . ($this->n + 1));
  10362. $this->_out('0000000000 65535 f ');
  10363. for ($i = 1; $i <= $this->n; $i++)
  10364. $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
  10365. //Trailer
  10366. $this->_out('trailer');
  10367. $this->_out('<<');
  10368. $this->_puttrailer();
  10369. $this->_out('>>');
  10370. $this->_out('startxref');
  10371. $this->_out($o);
  10372. $this->buffer .= '%%EOF';
  10373. $this->state = 3;
  10374. /* -- IMPORTS -- */
  10375. if ($this->enableImports && count($this->parsers) > 0) {
  10376. foreach ($this->parsers as $k => $_) {
  10377. $this->parsers[$k]->closeFile();
  10378. $this->parsers[$k] = null;
  10379. unset($this->parsers[$k]);
  10380. }
  10381. }
  10382. /* -- END IMPORTS -- */
  10383. }
  10384. function _beginpage($orientation, $mgl = '', $mgr = '', $mgt = '', $mgb = '', $mgh = '', $mgf = '', $ohname = '', $ehname = '', $ofname = '', $efname = '', $ohvalue = 0, $ehvalue = 0, $ofvalue = 0, $efvalue = 0, $pagesel = '', $newformat = '')
  10385. {
  10386. if (!($pagesel && $this->page == 1 && (sprintf("%0.4f", $this->y) == sprintf("%0.4f", $this->tMargin)))) {
  10387. $this->page++;
  10388. $this->pages[$this->page] = '';
  10389. }
  10390. $this->state = 2;
  10391. $resetHTMLHeadersrequired = false;
  10392. if ($newformat) {
  10393. $this->_setPageSize($newformat, $orientation);
  10394. }
  10395. /* -- CSS-PAGE -- */
  10396. // Paged media (page-box)
  10397. if ($pagesel || (isset($this->page_box['using']) && $this->page_box['using'])) {
  10398. if ($pagesel || $this->page == 1) {
  10399. $first = true;
  10400. } else {
  10401. $first = false;
  10402. }
  10403. if ($this->mirrorMargins && ($this->page % 2 == 0)) {
  10404. $oddEven = 'E';
  10405. } else {
  10406. $oddEven = 'O';
  10407. }
  10408. if ($pagesel) {
  10409. $psel = $pagesel;
  10410. } elseif ($this->page_box['current']) {
  10411. $psel = $this->page_box['current'];
  10412. } else {
  10413. $psel = '';
  10414. }
  10415. list($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS($psel, $first, $oddEven);
  10416. if ($this->mirrorMargins && ($this->page % 2 == 0)) {
  10417. if ($hname) {
  10418. $ehvalue = 1;
  10419. $ehname = $hname;
  10420. } else {
  10421. $ehvalue = -1;
  10422. }
  10423. if ($fname) {
  10424. $efvalue = 1;
  10425. $efname = $fname;
  10426. } else {
  10427. $efvalue = -1;
  10428. }
  10429. } else {
  10430. if ($hname) {
  10431. $ohvalue = 1;
  10432. $ohname = $hname;
  10433. } else {
  10434. $ohvalue = -1;
  10435. }
  10436. if ($fname) {
  10437. $ofvalue = 1;
  10438. $ofname = $fname;
  10439. } else {
  10440. $ofvalue = -1;
  10441. }
  10442. }
  10443. if ($resetpagenum || $pagenumstyle || $suppress) {
  10444. $this->PageNumSubstitutions[] = array('from' => ($this->page), 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress);
  10445. }
  10446. // PAGED MEDIA - CROP / CROSS MARKS from @PAGE
  10447. $this->show_marks = $marks;
  10448. // Background color
  10449. if (isset($bg['BACKGROUND-COLOR'])) {
  10450. $cor = $this->ConvertColor($bg['BACKGROUND-COLOR']);
  10451. if ($cor) {
  10452. $this->bodyBackgroundColor = $cor;
  10453. }
  10454. } else {
  10455. $this->bodyBackgroundColor = false;
  10456. }
  10457. /* -- BACKGROUNDS -- */
  10458. if (isset($bg['BACKGROUND-GRADIENT'])) {
  10459. $this->bodyBackgroundGradient = $bg['BACKGROUND-GRADIENT'];
  10460. } else {
  10461. $this->bodyBackgroundGradient = false;
  10462. }
  10463. // Tiling Patterns
  10464. if (isset($bg['BACKGROUND-IMAGE']) && $bg['BACKGROUND-IMAGE']) {
  10465. $ret = $this->SetBackground($bg, $this->pgwidth);
  10466. if ($ret) {
  10467. $this->bodyBackgroundImage = $ret;
  10468. }
  10469. } else {
  10470. $this->bodyBackgroundImage = false;
  10471. }
  10472. /* -- END BACKGROUNDS -- */
  10473. $this->page_box['current'] = $psel;
  10474. $this->page_box['using'] = true;
  10475. }
  10476. /* -- END CSS-PAGE -- */
  10477. //Page orientation
  10478. if (!$orientation)
  10479. $orientation = $this->DefOrientation;
  10480. else {
  10481. $orientation = strtoupper(substr($orientation, 0, 1));
  10482. if ($orientation != $this->DefOrientation)
  10483. $this->OrientationChanges[$this->page] = true;
  10484. }
  10485. if ($orientation != $this->CurOrientation || $newformat) {
  10486. //Change orientation
  10487. if ($orientation == 'P') {
  10488. $this->wPt = $this->fwPt;
  10489. $this->hPt = $this->fhPt;
  10490. $this->w = $this->fw;
  10491. $this->h = $this->fh;
  10492. if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {
  10493. $this->tMargin = $this->orig_tMargin;
  10494. $this->bMargin = $this->orig_bMargin;
  10495. $this->DeflMargin = $this->orig_lMargin;
  10496. $this->DefrMargin = $this->orig_rMargin;
  10497. $this->margin_header = $this->orig_hMargin;
  10498. $this->margin_footer = $this->orig_fMargin;
  10499. } else {
  10500. $resetHTMLHeadersrequired = true;
  10501. }
  10502. } else {
  10503. $this->wPt = $this->fhPt;
  10504. $this->hPt = $this->fwPt;
  10505. $this->w = $this->fh;
  10506. $this->h = $this->fw;
  10507. if (($this->forcePortraitHeaders || $this->forcePortraitMargins) && $this->DefOrientation == 'P') {
  10508. $this->tMargin = $this->orig_lMargin;
  10509. $this->bMargin = $this->orig_rMargin;
  10510. $this->DeflMargin = $this->orig_bMargin;
  10511. $this->DefrMargin = $this->orig_tMargin;
  10512. $this->margin_header = $this->orig_hMargin;
  10513. $this->margin_footer = $this->orig_fMargin;
  10514. } else {
  10515. $resetHTMLHeadersrequired = true;
  10516. }
  10517. }
  10518. $this->CurOrientation = $orientation;
  10519. $this->ResetMargins();
  10520. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  10521. $this->PageBreakTrigger = $this->h - $this->bMargin;
  10522. }
  10523. $this->pageDim[$this->page]['w'] = $this->w;
  10524. $this->pageDim[$this->page]['h'] = $this->h;
  10525. $this->pageDim[$this->page]['outer_width_LR'] = isset($this->page_box['outer_width_LR']) ? $this->page_box['outer_width_LR'] : 0;
  10526. $this->pageDim[$this->page]['outer_width_TB'] = isset($this->page_box['outer_width_TB']) ? $this->page_box['outer_width_TB'] : 0;
  10527. if (!isset($this->page_box['outer_width_LR']) && !isset($this->page_box['outer_width_TB'])) {
  10528. $this->pageDim[$this->page]['bleedMargin'] = 0;
  10529. } elseif ($this->bleedMargin <= $this->page_box['outer_width_LR'] && $this->bleedMargin <= $this->page_box['outer_width_TB']) {
  10530. $this->pageDim[$this->page]['bleedMargin'] = $this->bleedMargin;
  10531. } else {
  10532. $this->pageDim[$this->page]['bleedMargin'] = min($this->page_box['outer_width_LR'], $this->page_box['outer_width_TB']) - 0.01;
  10533. }
  10534. // If Page Margins are re-defined
  10535. // strlen()>0 is used to pick up (integer) 0, (string) '0', or set value
  10536. if ((strlen($mgl) > 0 && $this->DeflMargin != $mgl) || (strlen($mgr) > 0 && $this->DefrMargin != $mgr) || (strlen($mgt) > 0 && $this->tMargin != $mgt) || (strlen($mgb) > 0 && $this->bMargin != $mgb) || (strlen($mgh) > 0 && $this->margin_header != $mgh) || (strlen($mgf) > 0 && $this->margin_footer != $mgf)) {
  10537. if (strlen($mgl) > 0)
  10538. $this->DeflMargin = $mgl;
  10539. if (strlen($mgr) > 0)
  10540. $this->DefrMargin = $mgr;
  10541. if (strlen($mgt) > 0)
  10542. $this->tMargin = $mgt;
  10543. if (strlen($mgb) > 0)
  10544. $this->bMargin = $mgb;
  10545. if (strlen($mgh) > 0)
  10546. $this->margin_header = $mgh;
  10547. if (strlen($mgf) > 0)
  10548. $this->margin_footer = $mgf;
  10549. $this->ResetMargins();
  10550. $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
  10551. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  10552. $resetHTMLHeadersrequired = true;
  10553. }
  10554. $this->ResetMargins();
  10555. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  10556. $this->SetAutoPageBreak($this->autoPageBreak, $this->bMargin);
  10557. // Reset column top margin
  10558. $this->y0 = $this->tMargin;
  10559. $this->x = $this->lMargin;
  10560. $this->y = $this->tMargin;
  10561. $this->FontFamily = '';
  10562. // HEADERS AND FOOTERS // mPDF 6
  10563. if ($ohvalue < 0 || strtoupper($ohvalue) == 'OFF') {
  10564. $this->HTMLHeader = '';
  10565. $resetHTMLHeadersrequired = true;
  10566. } elseif ($ohname && $ohvalue > 0) {
  10567. if (preg_match('/^html_(.*)$/i', $ohname, $n)) {
  10568. $name = $n[1];
  10569. } else {
  10570. $name = $ohname;
  10571. }
  10572. if (isset($this->pageHTMLheaders[$name])) {
  10573. $this->HTMLHeader = $this->pageHTMLheaders[$name];
  10574. } else {
  10575. $this->HTMLHeader = '';
  10576. }
  10577. $resetHTMLHeadersrequired = true;
  10578. }
  10579. if ($ehvalue < 0 || strtoupper($ehvalue) == 'OFF') {
  10580. $this->HTMLHeaderE = '';
  10581. $resetHTMLHeadersrequired = true;
  10582. } elseif ($ehname && $ehvalue > 0) {
  10583. if (preg_match('/^html_(.*)$/i', $ehname, $n)) {
  10584. $name = $n[1];
  10585. } else {
  10586. $name = $ehname;
  10587. }
  10588. if (isset($this->pageHTMLheaders[$name])) {
  10589. $this->HTMLHeaderE = $this->pageHTMLheaders[$name];
  10590. } else {
  10591. $this->HTMLHeaderE = '';
  10592. }
  10593. $resetHTMLHeadersrequired = true;
  10594. }
  10595. if ($ofvalue < 0 || strtoupper($ofvalue) == 'OFF') {
  10596. $this->HTMLFooter = '';
  10597. $resetHTMLHeadersrequired = true;
  10598. } elseif ($ofname && $ofvalue > 0) {
  10599. if (preg_match('/^html_(.*)$/i', $ofname, $n)) {
  10600. $name = $n[1];
  10601. } else {
  10602. $name = $ofname;
  10603. }
  10604. if (isset($this->pageHTMLfooters[$name])) {
  10605. $this->HTMLFooter = $this->pageHTMLfooters[$name];
  10606. } else {
  10607. $this->HTMLFooter = '';
  10608. }
  10609. $resetHTMLHeadersrequired = true;
  10610. }
  10611. if ($efvalue < 0 || strtoupper($efvalue) == 'OFF') {
  10612. $this->HTMLFooterE = '';
  10613. $resetHTMLHeadersrequired = true;
  10614. } elseif ($efname && $efvalue > 0) {
  10615. if (preg_match('/^html_(.*)$/i', $efname, $n)) {
  10616. $name = $n[1];
  10617. } else {
  10618. $name = $efname;
  10619. }
  10620. if (isset($this->pageHTMLfooters[$name])) {
  10621. $this->HTMLFooterE = $this->pageHTMLfooters[$name];
  10622. } else {
  10623. $this->HTMLFooterE = '';
  10624. }
  10625. $resetHTMLHeadersrequired = true;
  10626. }
  10627. if ($resetHTMLHeadersrequired) {
  10628. $this->SetHTMLHeader($this->HTMLHeader);
  10629. $this->SetHTMLHeader($this->HTMLHeaderE, 'E');
  10630. $this->SetHTMLFooter($this->HTMLFooter);
  10631. $this->SetHTMLFooter($this->HTMLFooterE, 'E');
  10632. }
  10633. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  10634. $this->_setAutoHeaderHeight($this->HTMLHeaderE);
  10635. $this->_setAutoFooterHeight($this->HTMLFooterE);
  10636. } else { // ODD or DEFAULT
  10637. $this->_setAutoHeaderHeight($this->HTMLHeader);
  10638. $this->_setAutoFooterHeight($this->HTMLFooter);
  10639. }
  10640. // Reset column top margin
  10641. $this->y0 = $this->tMargin;
  10642. $this->x = $this->lMargin;
  10643. $this->y = $this->tMargin;
  10644. }
  10645. // mPDF 6
  10646. function _setAutoHeaderHeight(&$htmlh)
  10647. {
  10648. if ($this->setAutoTopMargin == 'pad') {
  10649. if (isset($htmlh['h']) && $htmlh['h']) {
  10650. $h = $htmlh['h'];
  10651. } // 5.7.3
  10652. else {
  10653. $h = 0;
  10654. }
  10655. $this->tMargin = $this->margin_header + $h + $this->orig_tMargin;
  10656. } elseif ($this->setAutoTopMargin == 'stretch') {
  10657. if (isset($htmlh['h']) && $htmlh['h']) {
  10658. $h = $htmlh['h'];
  10659. } // 5.7.3
  10660. else {
  10661. $h = 0;
  10662. }
  10663. $this->tMargin = max($this->orig_tMargin, $this->margin_header + $h + $this->autoMarginPadding);
  10664. }
  10665. }
  10666. // mPDF 6
  10667. function _setAutoFooterHeight(&$htmlf)
  10668. {
  10669. if ($this->setAutoBottomMargin == 'pad') {
  10670. if (isset($htmlf['h']) && $htmlf['h']) {
  10671. $h = $htmlf['h'];
  10672. } // 5.7.3
  10673. else {
  10674. $h = 0;
  10675. }
  10676. $this->bMargin = $this->margin_footer + $h + $this->orig_bMargin;
  10677. $this->PageBreakTrigger = $this->h - $this->bMargin;
  10678. } elseif ($this->setAutoBottomMargin == 'stretch') {
  10679. if (isset($htmlf['h']) && $htmlf['h']) {
  10680. $h = $htmlf['h'];
  10681. } // 5.7.3
  10682. else {
  10683. $h = 0;
  10684. }
  10685. $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $h + $this->autoMarginPadding);
  10686. $this->PageBreakTrigger = $this->h - $this->bMargin;
  10687. }
  10688. }
  10689. function _endpage()
  10690. {
  10691. /* -- CSS-IMAGE-FLOAT -- */
  10692. $this->printfloatbuffer();
  10693. /* -- END CSS-IMAGE-FLOAT -- */
  10694. if ($this->visibility != 'visible')
  10695. $this->SetVisibility('visible');
  10696. $this->EndLayer();
  10697. //End of page contents
  10698. $this->state = 1;
  10699. }
  10700. function _newobj($obj_id = false, $onlynewobj = false)
  10701. {
  10702. if (!$obj_id) {
  10703. $obj_id = ++$this->n;
  10704. }
  10705. //Begin a new object
  10706. if (!$onlynewobj) {
  10707. $this->offsets[$obj_id] = strlen($this->buffer);
  10708. $this->_out($obj_id . ' 0 obj');
  10709. $this->_current_obj_id = $obj_id; // for later use with encryption
  10710. }
  10711. }
  10712. function _dounderline($x, $y, $txt, $OTLdata = false, $textvar = 0)
  10713. {
  10714. // Now print line exactly where $y secifies - called from Text() and Cell() - adjust position there
  10715. // WORD SPACING
  10716. $w = ($this->GetStringWidth($txt, false, $OTLdata, $textvar) * _MPDFK) + ($this->charspacing * mb_strlen($txt, $this->mb_enc)) + ( $this->ws * mb_substr_count($txt, ' ', $this->mb_enc));
  10717. //Draw a line
  10718. return sprintf('%.3F %.3F m %.3F %.3F l S', $x * _MPDFK, ($this->h - $y) * _MPDFK, ($x * _MPDFK) + $w, ($this->h - $y) * _MPDFK);
  10719. }
  10720. function _imageError($file, $firsttime, $msg)
  10721. {
  10722. // Save re-trying image URL's which have already failed
  10723. $this->failedimages[$file] = true;
  10724. if ($firsttime && ($this->showImageErrors || $this->debug)) {
  10725. throw new MpdfException("IMAGE Error (" . $file . "): " . $msg);
  10726. }
  10727. return false;
  10728. }
  10729. function _getImage(&$file, $firsttime = true, $allowvector = true, $orig_srcpath = false, $interpolation = false)
  10730. { // mPDF 6
  10731. // firsttime i.e. whether to add to this->images - use false when calling iteratively
  10732. // Image Data passed directly as var:varname
  10733. if (preg_match('/var:\s*(.*)/', $file, $v)) {
  10734. $data = $this->{$v[1]};
  10735. $file = md5($data);
  10736. }
  10737. if (preg_match('/data:image\/(gif|jpeg|png);base64,(.*)/', $file, $v)) {
  10738. $type = $v[1];
  10739. $data = base64_decode($v[2]);
  10740. $file = md5($data);
  10741. }
  10742. // mPDF 5.7.4 URLs
  10743. if ($firsttime && $file && substr($file, 0, 5) != 'data:') {
  10744. $file = str_replace(" ", "%20", $file);
  10745. }
  10746. if ($firsttime && $orig_srcpath) {
  10747. // If orig_srcpath is a relative file path (and not a URL), then it needs to be URL decoded
  10748. if (substr($orig_srcpath, 0, 5) != 'data:') {
  10749. $orig_srcpath = str_replace(" ", "%20", $orig_srcpath);
  10750. }
  10751. if (!preg_match('/^(http|ftp)/', $orig_srcpath)) {
  10752. $orig_srcpath = urldecode_parts($orig_srcpath);
  10753. }
  10754. }
  10755. $ppUx = 0;
  10756. if ($orig_srcpath && isset($this->images[$orig_srcpath])) {
  10757. $file = $orig_srcpath;
  10758. return $this->images[$orig_srcpath];
  10759. }
  10760. if (isset($this->images[$file])) {
  10761. return $this->images[$file];
  10762. } elseif ($orig_srcpath && isset($this->formobjects[$orig_srcpath])) {
  10763. $file = $orig_srcpath;
  10764. return $this->formobjects[$file];
  10765. } elseif (isset($this->formobjects[$file])) {
  10766. return $this->formobjects[$file];
  10767. }
  10768. // Save re-trying image URL's which have already failed
  10769. elseif ($firsttime && isset($this->failedimages[$file])) {
  10770. return $this->_imageError($file, $firsttime, '');
  10771. }
  10772. if (empty($data)) {
  10773. $type = '';
  10774. $data = '';
  10775. if ($orig_srcpath && $this->basepathIsLocal && $check = @fopen($orig_srcpath, "rb")) {
  10776. fclose($check);
  10777. $file = $orig_srcpath;
  10778. $data = file_get_contents($file);
  10779. $type = $this->_imageTypeFromString($data);
  10780. }
  10781. if (!$data && $check = @fopen($file, "rb")) {
  10782. fclose($check);
  10783. $data = file_get_contents($file);
  10784. $type = $this->_imageTypeFromString($data);
  10785. }
  10786. if ((!$data || !$type) && !ini_get('allow_url_fopen')) { // only worth trying if remote file and !ini_get('allow_url_fopen')
  10787. $this->file_get_contents_by_socket($file, $data); // needs full url?? even on local (never needed for local)
  10788. if ($data) {
  10789. $type = $this->_imageTypeFromString($data);
  10790. }
  10791. }
  10792. if ((!$data || !$type) && function_exists("curl_init")) { // mPDF 5.7.4
  10793. $this->file_get_contents_by_curl($file, $data); // needs full url?? even on local (never needed for local)
  10794. if ($data) {
  10795. $type = $this->_imageTypeFromString($data);
  10796. }
  10797. }
  10798. }
  10799. if (!$data) {
  10800. return $this->_imageError($file, $firsttime, 'Could not find image file');
  10801. }
  10802. if (empty($type)) {
  10803. $type = $this->_imageTypeFromString($data);
  10804. }
  10805. if (($type == 'wmf' || $type == 'svg') && !$allowvector) {
  10806. return $this->_imageError($file, $firsttime, 'WMF or SVG image file not supported in this context');
  10807. }
  10808. // SVG
  10809. if ($type == 'svg') {
  10810. if (!class_exists('SVG', false)) {
  10811. include(_MPDF_PATH . 'classes/svg.php');
  10812. }
  10813. $svg = new SVG($this);
  10814. $family = $this->FontFamily;
  10815. $style = $this->FontStyle;
  10816. $size = $this->FontSizePt;
  10817. $info = $svg->ImageSVG($data);
  10818. //Restore font
  10819. if ($family)
  10820. $this->SetFont($family, $style, $size, false);
  10821. if (!$info) {
  10822. return $this->_imageError($file, $firsttime, 'Error parsing SVG file');
  10823. }
  10824. $info['type'] = 'svg';
  10825. $info['i'] = count($this->formobjects) + 1;
  10826. $this->formobjects[$file] = $info;
  10827. return $info;
  10828. }
  10829. // JPEG
  10830. if ($type == 'jpeg' || $type == 'jpg') {
  10831. $hdr = $this->_jpgHeaderFromString($data);
  10832. if (!$hdr) {
  10833. return $this->_imageError($file, $firsttime, 'Error parsing JPG header');
  10834. }
  10835. $a = $this->_jpgDataFromHeader($hdr);
  10836. $channels = intval($a[4]);
  10837. $j = strpos($data, 'JFIF');
  10838. if ($j) {
  10839. //Read resolution
  10840. $unitSp = ord(substr($data, ($j + 7), 1));
  10841. if ($unitSp > 0) {
  10842. $ppUx = $this->_twobytes2int(substr($data, ($j + 8), 2)); // horizontal pixels per meter, usually set to zero
  10843. if ($unitSp == 2) { // = dots per cm (if == 1 set as dpi)
  10844. $ppUx = round($ppUx / 10 * 25.4);
  10845. }
  10846. }
  10847. }
  10848. if ($a[2] == 'DeviceCMYK' && (($this->PDFA && $this->restrictColorSpace != 3) || $this->restrictColorSpace == 2)) {
  10849. // convert to RGB image
  10850. if (!function_exists("gd_info")) {
  10851. throw new MpdfException("JPG image may not use CMYK color space (" . $file . ").");
  10852. }
  10853. if ($this->PDFA && !$this->PDFAauto) {
  10854. $this->PDFAXwarnings[] = "JPG image may not use CMYK color space - " . $file . " - (Image converted to RGB. NB This will alter the colour profile of the image.)";
  10855. }
  10856. $im = @imagecreatefromstring($data);
  10857. if ($im) {
  10858. $tempfile = _MPDF_TEMP_PATH . '_tempImgPNG' . md5($file) . RAND(1, 10000) . '.png';
  10859. imageinterlace($im, false);
  10860. $check = @imagepng($im, $tempfile);
  10861. if (!$check) {
  10862. return $this->_imageError($file, $firsttime, 'Error creating temporary file (' . $tempfile . ') whilst using GD library to parse JPG(CMYK) image');
  10863. }
  10864. $info = $this->_getImage($tempfile, false);
  10865. if (!$info) {
  10866. return $this->_imageError($file, $firsttime, 'Error parsing temporary file (' . $tempfile . ') created with GD library to parse JPG(CMYK) image');
  10867. }
  10868. imagedestroy($im);
  10869. unlink($tempfile);
  10870. $info['type'] = 'jpg';
  10871. if ($firsttime) {
  10872. $info['i'] = count($this->images) + 1;
  10873. $info['interpolation'] = $interpolation; // mPDF 6
  10874. $this->images[$file] = $info;
  10875. }
  10876. return $info;
  10877. } else {
  10878. return $this->_imageError($file, $firsttime, 'Error creating GD image file from JPG(CMYK) image');
  10879. }
  10880. } elseif ($a[2] == 'DeviceRGB' && ($this->PDFX || $this->restrictColorSpace == 3)) {
  10881. // Convert to CMYK image stream - nominally returned as type='png'
  10882. $info = $this->_convImage($data, $a[2], 'DeviceCMYK', $a[0], $a[1], $ppUx, false);
  10883. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  10884. $this->PDFAXwarnings[] = "JPG image may not use RGB color space - " . $file . " - (Image converted to CMYK. NB This will alter the colour profile of the image.)";
  10885. }
  10886. } elseif (($a[2] == 'DeviceRGB' || $a[2] == 'DeviceCMYK') && $this->restrictColorSpace == 1) {
  10887. // Convert to Grayscale image stream - nominally returned as type='png'
  10888. $info = $this->_convImage($data, $a[2], 'DeviceGray', $a[0], $a[1], $ppUx, false);
  10889. } else {
  10890. // mPDF 6 Detect Adobe APP14 Tag
  10891. //$pos = strpos($data, "\xFF\xEE\x00\x0EAdobe\0");
  10892. //if ($pos !== false) {
  10893. //}
  10894. // mPDF 6 ICC profile
  10895. $offset = 0;
  10896. $icc = array();
  10897. while (($pos = strpos($data, "ICC_PROFILE\0", $offset)) !== false) {
  10898. // get ICC sequence length
  10899. $length = $this->_twobytes2int(substr($data, ($pos - 2), 2)) - 16;
  10900. $sn = max(1, ord($data[($pos + 12)]));
  10901. $nom = max(1, ord($data[($pos + 13)]));
  10902. $icc[($sn - 1)] = substr($data, ($pos + 14), $length);
  10903. $offset = ($pos + 14 + $length);
  10904. }
  10905. // order and compact ICC segments
  10906. if (count($icc) > 0) {
  10907. ksort($icc);
  10908. $icc = implode('', $icc);
  10909. if (substr($icc, 36, 4) != 'acsp') {
  10910. // invalid ICC profile
  10911. $icc = false;
  10912. }
  10913. $input = substr($icc, 16, 4);
  10914. $output = substr($icc, 20, 4);
  10915. // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
  10916. if ($input != 'RGB ' || $output != 'XYZ ') {
  10917. $icc = false;
  10918. }
  10919. } else {
  10920. $icc = false;
  10921. }
  10922. $info = array('w' => $a[0], 'h' => $a[1], 'cs' => $a[2], 'bpc' => $a[3], 'f' => 'DCTDecode', 'data' => $data, 'type' => 'jpg', 'ch' => $channels, 'icc' => $icc);
  10923. if ($ppUx) {
  10924. $info['set-dpi'] = $ppUx;
  10925. }
  10926. }
  10927. if (!$info) {
  10928. return $this->_imageError($file, $firsttime, 'Error parsing or converting JPG image');
  10929. }
  10930. if ($firsttime) {
  10931. $info['i'] = count($this->images) + 1;
  10932. $info['interpolation'] = $interpolation; // mPDF 6
  10933. $this->images[$file] = $info;
  10934. }
  10935. return $info;
  10936. }
  10937. // PNG
  10938. elseif ($type == 'png') {
  10939. //Check signature
  10940. if (substr($data, 0, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
  10941. return $this->_imageError($file, $firsttime, 'Error parsing PNG identifier');
  10942. }
  10943. //Read header chunk
  10944. if (substr($data, 12, 4) != 'IHDR') {
  10945. return $this->_imageError($file, $firsttime, 'Incorrect PNG file (no IHDR block found)');
  10946. }
  10947. $w = $this->_fourbytes2int(substr($data, 16, 4));
  10948. $h = $this->_fourbytes2int(substr($data, 20, 4));
  10949. $bpc = ord(substr($data, 24, 1));
  10950. $errpng = false;
  10951. $pngalpha = false;
  10952. $channels = 0;
  10953. // if($bpc>8) { $errpng = 'not 8-bit depth'; } // mPDF 6 Allow through to be handled as native PNG
  10954. $ct = ord(substr($data, 25, 1));
  10955. if ($ct == 0) {
  10956. $colspace = 'DeviceGray';
  10957. $channels = 1;
  10958. } elseif ($ct == 2) {
  10959. $colspace = 'DeviceRGB';
  10960. $channels = 3;
  10961. } elseif ($ct == 3) {
  10962. $colspace = 'Indexed';
  10963. $channels = 1;
  10964. } elseif ($ct == 4) {
  10965. $colspace = 'DeviceGray';
  10966. $channels = 1;
  10967. $errpng = 'alpha channel';
  10968. $pngalpha = true;
  10969. } else {
  10970. $colspace = 'DeviceRGB';
  10971. $channels = 3;
  10972. $errpng = 'alpha channel';
  10973. $pngalpha = true;
  10974. }
  10975. if ($ct < 4 && strpos($data, 'tRNS') !== false) {
  10976. $errpng = 'transparency';
  10977. $pngalpha = true;
  10978. } // mPDF 6
  10979. if ($ct == 3 && strpos($data, 'iCCP') !== false) {
  10980. $errpng = 'indexed plus ICC';
  10981. } // mPDF 6
  10982. // $pngalpha is used as a FLAG of any kind of transparency which COULD be tranferred to an alpha channel
  10983. // incl. single-color tarnsparency, depending which type of handling occurs later
  10984. if (ord(substr($data, 26, 1)) != 0) {
  10985. $errpng = 'compression method';
  10986. } // only 0 should be specified
  10987. if (ord(substr($data, 27, 1)) != 0) {
  10988. $errpng = 'filter method';
  10989. } // only 0 should be specified
  10990. if (ord(substr($data, 28, 1)) != 0) {
  10991. $errpng = 'interlaced file';
  10992. }
  10993. $j = strpos($data, 'pHYs');
  10994. if ($j) {
  10995. //Read resolution
  10996. $unitSp = ord(substr($data, ($j + 12), 1));
  10997. if ($unitSp == 1) {
  10998. $ppUx = $this->_fourbytes2int(substr($data, ($j + 4), 4)); // horizontal pixels per meter, usually set to zero
  10999. $ppUx = round($ppUx / 1000 * 25.4);
  11000. }
  11001. }
  11002. // mPDF 6 Gamma correction
  11003. $gamma_correction = 0;
  11004. $gAMA = 0;
  11005. $j = strpos($data, 'gAMA');
  11006. if ($j && strpos($data, 'sRGB') === false) { // sRGB colorspace - overrides gAMA
  11007. $gAMA = $this->_fourbytes2int(substr($data, ($j + 4), 4)); // Gamma value times 100000
  11008. $gAMA /= 100000;
  11009. // http://www.libpng.org/pub/png/spec/1.2/PNG-Encoders.html
  11010. // "If the source file's gamma value is greater than 1.0, it is probably a display system exponent,..."
  11011. // ("..and you should use its reciprocal for the PNG gamma.")
  11012. //if ($gAMA > 1) { $gAMA = 1/$gAMA; }
  11013. // (Some) Applications seem to ignore it... appearing how it was probably intended
  11014. // Test Case - image(s) on http://www.w3.org/TR/CSS21/intro.html - PNG has gAMA set as 1.45454
  11015. // Probably unintentional as mentioned above and should be 0.45454 which is 1 / 2.2
  11016. // Tested on Windows PC
  11017. // Firefox and Opera display gray as 234 (correct, but looks wrong)
  11018. // IE9 and Safari display gray as 193 (incorrect but looks right)
  11019. // See test different gamma chunks at http://www.libpng.org/pub/png/pngsuite-all-good.html
  11020. }
  11021. if ($gAMA) {
  11022. $gamma_correction = 1 / $gAMA;
  11023. }
  11024. // Don't need to apply gamma correction if == default i.e. 2.2
  11025. if ($gamma_correction > 2.15 && $gamma_correction < 2.25) {
  11026. $gamma_correction = 0;
  11027. }
  11028. // NOT supported at present
  11029. //$j = strpos($data,'sRGB'); // sRGB colorspace - overrides gAMA
  11030. //$j = strpos($data,'cHRM'); // Chromaticity and Whitepoint
  11031. // $firsttime added mPDF 6 so when PNG Grayscale with alpha using resrtictcolorspace to CMYK
  11032. // the alpha channel is sent through as secondtime as Indexed and should not be converted to CMYK
  11033. if ($firsttime && ($colspace == 'DeviceRGB' || $colspace == 'Indexed') && ($this->PDFX || $this->restrictColorSpace == 3)) {
  11034. // Convert to CMYK image stream - nominally returned as type='png'
  11035. $info = $this->_convImage($data, $colspace, 'DeviceCMYK', $w, $h, $ppUx, $pngalpha, $gamma_correction, $ct); // mPDF 5.7.2 Gamma correction
  11036. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  11037. $this->PDFAXwarnings[] = "PNG image may not use RGB color space - " . $file . " - (Image converted to CMYK. NB This will alter the colour profile of the image.)";
  11038. }
  11039. }
  11040. // $firsttime added mPDF 6 so when PNG Grayscale with alpha using resrtictcolorspace to CMYK
  11041. // the alpha channel is sent through as secondtime as Indexed and should not be converted to CMYK
  11042. elseif ($firsttime && ($colspace == 'DeviceRGB' || $colspace == 'Indexed') && $this->restrictColorSpace == 1) {
  11043. // Convert to Grayscale image stream - nominally returned as type='png'
  11044. $info = $this->_convImage($data, $colspace, 'DeviceGray', $w, $h, $ppUx, $pngalpha, $gamma_correction, $ct); // mPDF 5.7.2 Gamma correction
  11045. } elseif (($this->PDFA || $this->PDFX) && $pngalpha) {
  11046. // Remove alpha channel
  11047. if ($this->restrictColorSpace == 1) { // Grayscale
  11048. $info = $this->_convImage($data, $colspace, 'DeviceGray', $w, $h, $ppUx, $pngalpha, $gamma_correction, $ct); // mPDF 5.7.2 Gamma correction
  11049. } elseif ($this->restrictColorSpace == 3) { // CMYK
  11050. $info = $this->_convImage($data, $colspace, 'DeviceCMYK', $w, $h, $ppUx, $pngalpha, $gamma_correction, $ct); // mPDF 5.7.2 Gamma correction
  11051. } elseif ($this->PDFA) { // RGB
  11052. $info = $this->_convImage($data, $colspace, 'DeviceRGB', $w, $h, $ppUx, $pngalpha, $gamma_correction, $ct); // mPDF 5.7.2 Gamma correction
  11053. }
  11054. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  11055. $this->PDFAXwarnings[] = "Transparency (alpha channel) not permitted in PDFA or PDFX files - " . $file . " - (Image converted to one without transparency.)";
  11056. }
  11057. } elseif ($firsttime && ($errpng || $pngalpha || $gamma_correction)) { // mPDF 5.7.2 Gamma correction
  11058. if (function_exists('gd_info')) {
  11059. $gd = gd_info();
  11060. } else {
  11061. $gd = array();
  11062. }
  11063. if (!isset($gd['PNG Support'])) {
  11064. return $this->_imageError($file, $firsttime, 'GD library required for PNG image (' . $errpng . ')');
  11065. }
  11066. $im = imagecreatefromstring($data);
  11067. if (!$im) {
  11068. return $this->_imageError($file, $firsttime, 'Error creating GD image from PNG file (' . $errpng . ')');
  11069. }
  11070. $w = imagesx($im);
  11071. $h = imagesy($im);
  11072. if ($im) {
  11073. $tempfile = _MPDF_TEMP_PATH . '_tempImgPNG' . md5($file) . RAND(1, 10000) . '.png';
  11074. // Alpha channel set (including using tRNS for Paletted images)
  11075. if ($pngalpha) {
  11076. if ($this->PDFA) {
  11077. throw new MpdfException("PDFA1-b does not permit images with alpha channel transparency (" . $file . ").");
  11078. }
  11079. $imgalpha = imagecreate($w, $h);
  11080. // generate gray scale pallete
  11081. for ($c = 0; $c < 256; ++$c) {
  11082. imagecolorallocate($imgalpha, $c, $c, $c);
  11083. }
  11084. // mPDF 6
  11085. if ($colspace == 'Indexed') { // generate Alpha channel values from tRNS
  11086. //Read transparency info
  11087. $transparency = '';
  11088. $p = strpos($data, 'tRNS');
  11089. if ($p) {
  11090. $n = $this->_fourbytes2int(substr($data, ($p - 4), 4));
  11091. $transparency = substr($data, ($p + 4), $n);
  11092. // ord($transparency{$index}) = the alpha value for that index
  11093. // generate alpha channel
  11094. for ($ypx = 0; $ypx < $h; ++$ypx) {
  11095. for ($xpx = 0; $xpx < $w; ++$xpx) {
  11096. $colorindex = imagecolorat($im, $xpx, $ypx);
  11097. if ($colorindex >= $n) {
  11098. $alpha = 255;
  11099. } else {
  11100. $alpha = ord($transparency{$colorindex});
  11101. } // 0-255
  11102. if ($alpha > 0) {
  11103. imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
  11104. }
  11105. }
  11106. }
  11107. }
  11108. } elseif ($ct === 0 || $ct == 2) { // generate Alpha channel values from tRNS
  11109. // Get transparency as array of RGB
  11110. $p = strpos($data, 'tRNS');
  11111. if ($p) {
  11112. $trns = '';
  11113. $n = $this->_fourbytes2int(substr($data, ($p - 4), 4));
  11114. $t = substr($data, ($p + 4), $n);
  11115. if ($colspace == 'DeviceGray') { // ct===0
  11116. $trns = array($this->_trnsvalue(substr($t, 0, 2), $bpc));
  11117. } else /* $colspace=='DeviceRGB' */ { // ct==2
  11118. $trns = array();
  11119. $trns[0] = $this->_trnsvalue(substr($t, 0, 2), $bpc);
  11120. $trns[1] = $this->_trnsvalue(substr($t, 2, 2), $bpc);
  11121. $trns[2] = $this->_trnsvalue(substr($t, 4, 2), $bpc);
  11122. }
  11123. // generate alpha channel
  11124. for ($ypx = 0; $ypx < $h; ++$ypx) {
  11125. for ($xpx = 0; $xpx < $w; ++$xpx) {
  11126. $rgb = imagecolorat($im, $xpx, $ypx);
  11127. $r = ($rgb >> 16) & 0xFF;
  11128. $g = ($rgb >> 8) & 0xFF;
  11129. $b = $rgb & 0xFF;
  11130. if ($colspace == 'DeviceGray' && $b == $trns[0]) {
  11131. $alpha = 0;
  11132. } elseif ($r == $trns[0] && $g == $trns[1] && $b == $trns[2]) {
  11133. $alpha = 0;
  11134. } // ct==2
  11135. else {
  11136. $alpha = 255;
  11137. }
  11138. if ($alpha > 0) {
  11139. imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
  11140. }
  11141. }
  11142. }
  11143. }
  11144. } else {
  11145. // extract alpha channel
  11146. for ($ypx = 0; $ypx < $h; ++$ypx) {
  11147. for ($xpx = 0; $xpx < $w; ++$xpx) {
  11148. $alpha = (imagecolorat($im, $xpx, $ypx) & 0x7F000000) >> 24;
  11149. if ($alpha < 127) {
  11150. imagesetpixel($imgalpha, $xpx, $ypx, (255 - ($alpha * 2)));
  11151. }
  11152. }
  11153. }
  11154. }
  11155. // NB This must happen after the Alpha channel is extracted
  11156. // imagegammacorrect() removes the alpha channel data in $im - (I think this is a bug in PHP)
  11157. if ($gamma_correction) {
  11158. imagegammacorrect($im, $gamma_correction, 2.2);
  11159. } // mPDF 6 Gamma correction
  11160. // create temp alpha file
  11161. $tempfile_alpha = _MPDF_TEMP_PATH . '_tempMskPNG' . md5($file) . RAND(1, 10000) . '.png';
  11162. if (!is_writable(_MPDF_TEMP_PATH)) { // mPDF 5.7.2
  11163. ob_start();
  11164. $check = @imagepng($imgalpha);
  11165. if (!$check) {
  11166. return $this->_imageError($file, $firsttime, 'Error creating temporary image object whilst using GD library to parse PNG image');
  11167. }
  11168. imagedestroy($imgalpha);
  11169. $this->_tempimg = ob_get_contents();
  11170. $this->_tempimglnk = 'var:_tempimg';
  11171. ob_end_clean();
  11172. // extract image without alpha channel
  11173. $imgplain = imagecreatetruecolor($w, $h);
  11174. imagealphablending($imgplain, false); // mPDF 5.7.2
  11175. imagecopy($imgplain, $im, 0, 0, 0, 0, $w, $h);
  11176. // create temp image file
  11177. $minfo = $this->_getImage($this->_tempimglnk, false);
  11178. if (!$minfo) {
  11179. return $this->_imageError($file, $firsttime, 'Error parsing temporary file image object created with GD library to parse PNG image');
  11180. }
  11181. ob_start();
  11182. $check = @imagepng($imgplain);
  11183. if (!$check) {
  11184. return $this->_imageError($file, $firsttime, 'Error creating temporary image object whilst using GD library to parse PNG image');
  11185. }
  11186. $this->_tempimg = ob_get_contents();
  11187. $this->_tempimglnk = 'var:_tempimg';
  11188. ob_end_clean();
  11189. $info = $this->_getImage($this->_tempimglnk, false);
  11190. if (!$info) {
  11191. return $this->_imageError($file, $firsttime, 'Error parsing temporary file image object created with GD library to parse PNG image');
  11192. }
  11193. imagedestroy($imgplain);
  11194. $imgmask = count($this->images) + 1;
  11195. $minfo['cs'] = 'DeviceGray';
  11196. $minfo['i'] = $imgmask;
  11197. $this->images[$tempfile_alpha] = $minfo;
  11198. } else {
  11199. $check = @imagepng($imgalpha, $tempfile_alpha);
  11200. if (!$check) {
  11201. return $this->_imageError($file, $firsttime, 'Failed to create temporary image file (' . $tempfile_alpha . ') parsing PNG image with alpha channel (' . $errpng . ')');
  11202. }
  11203. imagedestroy($imgalpha);
  11204. // extract image without alpha channel
  11205. $imgplain = imagecreatetruecolor($w, $h);
  11206. imagealphablending($imgplain, false); // mPDF 5.7.2
  11207. imagecopy($imgplain, $im, 0, 0, 0, 0, $w, $h);
  11208. // create temp image file
  11209. $check = @imagepng($imgplain, $tempfile);
  11210. if (!$check) {
  11211. return $this->_imageError($file, $firsttime, 'Failed to create temporary image file (' . $tempfile . ') parsing PNG image with alpha channel (' . $errpng . ')');
  11212. }
  11213. imagedestroy($imgplain);
  11214. // embed mask image
  11215. $minfo = $this->_getImage($tempfile_alpha, false);
  11216. unlink($tempfile_alpha);
  11217. if (!$minfo) {
  11218. return $this->_imageError($file, $firsttime, 'Error parsing temporary file (' . $tempfile_alpha . ') created with GD library to parse PNG image');
  11219. }
  11220. $imgmask = count($this->images) + 1;
  11221. $minfo['cs'] = 'DeviceGray';
  11222. $minfo['i'] = $imgmask;
  11223. $this->images[$tempfile_alpha] = $minfo;
  11224. // embed image, masked with previously embedded mask
  11225. $info = $this->_getImage($tempfile, false);
  11226. unlink($tempfile);
  11227. if (!$info) {
  11228. return $this->_imageError($file, $firsttime, 'Error parsing temporary file (' . $tempfile . ') created with GD library to parse PNG image');
  11229. }
  11230. }
  11231. $info['masked'] = $imgmask;
  11232. if ($ppUx) {
  11233. $info['set-dpi'] = $ppUx;
  11234. }
  11235. $info['type'] = 'png';
  11236. if ($firsttime) {
  11237. $info['i'] = count($this->images) + 1;
  11238. $info['interpolation'] = $interpolation; // mPDF 6
  11239. $this->images[$file] = $info;
  11240. }
  11241. return $info;
  11242. } else { // No alpha/transparency set (but cannot read directly because e.g. bit-depth != 8, interlaced etc)
  11243. // ICC profile
  11244. $icc = false;
  11245. $p = strpos($data, 'iCCP');
  11246. if ($p && $colspace == "Indexed") { // Cannot have ICC profile and Indexed together
  11247. $p += 4;
  11248. $n = $this->_fourbytes2int(substr($data, ($p - 8), 4));
  11249. $nullsep = strpos(substr($data, $p, 80), chr(0));
  11250. $icc = substr($data, ($p + $nullsep + 2), ($n - ($nullsep + 2)));
  11251. $icc = @gzuncompress($icc); // Ignored if fails
  11252. if ($icc) {
  11253. if (substr($icc, 36, 4) != 'acsp') {
  11254. $icc = false;
  11255. } // invalid ICC profile
  11256. else {
  11257. $input = substr($icc, 16, 4);
  11258. $output = substr($icc, 20, 4);
  11259. // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
  11260. if ($input != 'RGB ' || $output != 'XYZ ') {
  11261. $icc = false;
  11262. }
  11263. }
  11264. }
  11265. // Convert to RGB colorspace so can use ICC Profile
  11266. if ($icc) {
  11267. imagepalettetotruecolor($im);
  11268. $colspace = 'DeviceRGB';
  11269. $channels = 3;
  11270. }
  11271. }
  11272. if ($gamma_correction) {
  11273. imagegammacorrect($im, $gamma_correction, 2.2);
  11274. } // mPDF 6 Gamma correction
  11275. imagealphablending($im, false);
  11276. imagesavealpha($im, false);
  11277. imageinterlace($im, false);
  11278. if (!is_writable(_MPDF_TEMP_PATH)) { // mPDF 5.7.2
  11279. ob_start();
  11280. $check = @imagepng($im);
  11281. if (!$check) {
  11282. return $this->_imageError($file, $firsttime, 'Error creating temporary image object whilst using GD library to parse PNG image');
  11283. }
  11284. $this->_tempimg = ob_get_contents();
  11285. $this->_tempimglnk = 'var:_tempimg';
  11286. ob_end_clean();
  11287. $info = $this->_getImage($this->_tempimglnk, false);
  11288. if (!$info) {
  11289. return $this->_imageError($file, $firsttime, 'Error parsing temporary file image object created with GD library to parse PNG image');
  11290. }
  11291. imagedestroy($im);
  11292. } else {
  11293. $check = @imagepng($im, $tempfile);
  11294. if (!$check) {
  11295. return $this->_imageError($file, $firsttime, 'Failed to create temporary image file (' . $tempfile . ') parsing PNG image (' . $errpng . ')');
  11296. }
  11297. imagedestroy($im);
  11298. $info = $this->_getImage($tempfile, false);
  11299. unlink($tempfile);
  11300. if (!$info) {
  11301. return $this->_imageError($file, $firsttime, 'Error parsing temporary file (' . $tempfile . ') created with GD library to parse PNG image');
  11302. }
  11303. }
  11304. if ($ppUx) {
  11305. $info['set-dpi'] = $ppUx;
  11306. }
  11307. $info['type'] = 'png';
  11308. if ($firsttime) {
  11309. $info['i'] = count($this->images) + 1;
  11310. $info['interpolation'] = $interpolation; // mPDF 6
  11311. if ($icc) {
  11312. $info['ch'] = $channels;
  11313. $info['icc'] = $icc;
  11314. }
  11315. $this->images[$file] = $info;
  11316. }
  11317. return $info;
  11318. }
  11319. }
  11320. } else { // PNG image with no need to convert alph channels, bpc <> 8 etc.
  11321. $parms = '/DecodeParms <</Predictor 15 /Colors ' . $channels . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w . '>>';
  11322. //Scan chunks looking for palette, transparency and image data
  11323. $pal = '';
  11324. $trns = '';
  11325. $pngdata = '';
  11326. $icc = false;
  11327. $p = 33;
  11328. do {
  11329. $n = $this->_fourbytes2int(substr($data, $p, 4));
  11330. $p += 4;
  11331. $type = substr($data, $p, 4);
  11332. $p += 4;
  11333. if ($type == 'PLTE') {
  11334. //Read palette
  11335. $pal = substr($data, $p, $n);
  11336. $p += $n;
  11337. $p += 4;
  11338. } elseif ($type == 'tRNS') {
  11339. //Read transparency info
  11340. $t = substr($data, $p, $n);
  11341. $p += $n;
  11342. if ($ct == 0)
  11343. $trns = array(ord(substr($t, 1, 1)));
  11344. elseif ($ct == 2)
  11345. $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
  11346. else {
  11347. $pos = strpos($t, chr(0));
  11348. if (is_int($pos))
  11349. $trns = array($pos);
  11350. }
  11351. $p += 4;
  11352. }
  11353. elseif ($type == 'IDAT') {
  11354. $pngdata.=substr($data, $p, $n);
  11355. $p += $n;
  11356. $p += 4;
  11357. } elseif ($type == 'iCCP') {
  11358. $nullsep = strpos(substr($data, $p, 80), chr(0));
  11359. $icc = substr($data, ($p + $nullsep + 2), ($n - ($nullsep + 2)));
  11360. $icc = @gzuncompress($icc); // Ignored if fails
  11361. if ($icc) {
  11362. if (substr($icc, 36, 4) != 'acsp') {
  11363. $icc = false;
  11364. } // invalid ICC profile
  11365. else {
  11366. $input = substr($icc, 16, 4);
  11367. $output = substr($icc, 20, 4);
  11368. // Ignore Color profiles for conversion to other colorspaces e.g. CMYK/Lab
  11369. if ($input != 'RGB ' || $output != 'XYZ ') {
  11370. $icc = false;
  11371. }
  11372. }
  11373. }
  11374. $p += $n;
  11375. $p += 4;
  11376. } elseif ($type == 'IEND') {
  11377. break;
  11378. } elseif (preg_match('/[a-zA-Z]{4}/', $type)) {
  11379. $p += $n + 4;
  11380. } else {
  11381. return $this->_imageError($file, $firsttime, 'Error parsing PNG image data');
  11382. }
  11383. } while ($n);
  11384. if (!$pngdata) {
  11385. return $this->_imageError($file, $firsttime, 'Error parsing PNG image data - no IDAT data found');
  11386. }
  11387. if ($colspace == 'Indexed' && empty($pal)) {
  11388. return $this->_imageError($file, $firsttime, 'Error parsing PNG image data - missing colour palette');
  11389. }
  11390. if ($colspace == 'Indexed' && $icc) {
  11391. $icc = false;
  11392. } // mPDF 6 cannot have ICC profile and Indexed in a PDF document as both use the colorspace tag.
  11393. $info = array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $pngdata, 'ch' => $channels, 'icc' => $icc);
  11394. $info['type'] = 'png';
  11395. if ($ppUx) {
  11396. $info['set-dpi'] = $ppUx;
  11397. }
  11398. }
  11399. if (!$info) {
  11400. return $this->_imageError($file, $firsttime, 'Error parsing or converting PNG image');
  11401. }
  11402. if ($firsttime) {
  11403. $info['i'] = count($this->images) + 1;
  11404. $info['interpolation'] = $interpolation; // mPDF 6
  11405. $this->images[$file] = $info;
  11406. }
  11407. return $info;
  11408. }
  11409. // GIF
  11410. elseif ($type == 'gif') {
  11411. if (function_exists('gd_info')) {
  11412. $gd = gd_info();
  11413. } else {
  11414. $gd = array();
  11415. }
  11416. if (isset($gd['GIF Read Support']) && $gd['GIF Read Support']) {
  11417. $im = @imagecreatefromstring($data);
  11418. if ($im) {
  11419. $tempfile = _MPDF_TEMP_PATH . '_tempImgPNG' . md5($file) . RAND(1, 10000) . '.png';
  11420. imagealphablending($im, false);
  11421. imagesavealpha($im, false);
  11422. imageinterlace($im, false);
  11423. if (!is_writable($tempfile)) {
  11424. ob_start();
  11425. $check = @imagepng($im);
  11426. if (!$check) {
  11427. return $this->_imageError($file, $firsttime, 'Error creating temporary image object whilst using GD library to parse GIF image');
  11428. }
  11429. $this->_tempimg = ob_get_contents();
  11430. $this->_tempimglnk = 'var:_tempimg';
  11431. ob_end_clean();
  11432. $info = $this->_getImage($this->_tempimglnk, false);
  11433. if (!$info) {
  11434. return $this->_imageError($file, $firsttime, 'Error parsing temporary file image object created with GD library to parse GIF image');
  11435. }
  11436. imagedestroy($im);
  11437. } else {
  11438. $check = @imagepng($im, $tempfile);
  11439. if (!$check) {
  11440. return $this->_imageError($file, $firsttime, 'Error creating temporary file (' . $tempfile . ') whilst using GD library to parse GIF image');
  11441. }
  11442. $info = $this->_getImage($tempfile, false);
  11443. if (!$info) {
  11444. return $this->_imageError($file, $firsttime, 'Error parsing temporary file (' . $tempfile . ') created with GD library to parse GIF image');
  11445. }
  11446. imagedestroy($im);
  11447. unlink($tempfile);
  11448. }
  11449. $info['type'] = 'gif';
  11450. if ($firsttime) {
  11451. $info['i'] = count($this->images) + 1;
  11452. $info['interpolation'] = $interpolation; // mPDF 6
  11453. $this->images[$file] = $info;
  11454. }
  11455. return $info;
  11456. } else {
  11457. return $this->_imageError($file, $firsttime, 'Error creating GD image file from GIF image');
  11458. }
  11459. }
  11460. if (!class_exists('gif', false)) {
  11461. include_once(_MPDF_PATH . 'classes/gif.php');
  11462. }
  11463. $gif = new CGIF();
  11464. $h = 0;
  11465. $w = 0;
  11466. $gif->loadFile($data, 0);
  11467. if (isset($gif->m_img->m_gih->m_bLocalClr) && $gif->m_img->m_gih->m_bLocalClr) {
  11468. $nColors = $gif->m_img->m_gih->m_nTableSize;
  11469. $pal = $gif->m_img->m_gih->m_colorTable->toString();
  11470. if ((isset($bgColor)) and $bgColor != -1) { // mPDF 5.7.3
  11471. $bgColor = $gif->m_img->m_gih->m_colorTable->colorIndex($bgColor);
  11472. }
  11473. $colspace = 'Indexed';
  11474. } elseif (isset($gif->m_gfh->m_bGlobalClr) && $gif->m_gfh->m_bGlobalClr) {
  11475. $nColors = $gif->m_gfh->m_nTableSize;
  11476. $pal = $gif->m_gfh->m_colorTable->toString();
  11477. if ((isset($bgColor)) and $bgColor != -1) {
  11478. $bgColor = $gif->m_gfh->m_colorTable->colorIndex($bgColor);
  11479. }
  11480. $colspace = 'Indexed';
  11481. } else {
  11482. $nColors = 0;
  11483. $bgColor = -1;
  11484. $colspace = 'DeviceGray';
  11485. $pal = '';
  11486. }
  11487. $trns = '';
  11488. if (isset($gif->m_img->m_bTrans) && $gif->m_img->m_bTrans && ($nColors > 0)) {
  11489. $trns = array($gif->m_img->m_nTrans);
  11490. }
  11491. $gifdata = $gif->m_img->m_data;
  11492. $w = $gif->m_gfh->m_nWidth;
  11493. $h = $gif->m_gfh->m_nHeight;
  11494. $gif->ClearData();
  11495. if ($colspace == 'Indexed' and empty($pal)) {
  11496. return $this->_imageError($file, $firsttime, 'Error parsing GIF image - missing colour palette');
  11497. }
  11498. if ($this->compress) {
  11499. $gifdata = gzcompress($gifdata);
  11500. $info = array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => 8, 'f' => 'FlateDecode', 'pal' => $pal, 'trns' => $trns, 'data' => $gifdata);
  11501. } else {
  11502. $info = array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => 8, 'pal' => $pal, 'trns' => $trns, 'data' => $gifdata);
  11503. }
  11504. $info['type'] = 'gif';
  11505. if ($firsttime) {
  11506. $info['i'] = count($this->images) + 1;
  11507. $info['interpolation'] = $interpolation; // mPDF 6
  11508. $this->images[$file] = $info;
  11509. }
  11510. return $info;
  11511. }
  11512. /* -- IMAGES-BMP -- */
  11513. // BMP (Windows Bitmap)
  11514. elseif ($type == 'bmp') {
  11515. if (!class_exists('bmp', false)) {
  11516. include(_MPDF_PATH . 'classes/bmp.php');
  11517. }
  11518. if (empty($this->bmp)) {
  11519. $this->bmp = new bmp($this);
  11520. }
  11521. $info = $this->bmp->_getBMPimage($data, $file);
  11522. if (isset($info['error'])) {
  11523. return $this->_imageError($file, $firsttime, $info['error']);
  11524. }
  11525. if ($firsttime) {
  11526. $info['i'] = count($this->images) + 1;
  11527. $info['interpolation'] = $interpolation; // mPDF 6
  11528. $this->images[$file] = $info;
  11529. }
  11530. return $info;
  11531. }
  11532. /* -- END IMAGES-BMP -- */
  11533. /* -- IMAGES-WMF -- */
  11534. // WMF
  11535. elseif ($type == 'wmf') {
  11536. if (!class_exists('wmf', false)) {
  11537. include(_MPDF_PATH . 'classes/wmf.php');
  11538. }
  11539. if (empty($this->wmf)) {
  11540. $this->wmf = new wmf($this);
  11541. }
  11542. $wmfres = $this->wmf->_getWMFimage($data);
  11543. if ($wmfres[0] == 0) {
  11544. if ($wmfres[1]) {
  11545. return $this->_imageError($file, $firsttime, $wmfres[1]);
  11546. }
  11547. return $this->_imageError($file, $firsttime, 'Error parsing WMF image');
  11548. }
  11549. $info = array('x' => $wmfres[2][0], 'y' => $wmfres[2][1], 'w' => $wmfres[3][0], 'h' => $wmfres[3][1], 'data' => $wmfres[1]);
  11550. $info['i'] = count($this->formobjects) + 1;
  11551. $info['type'] = 'wmf';
  11552. $this->formobjects[$file] = $info;
  11553. return $info;
  11554. }
  11555. /* -- END IMAGES-WMF -- */
  11556. // UNKNOWN TYPE - try GD imagecreatefromstring
  11557. else {
  11558. if (function_exists('gd_info')) {
  11559. $gd = gd_info();
  11560. } else {
  11561. $gd = array();
  11562. }
  11563. if (isset($gd['PNG Support']) && $gd['PNG Support']) {
  11564. $im = @imagecreatefromstring($data);
  11565. if (!$im) {
  11566. return $this->_imageError($file, $firsttime, 'Error parsing image file - image type not recognised, and not supported by GD imagecreate');
  11567. }
  11568. $tempfile = _MPDF_TEMP_PATH . '_tempImgPNG' . md5($file) . RAND(1, 10000) . '.png';
  11569. imagealphablending($im, false);
  11570. imagesavealpha($im, false);
  11571. imageinterlace($im, false);
  11572. $check = @imagepng($im, $tempfile);
  11573. if (!$check) {
  11574. return $this->_imageError($file, $firsttime, 'Error creating temporary file (' . $tempfile . ') whilst using GD library to parse unknown image type');
  11575. }
  11576. $info = $this->_getImage($tempfile, false);
  11577. imagedestroy($im);
  11578. unlink($tempfile);
  11579. if (!$info) {
  11580. return $this->_imageError($file, $firsttime, 'Error parsing temporary file (' . $tempfile . ') created with GD library to parse unknown image type');
  11581. }
  11582. $info['type'] = 'png';
  11583. if ($firsttime) {
  11584. $info['i'] = count($this->images) + 1;
  11585. $info['interpolation'] = $interpolation; // mPDF 6
  11586. $this->images[$file] = $info;
  11587. }
  11588. return $info;
  11589. }
  11590. }
  11591. return $this->_imageError($file, $firsttime, 'Error parsing image file - image type not recognised');
  11592. }
  11593. //==============================================================
  11594. function _convImage(&$data, $colspace, $targetcs, $w, $h, $dpi, $mask, $gamma_correction = false, $pngcolortype = false)
  11595. { // mPDF 5.7.2 Gamma correction
  11596. if ($this->PDFA || $this->PDFX) {
  11597. $mask = false;
  11598. }
  11599. $im = @imagecreatefromstring($data);
  11600. $info = array();
  11601. $bpc = ord(substr($data, 24, 1));
  11602. if ($im) {
  11603. $imgdata = '';
  11604. $mimgdata = '';
  11605. $minfo = array();
  11606. // mPDF 6 Gamma correction
  11607. // Need to extract alpha channel info before imagegammacorrect (which loses the data)
  11608. if ($mask) { // i.e. $pngalpha for PNG
  11609. // mPDF 6
  11610. if ($colspace == 'Indexed') { // generate Alpha channel values from tRNS - only from PNG
  11611. //Read transparency info
  11612. $transparency = '';
  11613. $p = strpos($data, 'tRNS');
  11614. if ($p) {
  11615. $n = $this->_fourbytes2int(substr($data, ($p - 4), 4));
  11616. $transparency = substr($data, ($p + 4), $n);
  11617. // ord($transparency{$index}) = the alpha value for that index
  11618. // generate alpha channel
  11619. for ($ypx = 0; $ypx < $h; ++$ypx) {
  11620. for ($xpx = 0; $xpx < $w; ++$xpx) {
  11621. $colorindex = imagecolorat($im, $xpx, $ypx);
  11622. if ($colorindex >= $n) {
  11623. $alpha = 255;
  11624. } else {
  11625. $alpha = ord($transparency{$colorindex});
  11626. } // 0-255
  11627. $mimgdata .= chr($alpha);
  11628. }
  11629. }
  11630. }
  11631. } elseif ($pngcolortype === 0 || $pngcolortype == 2) { // generate Alpha channel values from tRNS
  11632. // Get transparency as array of RGB
  11633. $p = strpos($data, 'tRNS');
  11634. if ($p) {
  11635. $trns = '';
  11636. $n = $this->_fourbytes2int(substr($data, ($p - 4), 4));
  11637. $t = substr($data, ($p + 4), $n);
  11638. if ($colspace == 'DeviceGray') { // ct===0
  11639. $trns = array($this->_trnsvalue(substr($t, 0, 2), $bpc));
  11640. } else /* $colspace=='DeviceRGB' */ { // ct==2
  11641. $trns = array();
  11642. $trns[0] = $this->_trnsvalue(substr($t, 0, 2), $bpc);
  11643. $trns[1] = $this->_trnsvalue(substr($t, 2, 2), $bpc);
  11644. $trns[2] = $this->_trnsvalue(substr($t, 4, 2), $bpc);
  11645. }
  11646. // generate alpha channel
  11647. for ($ypx = 0; $ypx < $h; ++$ypx) {
  11648. for ($xpx = 0; $xpx < $w; ++$xpx) {
  11649. $rgb = imagecolorat($im, $xpx, $ypx);
  11650. $r = ($rgb >> 16) & 0xFF;
  11651. $g = ($rgb >> 8) & 0xFF;
  11652. $b = $rgb & 0xFF;
  11653. if ($colspace == 'DeviceGray' && $b == $trns[0]) {
  11654. $alpha = 0;
  11655. } elseif ($r == $trns[0] && $g == $trns[1] && $b == $trns[2]) {
  11656. $alpha = 0;
  11657. } // ct==2
  11658. else {
  11659. $alpha = 255;
  11660. }
  11661. $mimgdata .= chr($alpha);
  11662. }
  11663. }
  11664. }
  11665. } else {
  11666. for ($i = 0; $i < $h; $i++) {
  11667. for ($j = 0; $j < $w; $j++) {
  11668. $rgb = imagecolorat($im, $j, $i);
  11669. $alpha = ($rgb & 0x7F000000) >> 24;
  11670. if ($alpha < 127) {
  11671. $mimgdata .= chr(255 - ($alpha * 2));
  11672. } else {
  11673. $mimgdata .= chr(0);
  11674. }
  11675. }
  11676. }
  11677. }
  11678. }
  11679. if ($gamma_correction) {
  11680. imagegammacorrect($im, $gamma_correction, 2.2);
  11681. } // mPDF 6 Gamma correction
  11682. //Read transparency info
  11683. $trns = array();
  11684. $trnsrgb = false;
  11685. if (!$this->PDFA && !$this->PDFX && !$mask) { // mPDF 6 added NOT mask
  11686. $p = strpos($data, 'tRNS');
  11687. if ($p) {
  11688. $n = $this->_fourbytes2int(substr($data, ($p - 4), 4));
  11689. $t = substr($data, ($p + 4), $n);
  11690. if ($colspace == 'DeviceGray') { // ct===0
  11691. $trns = array($this->_trnsvalue(substr($t, 0, 2), $bpc));
  11692. } elseif ($colspace == 'DeviceRGB') { // ct==2
  11693. $trns[0] = $this->_trnsvalue(substr($t, 0, 2), $bpc);
  11694. $trns[1] = $this->_trnsvalue(substr($t, 2, 2), $bpc);
  11695. $trns[2] = $this->_trnsvalue(substr($t, 4, 2), $bpc);
  11696. $trnsrgb = $trns;
  11697. if ($targetcs == 'DeviceCMYK') {
  11698. $col = $this->rgb2cmyk(array(3, $trns[0], $trns[1], $trns[2]));
  11699. $c1 = intval($col[1] * 2.55);
  11700. $c2 = intval($col[2] * 2.55);
  11701. $c3 = intval($col[3] * 2.55);
  11702. $c4 = intval($col[4] * 2.55);
  11703. $trns = array($c1, $c2, $c3, $c4);
  11704. } elseif ($targetcs == 'DeviceGray') {
  11705. $c = intval(($trns[0] * .21) + ($trns[1] * .71) + ($trns[2] * .07));
  11706. $trns = array($c);
  11707. }
  11708. } else { // Indexed
  11709. $pos = strpos($t, chr(0));
  11710. if (is_int($pos)) {
  11711. $pal = imagecolorsforindex($im, $pos);
  11712. $r = $pal['red'];
  11713. $g = $pal['green'];
  11714. $b = $pal['blue'];
  11715. $trns = array($r, $g, $b); // ****
  11716. $trnsrgb = $trns;
  11717. if ($targetcs == 'DeviceCMYK') {
  11718. $col = $this->rgb2cmyk(array(3, $r, $g, $b));
  11719. $c1 = intval($col[1] * 2.55);
  11720. $c2 = intval($col[2] * 2.55);
  11721. $c3 = intval($col[3] * 2.55);
  11722. $c4 = intval($col[4] * 2.55);
  11723. $trns = array($c1, $c2, $c3, $c4);
  11724. } elseif ($targetcs == 'DeviceGray') {
  11725. $c = intval(($r * .21) + ($g * .71) + ($b * .07));
  11726. $trns = array($c);
  11727. }
  11728. }
  11729. }
  11730. }
  11731. }
  11732. for ($i = 0; $i < $h; $i++) {
  11733. for ($j = 0; $j < $w; $j++) {
  11734. $rgb = imagecolorat($im, $j, $i);
  11735. $r = ($rgb >> 16) & 0xFF;
  11736. $g = ($rgb >> 8) & 0xFF;
  11737. $b = $rgb & 0xFF;
  11738. if ($colspace == 'Indexed') {
  11739. $pal = imagecolorsforindex($im, $rgb);
  11740. $r = $pal['red'];
  11741. $g = $pal['green'];
  11742. $b = $pal['blue'];
  11743. }
  11744. if ($targetcs == 'DeviceCMYK') {
  11745. $col = $this->rgb2cmyk(array(3, $r, $g, $b));
  11746. $c1 = intval($col[1] * 2.55);
  11747. $c2 = intval($col[2] * 2.55);
  11748. $c3 = intval($col[3] * 2.55);
  11749. $c4 = intval($col[4] * 2.55);
  11750. if ($trnsrgb) {
  11751. // original pixel was not set as transparent but processed color does match
  11752. if ($trnsrgb != array($r, $g, $b) && $trns == array($c1, $c2, $c3, $c4)) {
  11753. if ($c4 == 0) {
  11754. $c4 = 1;
  11755. } else {
  11756. $c4--;
  11757. }
  11758. }
  11759. }
  11760. $imgdata .= chr($c1) . chr($c2) . chr($c3) . chr($c4);
  11761. } elseif ($targetcs == 'DeviceGray') {
  11762. $c = intval(($r * .21) + ($g * .71) + ($b * .07));
  11763. if ($trnsrgb) {
  11764. // original pixel was not set as transparent but processed color does match
  11765. if ($trnsrgb != array($r, $g, $b) && $trns == array($c)) {
  11766. if ($c == 0) {
  11767. $c = 1;
  11768. } else {
  11769. $c--;
  11770. }
  11771. }
  11772. }
  11773. $imgdata .= chr($c);
  11774. } elseif ($targetcs == 'DeviceRGB') {
  11775. $imgdata .= chr($r) . chr($g) . chr($b);
  11776. }
  11777. }
  11778. }
  11779. if ($targetcs == 'DeviceGray') {
  11780. $ncols = 1;
  11781. } elseif ($targetcs == 'DeviceRGB') {
  11782. $ncols = 3;
  11783. } elseif ($targetcs == 'DeviceCMYK') {
  11784. $ncols = 4;
  11785. }
  11786. $imgdata = gzcompress($imgdata);
  11787. $info = array('w' => $w, 'h' => $h, 'cs' => $targetcs, 'bpc' => 8, 'f' => 'FlateDecode', 'data' => $imgdata, 'type' => 'png',
  11788. 'parms' => '/DecodeParms <</Colors ' . $ncols . ' /BitsPerComponent 8 /Columns ' . $w . '>>');
  11789. if ($dpi) {
  11790. $info['set-dpi'] = $dpi;
  11791. }
  11792. if ($mask) {
  11793. $mimgdata = gzcompress($mimgdata);
  11794. $minfo = array('w' => $w, 'h' => $h, 'cs' => 'DeviceGray', 'bpc' => 8, 'f' => 'FlateDecode', 'data' => $mimgdata, 'type' => 'png',
  11795. 'parms' => '/DecodeParms <</Colors ' . $ncols . ' /BitsPerComponent 8 /Columns ' . $w . '>>');
  11796. if ($dpi) {
  11797. $minfo['set-dpi'] = $dpi;
  11798. }
  11799. $tempfile = '_tempImgPNG' . md5($data) . RAND(1, 10000) . '.png';
  11800. $imgmask = count($this->images) + 1;
  11801. $minfo['i'] = $imgmask;
  11802. $this->images[$tempfile] = $minfo;
  11803. $info['masked'] = $imgmask;
  11804. } elseif ($trns) {
  11805. $info['trns'] = $trns;
  11806. }
  11807. imagedestroy($im);
  11808. }
  11809. return $info;
  11810. }
  11811. function _trnsvalue($s, $bpc)
  11812. {
  11813. // Corrects 2-byte integer to 8-bit depth value
  11814. // If original image is bpc != 8, tRNS will be in this bpc
  11815. // $im from imagecreatefromstring will always be in bpc=8
  11816. // So why do we only need to correct 16-bit tRNS and NOT 2 or 4-bit???
  11817. $n = $this->_twobytes2int($s);
  11818. if ($bpc == 16) {
  11819. $n = ($n >> 8);
  11820. }
  11821. //elseif ($bpc==4) { $n = ($n << 2); }
  11822. //elseif ($bpc==2) { $n = ($n << 4); }
  11823. return $n;
  11824. }
  11825. function _fourbytes2int($s)
  11826. {
  11827. //Read a 4-byte integer from string
  11828. return (ord($s[0]) << 24) + (ord($s[1]) << 16) + (ord($s[2]) << 8) + ord($s[3]);
  11829. }
  11830. function _twobytes2int($s)
  11831. { // equivalent to _get_ushort
  11832. //Read a 2-byte integer from string
  11833. return (ord(substr($s, 0, 1)) << 8) + ord(substr($s, 1, 1));
  11834. }
  11835. function _jpgHeaderFromString(&$data)
  11836. {
  11837. $p = 4;
  11838. $p += $this->_twobytes2int(substr($data, $p, 2)); // Length of initial marker block
  11839. $marker = substr($data, $p, 2);
  11840. while ($marker != chr(255) . chr(192) && $marker != chr(255) . chr(194) && $p < strlen($data)) {
  11841. // Start of frame marker (FFC0) or (FFC2) mPDF 4.4.004
  11842. $p += ($this->_twobytes2int(substr($data, $p + 2, 2))) + 2; // Length of marker block
  11843. $marker = substr($data, $p, 2);
  11844. }
  11845. if ($marker != chr(255) . chr(192) && $marker != chr(255) . chr(194)) {
  11846. return false;
  11847. }
  11848. return substr($data, $p + 2, 10);
  11849. }
  11850. function _jpgDataFromHeader($hdr)
  11851. {
  11852. $bpc = ord(substr($hdr, 2, 1));
  11853. if (!$bpc) {
  11854. $bpc = 8;
  11855. }
  11856. $h = $this->_twobytes2int(substr($hdr, 3, 2));
  11857. $w = $this->_twobytes2int(substr($hdr, 5, 2));
  11858. $channels = ord(substr($hdr, 7, 1));
  11859. if ($channels == 3) {
  11860. $colspace = 'DeviceRGB';
  11861. } elseif ($channels == 4) {
  11862. $colspace = 'DeviceCMYK';
  11863. } else {
  11864. $colspace = 'DeviceGray';
  11865. }
  11866. return array($w, $h, $colspace, $bpc, $channels);
  11867. }
  11868. function file_get_contents_by_curl($url, &$data)
  11869. {
  11870. $timeout = 5;
  11871. $ch = curl_init($url);
  11872. curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:13.0) Gecko/20100101 Firefox/13.0.1'); // mPDF 5.7.4
  11873. curl_setopt($ch, CURLOPT_HEADER, 0);
  11874. curl_setopt($ch, CURLOPT_NOBODY, 0);
  11875. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  11876. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  11877. $data = curl_exec($ch);
  11878. curl_close($ch);
  11879. }
  11880. function file_get_contents_by_socket($url, &$data)
  11881. {
  11882. // mPDF 5.7.3
  11883. $timeout = 1;
  11884. $p = parse_url($url);
  11885. $file = $p['path'];
  11886. if ($p['scheme'] == 'https') {
  11887. $prefix = 'ssl://';
  11888. $port = ($p['port'] ? $p['port'] : 443);
  11889. } else {
  11890. $prefix = '';
  11891. $port = ($p['port'] ? $p['port'] : 80);
  11892. }
  11893. if ($p['query']) {
  11894. $file .= '?' . $p['query'];
  11895. }
  11896. if (!($fh = @fsockopen($prefix . $p['host'], $port, $errno, $errstr, $timeout))) {
  11897. return false;
  11898. }
  11899. $getstring = "GET " . $file . " HTTP/1.0 \r\n" .
  11900. "Host: " . $p['host'] . " \r\n" .
  11901. "Connection: close\r\n\r\n";
  11902. fwrite($fh, $getstring);
  11903. // Get rid of HTTP header
  11904. $s = fgets($fh, 1024);
  11905. if (!$s) {
  11906. return false;
  11907. }
  11908. $httpheader .= $s;
  11909. while (!feof($fh)) {
  11910. $s = fgets($fh, 1024);
  11911. if ($s == "\r\n") {
  11912. break;
  11913. }
  11914. }
  11915. $data = '';
  11916. while (!feof($fh)) {
  11917. $data .= fgets($fh, 1024);
  11918. }
  11919. fclose($fh);
  11920. }
  11921. //==============================================================
  11922. function _imageTypeFromString(&$data)
  11923. {
  11924. $type = '';
  11925. if (substr($data, 6, 4) == 'JFIF' || substr($data, 6, 4) == 'Exif' || substr($data, 0, 2) == chr(255) . chr(216)) { // 0xFF 0xD8 // mpDF 5.7.2
  11926. $type = 'jpeg';
  11927. } elseif (substr($data, 0, 6) == "GIF87a" || substr($data, 0, 6) == "GIF89a") {
  11928. $type = 'gif';
  11929. } elseif (substr($data, 0, 8) == chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
  11930. $type = 'png';
  11931. }
  11932. /* -- IMAGES-WMF -- */ elseif (substr($data, 0, 4) == chr(215) . chr(205) . chr(198) . chr(154)) {
  11933. $type = 'wmf';
  11934. }
  11935. /* -- END IMAGES-WMF -- */ elseif (preg_match('/<svg.*<\/svg>/is', $data)) {
  11936. $type = 'svg';
  11937. }
  11938. // BMP images
  11939. elseif (substr($data, 0, 2) == "BM") {
  11940. $type = 'bmp';
  11941. }
  11942. return $type;
  11943. }
  11944. //==============================================================
  11945. // Moved outside WMF as also needed for SVG
  11946. function _putformobjects()
  11947. {
  11948. reset($this->formobjects);
  11949. while (list($file, $info) = each($this->formobjects)) {
  11950. $this->_newobj();
  11951. $this->formobjects[$file]['n'] = $this->n;
  11952. $this->_out('<</Type /XObject');
  11953. $this->_out('/Subtype /Form');
  11954. $this->_out('/Group ' . ($this->n + 1) . ' 0 R');
  11955. $this->_out('/BBox [' . $info['x'] . ' ' . $info['y'] . ' ' . ($info['w'] + $info['x']) . ' ' . ($info['h'] + $info['y']) . ']');
  11956. if ($this->compress)
  11957. $this->_out('/Filter /FlateDecode');
  11958. $data = ($this->compress) ? gzcompress($info['data']) : $info['data'];
  11959. $this->_out('/Length ' . strlen($data) . '>>');
  11960. $this->_putstream($data);
  11961. unset($this->formobjects[$file]['data']);
  11962. $this->_out('endobj');
  11963. // Required for SVG transparency (opacity) to work
  11964. $this->_newobj();
  11965. $this->_out('<</Type /Group');
  11966. $this->_out('/S /Transparency');
  11967. $this->_out('>>');
  11968. $this->_out('endobj');
  11969. }
  11970. }
  11971. function _freadint($f)
  11972. {
  11973. //Read a 4-byte integer from file
  11974. $i = ord(fread($f, 1)) << 24;
  11975. $i+=ord(fread($f, 1)) << 16;
  11976. $i+=ord(fread($f, 1)) << 8;
  11977. $i+=ord(fread($f, 1));
  11978. return $i;
  11979. }
  11980. function _UTF16BEtextstring($s)
  11981. {
  11982. $s = $this->UTF8ToUTF16BE($s, true);
  11983. /* -- ENCRYPTION -- */
  11984. if ($this->encrypted) {
  11985. $s = $this->_RC4($this->_objectkey($this->_current_obj_id), $s);
  11986. }
  11987. /* -- END ENCRYPTION -- */
  11988. return '(' . $this->_escape($s) . ')';
  11989. }
  11990. function _textstring($s)
  11991. {
  11992. /* -- ENCRYPTION -- */
  11993. if ($this->encrypted) {
  11994. $s = $this->_RC4($this->_objectkey($this->_current_obj_id), $s);
  11995. }
  11996. /* -- END ENCRYPTION -- */
  11997. return '(' . $this->_escape($s) . ')';
  11998. }
  11999. function _escape($s)
  12000. {
  12001. // the chr(13) substitution fixes the Bugs item #1421290.
  12002. return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
  12003. }
  12004. function _putstream($s)
  12005. {
  12006. /* -- ENCRYPTION -- */
  12007. if ($this->encrypted) {
  12008. $s = $this->_RC4($this->_objectkey($this->_current_obj_id), $s);
  12009. }
  12010. /* -- END ENCRYPTION -- */
  12011. $this->_out('stream');
  12012. $this->_out($s);
  12013. $this->_out('endstream');
  12014. }
  12015. function _out($s, $ln = true)
  12016. {
  12017. if ($this->state == 2) {
  12018. if ($this->bufferoutput) {
  12019. $this->headerbuffer.= $s . "\n";
  12020. }
  12021. /* -- COLUMNS -- */ elseif (($this->ColActive) && !$this->processingHeader && !$this->processingFooter) {
  12022. // Captures everything in buffer for columns; Almost everything is sent from fn. Cell() except:
  12023. // Images sent from Image() or
  12024. // later sent as _out($textto) in printbuffer
  12025. // Line()
  12026. if (preg_match('/q \d+\.\d\d+ 0 0 (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ cm \/(I|FO)\d+ Do Q/', $s, $m)) { // Image data
  12027. $h = ($m[1] / _MPDFK);
  12028. // Update/overwrite the lowest bottom of printing y value for a column
  12029. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
  12030. }
  12031. /* -- TABLES -- */ elseif (preg_match('/\d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ ([\-]{0,1}\d+\.\d\d+) re/', $s, $m) && $this->tableLevel > 0) { // Rect in table
  12032. $h = ($m[1] / _MPDFK);
  12033. // Update/overwrite the lowest bottom of printing y value for a column
  12034. $this->ColDetails[$this->CurrCol]['bottom_margin'] = max($this->ColDetails[$this->CurrCol]['bottom_margin'], ($this->y + $h));
  12035. }
  12036. /* -- END TABLES -- */ else { // Td Text Set in Cell()
  12037. if (isset($this->ColDetails[$this->CurrCol]['bottom_margin'])) {
  12038. $h = $this->ColDetails[$this->CurrCol]['bottom_margin'] - $this->y;
  12039. } else {
  12040. $h = 0;
  12041. }
  12042. }
  12043. if ($h < 0) {
  12044. $h = -$h;
  12045. }
  12046. $this->columnbuffer[] = array(
  12047. 's' => $s, // Text string to output
  12048. 'col' => $this->CurrCol, // Column when printed
  12049. 'x' => $this->x, // x when printed
  12050. 'y' => $this->y, // this->y when printed (after column break)
  12051. 'h' => $h // actual y at bottom when printed = y+h
  12052. );
  12053. }
  12054. /* -- END COLUMNS -- */
  12055. /* -- TABLES -- */ elseif ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
  12056. // Captures eveything in buffer for rotated tables;
  12057. $this->tablebuffer .= $s . "\n";
  12058. }
  12059. /* -- END TABLES -- */ elseif ($this->kwt && !$this->processingHeader && !$this->processingFooter) {
  12060. // Captures eveything in buffer for keep-with-table (h1-6);
  12061. $this->kwt_buffer[] = array(
  12062. 's' => $s, // Text string to output
  12063. 'x' => $this->x, // x when printed
  12064. 'y' => $this->y, // y when printed
  12065. );
  12066. } elseif (($this->keep_block_together) && !$this->processingHeader && !$this->processingFooter) {
  12067. // do nothing
  12068. } else {
  12069. $this->pages[$this->page] .= $s . ($ln == true ? "\n" : '');
  12070. }
  12071. } else {
  12072. $this->buffer .= $s . ($ln == true ? "\n" : '');
  12073. }
  12074. }
  12075. /* -- WATERMARK -- */
  12076. // add a watermark
  12077. function watermark($texte, $angle = 45, $fontsize = 96, $alpha = 0.2)
  12078. {
  12079. if ($this->PDFA || $this->PDFX) {
  12080. throw new MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
  12081. }
  12082. if (!$this->watermark_font) {
  12083. $this->watermark_font = $this->default_font;
  12084. }
  12085. $this->SetFont($this->watermark_font, "B", $fontsize, false); // Don't output
  12086. $texte = $this->purify_utf8_text($texte);
  12087. if ($this->text_input_as_HTML) {
  12088. $texte = $this->all_entities_to_utf8($texte);
  12089. }
  12090. if ($this->usingCoreFont) {
  12091. $texte = mb_convert_encoding($texte, $this->mb_enc, 'UTF-8');
  12092. }
  12093. // DIRECTIONALITY
  12094. if (preg_match("/([" . $this->pregRTLchars . "])/u", $texte)) {
  12095. $this->biDirectional = true;
  12096. } // *OTL*
  12097. $textvar = 0;
  12098. $save_OTLtags = $this->OTLtags;
  12099. $this->OTLtags = array();
  12100. if ($this->useKerning) {
  12101. if ($this->CurrentFont['haskernGPOS']) {
  12102. $this->OTLtags['Plus'] .= ' kern';
  12103. } else {
  12104. $textvar = ($textvar | FC_KERNING);
  12105. }
  12106. }
  12107. /* -- OTL -- */
  12108. // Use OTL OpenType Table Layout - GSUB & GPOS
  12109. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  12110. $texte = $this->otl->applyOTL($texte, $this->CurrentFont['useOTL']);
  12111. $OTLdata = $this->otl->OTLdata;
  12112. }
  12113. /* -- END OTL -- */
  12114. $this->OTLtags = $save_OTLtags;
  12115. $this->magic_reverse_dir($texte, $this->directionality, $OTLdata);
  12116. $this->SetAlpha($alpha);
  12117. $this->SetTColor($this->ConvertColor(0));
  12118. $szfont = $fontsize;
  12119. $loop = 0;
  12120. $maxlen = (min($this->w, $this->h) ); // sets max length of text as 7/8 width/height of page
  12121. while ($loop == 0) {
  12122. $this->SetFont($this->watermark_font, "B", $szfont, false); // Don't output
  12123. $offset = ((sin(deg2rad($angle))) * ($szfont / _MPDFK));
  12124. $strlen = $this->GetStringWidth($texte, true, $OTLdata, $textvar);
  12125. if ($strlen > $maxlen - $offset)
  12126. $szfont --;
  12127. else
  12128. $loop ++;
  12129. }
  12130. $this->SetFont($this->watermark_font, "B", $szfont - 0.1, true, true); // Output The -0.1 is because SetFont above is not written to PDF
  12131. // Repeating it will not output anything as mPDF thinks it is set
  12132. $adj = ((cos(deg2rad($angle))) * ($strlen / 2));
  12133. $opp = ((sin(deg2rad($angle))) * ($strlen / 2));
  12134. $wx = ($this->w / 2) - $adj + $offset / 3;
  12135. $wy = ($this->h / 2) + $opp;
  12136. $this->Rotate($angle, $wx, $wy);
  12137. $this->Text($wx, $wy, $texte, $OTLdata, $textvar);
  12138. $this->Rotate(0);
  12139. $this->SetTColor($this->ConvertColor(0));
  12140. $this->SetAlpha(1);
  12141. }
  12142. function watermarkImg($src, $alpha = 0.2)
  12143. {
  12144. if ($this->PDFA || $this->PDFX) {
  12145. throw new MpdfException('PDFA and PDFX do not permit transparency, so mPDF does not allow Watermarks!');
  12146. }
  12147. if ($this->watermarkImgBehind) {
  12148. $this->watermarkImgAlpha = $this->SetAlpha($alpha, 'Normal', true);
  12149. } else {
  12150. $this->SetAlpha($alpha, $this->watermarkImgAlphaBlend);
  12151. }
  12152. $this->Image($src, 0, 0, 0, 0, '', '', true, true, true);
  12153. if (!$this->watermarkImgBehind) {
  12154. $this->SetAlpha(1);
  12155. }
  12156. }
  12157. /* -- END WATERMARK -- */
  12158. function Rotate($angle, $x = -1, $y = -1)
  12159. {
  12160. if ($x == -1)
  12161. $x = $this->x;
  12162. if ($y == -1)
  12163. $y = $this->y;
  12164. if ($this->angle != 0)
  12165. $this->_out('Q');
  12166. $this->angle = $angle;
  12167. if ($angle != 0) {
  12168. $angle*=M_PI / 180;
  12169. $c = cos($angle);
  12170. $s = sin($angle);
  12171. $cx = $x * _MPDFK;
  12172. $cy = ($this->h - $y) * _MPDFK;
  12173. $this->_out(sprintf('q %.5F %.5F %.5F %.5F %.3F %.3F cm 1 0 0 1 %.3F %.3F cm', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy));
  12174. }
  12175. }
  12176. function CircularText($x, $y, $r, $text, $align = 'top', $fontfamily = '', $fontsize = 0, $fontstyle = '', $kerning = 120, $fontwidth = 100, $divider)
  12177. {
  12178. if (!class_exists('directw', false)) {
  12179. include(_MPDF_PATH . 'classes/directw.php');
  12180. }
  12181. if (empty($this->directw)) {
  12182. $this->directw = new directw($this);
  12183. }
  12184. $this->directw->CircularText($x, $y, $r, $text, $align, $fontfamily, $fontsize, $fontstyle, $kerning, $fontwidth, $divider);
  12185. }
  12186. // From Invoice
  12187. function RoundedRect($x, $y, $w, $h, $r, $style = '')
  12188. {
  12189. $hp = $this->h;
  12190. if ($style == 'F')
  12191. $op = 'f';
  12192. elseif ($style == 'FD' or $style == 'DF')
  12193. $op = 'B';
  12194. else
  12195. $op = 'S';
  12196. $MyArc = 4 / 3 * (sqrt(2) - 1);
  12197. $this->_out(sprintf('%.3F %.3F m', ($x + $r) * _MPDFK, ($hp - $y) * _MPDFK));
  12198. $xc = $x + $w - $r;
  12199. $yc = $y + $r;
  12200. $this->_out(sprintf('%.3F %.3F l', $xc * _MPDFK, ($hp - $y) * _MPDFK));
  12201. $this->_Arc($xc + $r * $MyArc, $yc - $r, $xc + $r, $yc - $r * $MyArc, $xc + $r, $yc);
  12202. $xc = $x + $w - $r;
  12203. $yc = $y + $h - $r;
  12204. $this->_out(sprintf('%.3F %.3F l', ($x + $w) * _MPDFK, ($hp - $yc) * _MPDFK));
  12205. $this->_Arc($xc + $r, $yc + $r * $MyArc, $xc + $r * $MyArc, $yc + $r, $xc, $yc + $r);
  12206. $xc = $x + $r;
  12207. $yc = $y + $h - $r;
  12208. $this->_out(sprintf('%.3F %.3F l', $xc * _MPDFK, ($hp - ($y + $h)) * _MPDFK));
  12209. $this->_Arc($xc - $r * $MyArc, $yc + $r, $xc - $r, $yc + $r * $MyArc, $xc - $r, $yc);
  12210. $xc = $x + $r;
  12211. $yc = $y + $r;
  12212. $this->_out(sprintf('%.3F %.3F l', ($x) * _MPDFK, ($hp - $yc) * _MPDFK));
  12213. $this->_Arc($xc - $r, $yc - $r * $MyArc, $xc - $r * $MyArc, $yc - $r, $xc, $yc - $r);
  12214. $this->_out($op);
  12215. }
  12216. function _Arc($x1, $y1, $x2, $y2, $x3, $y3)
  12217. {
  12218. $h = $this->h;
  12219. $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', $x1 * _MPDFK, ($h - $y1) * _MPDFK, $x2 * _MPDFK, ($h - $y2) * _MPDFK, $x3 * _MPDFK, ($h - $y3) * _MPDFK));
  12220. }
  12221. //====================================================
  12222. /* -- DIRECTW -- */
  12223. function Shaded_box($text, $font = '', $fontstyle = 'B', $szfont = '', $width = '70%', $style = 'DF', $radius = 2.5, $fill = '#FFFFFF', $color = '#000000', $pad = 2)
  12224. {
  12225. // F (shading - no line),S (line, no shading),DF (both)
  12226. if (!class_exists('directw', false)) {
  12227. include(_MPDF_PATH . 'classes/directw.php');
  12228. }
  12229. if (empty($this->directw)) {
  12230. $this->directw = new directw($this);
  12231. }
  12232. $this->directw->Shaded_box($text, $font, $fontstyle, $szfont, $width, $style, $radius, $fill, $color, $pad);
  12233. }
  12234. /* -- END DIRECTW -- */
  12235. function UTF8StringToArray($str, $addSubset = true)
  12236. {
  12237. $out = array();
  12238. $len = strlen($str);
  12239. for ($i = 0; $i < $len; $i++) {
  12240. $uni = -1;
  12241. $h = ord($str[$i]);
  12242. if ($h <= 0x7F)
  12243. $uni = $h;
  12244. elseif ($h >= 0xC2) {
  12245. if (($h <= 0xDF) && ($i < $len - 1))
  12246. $uni = ($h & 0x1F) << 6 | (ord($str[++$i]) & 0x3F);
  12247. elseif (($h <= 0xEF) && ($i < $len - 2))
  12248. $uni = ($h & 0x0F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
  12249. elseif (($h <= 0xF4) && ($i < $len - 3))
  12250. $uni = ($h & 0x0F) << 18 | (ord($str[++$i]) & 0x3F) << 12 | (ord($str[++$i]) & 0x3F) << 6 | (ord($str[++$i]) & 0x3F);
  12251. }
  12252. if ($uni >= 0) {
  12253. $out[] = $uni;
  12254. if ($addSubset && isset($this->CurrentFont['subset'])) {
  12255. $this->CurrentFont['subset'][$uni] = $uni;
  12256. }
  12257. }
  12258. }
  12259. return $out;
  12260. }
  12261. //Convert utf-8 string to <HHHHHH> for Font Subsets
  12262. function UTF8toSubset($str)
  12263. {
  12264. $ret = '<';
  12265. //$str = preg_replace('/'.preg_quote($this->aliasNbPg,'/').'/', chr(7), $str ); // mPDF 6 deleted
  12266. //$str = preg_replace('/'.preg_quote($this->aliasNbPgGp,'/').'/', chr(8), $str ); // mPDF 6 deleted
  12267. $unicode = $this->UTF8StringToArray($str);
  12268. $orig_fid = $this->CurrentFont['subsetfontids'][0];
  12269. $last_fid = $this->CurrentFont['subsetfontids'][0];
  12270. foreach ($unicode as $c) {
  12271. /* // mPDF 6 deleted
  12272. if ($c == 7 || $c == 8) {
  12273. if ($orig_fid != $last_fid) {
  12274. $ret .= '> Tj /F'.$orig_fid.' '.$this->FontSizePt.' Tf <';
  12275. $last_fid = $orig_fid;
  12276. }
  12277. if ($c == 7) { $ret .= $this->aliasNbPgHex; }
  12278. else { $ret .= $this->aliasNbPgGpHex; }
  12279. continue;
  12280. }
  12281. */
  12282. if (!$this->_charDefined($this->CurrentFont['cw'], $c)) {
  12283. $c = 0;
  12284. } // mPDF 6
  12285. for ($i = 0; $i < 99; $i++) {
  12286. // return c as decimal char
  12287. $init = array_search($c, $this->CurrentFont['subsets'][$i]);
  12288. if ($init !== false) {
  12289. if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
  12290. $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
  12291. $last_fid = $this->CurrentFont['subsetfontids'][$i];
  12292. }
  12293. $ret .= sprintf("%02s", strtoupper(dechex($init)));
  12294. break;
  12295. }
  12296. // TrueType embedded SUBSETS
  12297. elseif (count($this->CurrentFont['subsets'][$i]) < 255) {
  12298. $n = count($this->CurrentFont['subsets'][$i]);
  12299. $this->CurrentFont['subsets'][$i][$n] = $c;
  12300. if ($this->CurrentFont['subsetfontids'][$i] != $last_fid) {
  12301. $ret .= '> Tj /F' . $this->CurrentFont['subsetfontids'][$i] . ' ' . $this->FontSizePt . ' Tf <';
  12302. $last_fid = $this->CurrentFont['subsetfontids'][$i];
  12303. }
  12304. $ret .= sprintf("%02s", strtoupper(dechex($n)));
  12305. break;
  12306. } elseif (!isset($this->CurrentFont['subsets'][($i + 1)])) {
  12307. // TrueType embedded SUBSETS
  12308. $this->CurrentFont['subsets'][($i + 1)] = array(0 => 0);
  12309. $new_fid = count($this->fonts) + $this->extraFontSubsets + 1;
  12310. $this->CurrentFont['subsetfontids'][($i + 1)] = $new_fid;
  12311. $this->extraFontSubsets++;
  12312. }
  12313. }
  12314. }
  12315. $ret .= '>';
  12316. if ($last_fid != $orig_fid) {
  12317. $ret .= ' Tj /F' . $orig_fid . ' ' . $this->FontSizePt . ' Tf <> ';
  12318. }
  12319. return $ret;
  12320. }
  12321. // Converts UTF-8 strings to UTF16-BE.
  12322. function UTF8ToUTF16BE($str, $setbom = true)
  12323. {
  12324. if ($this->checkSIP && preg_match("/([\x{20000}-\x{2FFFF}])/u", $str)) {
  12325. if (!in_array($this->currentfontfamily, array('gb', 'big5', 'sjis', 'uhc', 'gbB', 'big5B', 'sjisB', 'uhcB', 'gbI', 'big5I', 'sjisI', 'uhcI',
  12326. 'gbBI', 'big5BI', 'sjisBI', 'uhcBI'))) {
  12327. $str = preg_replace("/[\x{20000}-\x{2FFFF}]/u", chr(0), $str);
  12328. }
  12329. }
  12330. if ($this->checkSMP && preg_match("/([\x{10000}-\x{1FFFF}])/u", $str)) {
  12331. $str = preg_replace("/[\x{10000}-\x{1FFFF}]/u", chr(0), $str);
  12332. }
  12333. $outstr = ""; // string to be returned
  12334. if ($setbom) {
  12335. $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
  12336. }
  12337. $outstr .= mb_convert_encoding($str, 'UTF-16BE', 'UTF-8');
  12338. return $outstr;
  12339. }
  12340. // ====================================================
  12341. // ====================================================
  12342. /* -- CJK-FONTS -- */
  12343. // from class PDF_Chinese CJK EXTENSIONS
  12344. function AddCIDFont($family, $style, $name, &$cw, $CMap, $registry, $desc)
  12345. {
  12346. $fontkey = strtolower($family) . strtoupper($style);
  12347. if (isset($this->fonts[$fontkey]))
  12348. throw new MpdfException("Font already added: $family $style");
  12349. $i = count($this->fonts) + $this->extraFontSubsets + 1;
  12350. $name = str_replace(' ', '', $name);
  12351. if ($family == 'sjis') {
  12352. $up = -120;
  12353. } else {
  12354. $up = -130;
  12355. }
  12356. // ? 'up' and 'ut' do not seem to be referenced anywhere
  12357. $this->fonts[$fontkey] = array('i' => $i, 'type' => 'Type0', 'name' => $name, 'up' => $up, 'ut' => 40, 'cw' => $cw, 'CMap' => $CMap, 'registry' => $registry, 'MissingWidth' => 1000, 'desc' => $desc);
  12358. }
  12359. function AddCJKFont($family)
  12360. {
  12361. if ($this->PDFA || $this->PDFX) {
  12362. throw new MpdfException("Adobe CJK fonts cannot be embedded in mPDF (required for PDFA1-b and PDFX/1-a).");
  12363. }
  12364. if ($family == 'big5') {
  12365. $this->AddBig5Font();
  12366. } elseif ($family == 'gb') {
  12367. $this->AddGBFont();
  12368. } elseif ($family == 'sjis') {
  12369. $this->AddSJISFont();
  12370. } elseif ($family == 'uhc') {
  12371. $this->AddUHCFont();
  12372. }
  12373. }
  12374. function AddBig5Font()
  12375. {
  12376. //Add Big5 font with proportional Latin
  12377. $family = 'big5';
  12378. $name = 'MSungStd-Light-Acro';
  12379. $cw = $this->Big5_widths;
  12380. $CMap = 'UniCNS-UTF16-H';
  12381. $registry = array('ordering' => 'CNS1', 'supplement' => 4);
  12382. $desc = array(
  12383. 'Ascent' => 880,
  12384. 'Descent' => -120,
  12385. 'CapHeight' => 880,
  12386. 'Flags' => 6,
  12387. 'FontBBox' => '[-160 -249 1015 1071]',
  12388. 'ItalicAngle' => 0,
  12389. 'StemV' => 93,
  12390. );
  12391. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  12392. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  12393. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  12394. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  12395. }
  12396. function AddGBFont()
  12397. {
  12398. //Add GB font with proportional Latin
  12399. $family = 'gb';
  12400. $name = 'STSongStd-Light-Acro';
  12401. $cw = $this->GB_widths;
  12402. $CMap = 'UniGB-UTF16-H';
  12403. $registry = array('ordering' => 'GB1', 'supplement' => 4);
  12404. $desc = array(
  12405. 'Ascent' => 880,
  12406. 'Descent' => -120,
  12407. 'CapHeight' => 737,
  12408. 'Flags' => 6,
  12409. 'FontBBox' => '[-25 -254 1000 880]',
  12410. 'ItalicAngle' => 0,
  12411. 'StemV' => 58,
  12412. 'Style' => '<< /Panose <000000000400000000000000> >>',
  12413. );
  12414. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  12415. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  12416. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  12417. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  12418. }
  12419. function AddSJISFont()
  12420. {
  12421. //Add SJIS font with proportional Latin
  12422. $family = 'sjis';
  12423. $name = 'KozMinPro-Regular-Acro';
  12424. $cw = $this->SJIS_widths;
  12425. $CMap = 'UniJIS-UTF16-H';
  12426. $registry = array('ordering' => 'Japan1', 'supplement' => 5);
  12427. $desc = array(
  12428. 'Ascent' => 880,
  12429. 'Descent' => -120,
  12430. 'CapHeight' => 740,
  12431. 'Flags' => 6,
  12432. 'FontBBox' => '[-195 -272 1110 1075]',
  12433. 'ItalicAngle' => 0,
  12434. 'StemV' => 86,
  12435. 'XHeight' => 502,
  12436. );
  12437. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  12438. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  12439. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  12440. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  12441. }
  12442. function AddUHCFont()
  12443. {
  12444. //Add UHC font with proportional Latin
  12445. $family = 'uhc';
  12446. $name = 'HYSMyeongJoStd-Medium-Acro';
  12447. $cw = $this->UHC_widths;
  12448. $CMap = 'UniKS-UTF16-H';
  12449. $registry = array('ordering' => 'Korea1', 'supplement' => 2);
  12450. $desc = array(
  12451. 'Ascent' => 880,
  12452. 'Descent' => -120,
  12453. 'CapHeight' => 720,
  12454. 'Flags' => 6,
  12455. 'FontBBox' => '[-28 -148 1001 880]',
  12456. 'ItalicAngle' => 0,
  12457. 'StemV' => 60,
  12458. 'Style' => '<< /Panose <000000000600000000000000> >>',
  12459. );
  12460. $this->AddCIDFont($family, '', $name, $cw, $CMap, $registry, $desc);
  12461. $this->AddCIDFont($family, 'B', $name . ',Bold', $cw, $CMap, $registry, $desc);
  12462. $this->AddCIDFont($family, 'I', $name . ',Italic', $cw, $CMap, $registry, $desc);
  12463. $this->AddCIDFont($family, 'BI', $name . ',BoldItalic', $cw, $CMap, $registry, $desc);
  12464. }
  12465. /* -- END CJK-FONTS -- */
  12466. //////////////////////////////////////////////////////////////////////////////
  12467. //////////////////////////////////////////////////////////////////////////////
  12468. //////////////////////////////////////////////////////////////////////////////
  12469. //////////////////////////////////////////////////////////////////////////////
  12470. //////////////////////////////////////////////////////////////////////////////
  12471. //////////////////////////////////////////////////////////////////////////////
  12472. //////////////////////////////////////////////////////////////////////////////
  12473. function SetDefaultFont($font)
  12474. {
  12475. // Disallow embedded fonts to be used as defaults in PDFA
  12476. if ($this->PDFA || $this->PDFX) {
  12477. if (strtolower($font) == 'ctimes') {
  12478. $font = 'serif';
  12479. }
  12480. if (strtolower($font) == 'ccourier') {
  12481. $font = 'monospace';
  12482. }
  12483. if (strtolower($font) == 'chelvetica') {
  12484. $font = 'sans-serif';
  12485. }
  12486. }
  12487. $font = $this->SetFont($font); // returns substituted font if necessary
  12488. $this->default_font = $font;
  12489. $this->original_default_font = $font;
  12490. if (!$this->watermark_font) {
  12491. $this->watermark_font = $font;
  12492. } // *WATERMARK*
  12493. $this->defaultCSS['BODY']['FONT-FAMILY'] = $font;
  12494. $this->cssmgr->CSS['BODY']['FONT-FAMILY'] = $font;
  12495. }
  12496. function SetDefaultFontSize($fontsize)
  12497. {
  12498. $this->default_font_size = $fontsize;
  12499. $this->original_default_font_size = $fontsize;
  12500. $this->SetFontSize($fontsize);
  12501. $this->defaultCSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
  12502. $this->cssmgr->CSS['BODY']['FONT-SIZE'] = $fontsize . 'pt';
  12503. }
  12504. function SetDefaultBodyCSS($prop, $val)
  12505. {
  12506. if ($prop) {
  12507. $this->defaultCSS['BODY'][strtoupper($prop)] = $val;
  12508. $this->cssmgr->CSS['BODY'][strtoupper($prop)] = $val;
  12509. }
  12510. }
  12511. function SetDirectionality($dir = 'ltr')
  12512. {
  12513. /* -- OTL -- */
  12514. if (strtolower($dir) == 'rtl') {
  12515. if ($this->directionality != 'rtl') {
  12516. // Swop L/R Margins so page 1 RTL is an 'even' page
  12517. $tmp = $this->DeflMargin;
  12518. $this->DeflMargin = $this->DefrMargin;
  12519. $this->DefrMargin = $tmp;
  12520. $this->orig_lMargin = $this->DeflMargin;
  12521. $this->orig_rMargin = $this->DefrMargin;
  12522. $this->SetMargins($this->DeflMargin, $this->DefrMargin, $this->tMargin);
  12523. }
  12524. $this->directionality = 'rtl';
  12525. $this->defaultAlign = 'R';
  12526. $this->defaultTableAlign = 'R';
  12527. } else {
  12528. /* -- END OTL -- */
  12529. $this->directionality = 'ltr';
  12530. $this->defaultAlign = 'L';
  12531. $this->defaultTableAlign = 'L';
  12532. } // *OTL*
  12533. $this->cssmgr->CSS['BODY']['DIRECTION'] = $this->directionality;
  12534. }
  12535. // Return either a number (factor) - based on current set fontsize (if % or em) - or exact lineheight (with 'mm' after it)
  12536. function fixLineheight($v)
  12537. {
  12538. $lh = false;
  12539. if (preg_match('/^[0-9\.,]*$/', $v) && $v >= 0) {
  12540. return ($v + 0);
  12541. } elseif (strtoupper($v) == 'NORMAL' || $v == 'N') {
  12542. return 'N'; // mPDF 6
  12543. } else {
  12544. $tlh = $this->ConvertSize($v, $this->FontSize, $this->FontSize, true);
  12545. if ($tlh) {
  12546. return ($tlh . 'mm');
  12547. }
  12548. }
  12549. return $this->normalLineheight;
  12550. }
  12551. function _getNormalLineheight($desc = false)
  12552. {
  12553. if (!$desc) {
  12554. $desc = $this->CurrentFont['desc'];
  12555. }
  12556. if (!isset($desc['Leading'])) {
  12557. $desc['Leading'] = 0;
  12558. }
  12559. if ($this->useFixedNormalLineHeight) {
  12560. $lh = $this->normalLineheight;
  12561. } elseif (isset($desc['Ascent']) && $desc['Ascent']) {
  12562. $lh = ($this->adjustFontDescLineheight * ($desc['Ascent'] - $desc['Descent'] + $desc['Leading']) / 1000);
  12563. } else {
  12564. $lh = $this->normalLineheight;
  12565. }
  12566. return $lh;
  12567. }
  12568. // Set a (fixed) lineheight to an actual value - either to named fontsize(pts) or default
  12569. function SetLineHeight($FontPt = '', $lh = '')
  12570. {
  12571. if (!$FontPt) {
  12572. $FontPt = $this->FontSizePt;
  12573. }
  12574. $fs = $FontPt / _MPDFK;
  12575. $this->lineheight = $this->_computeLineheight($lh, $fs);
  12576. }
  12577. function _computeLineheight($lh, $fs = '')
  12578. {
  12579. if ($this->shrin_k > 1) {
  12580. $k = $this->shrin_k;
  12581. } else {
  12582. $k = 1;
  12583. }
  12584. if (!$fs) {
  12585. $fs = $this->FontSize;
  12586. }
  12587. if ($lh == 'N') {
  12588. $lh = $this->_getNormalLineheight();
  12589. }
  12590. if (preg_match('/mm/', $lh)) {
  12591. return (($lh + 0.0) / $k); // convert to number
  12592. } elseif ($lh > 0) {
  12593. return ($fs * $lh);
  12594. }
  12595. return ($fs * $this->normalLineheight);
  12596. }
  12597. function _setLineYpos(&$fontsize, &$fontdesc, &$CSSlineheight, $blockYpos = false)
  12598. {
  12599. $ypos['glyphYorigin'] = 0;
  12600. $ypos['baseline-shift'] = 0;
  12601. $linegap = 0;
  12602. $leading = 0;
  12603. if (isset($fontdesc['Ascent']) && $fontdesc['Ascent'] && !$this->useFixedTextBaseline) {
  12604. // Fontsize uses font metrics - this method seems to produce results compatible with browsers (except IE9)
  12605. $ypos['boxtop'] = $fontdesc['Ascent'] / 1000 * $fontsize;
  12606. $ypos['boxbottom'] = $fontdesc['Descent'] / 1000 * $fontsize;
  12607. if (isset($fontdesc['Leading'])) {
  12608. $linegap = $fontdesc['Leading'] / 1000 * $fontsize;
  12609. }
  12610. }
  12611. // Default if not set - uses baselineC
  12612. else {
  12613. $ypos['boxtop'] = (0.5 + $this->baselineC) * $fontsize;
  12614. $ypos['boxbottom'] = -(0.5 - $this->baselineC) * $fontsize;
  12615. }
  12616. $fontheight = $ypos['boxtop'] - $ypos['boxbottom'];
  12617. if ($this->shrin_k > 1) {
  12618. $shrin_k = $this->shrin_k;
  12619. } else {
  12620. $shrin_k = 1;
  12621. }
  12622. $leading = 0;
  12623. if ($CSSlineheight == 'N') {
  12624. $lh = $this->_getNormalLineheight($fontdesc);
  12625. $lineheight = ($fontsize * $lh);
  12626. $leading += $linegap; // specified in hhea or sTypo in OpenType tables ****************************************
  12627. } elseif (preg_match('/mm/', $CSSlineheight)) {
  12628. $lineheight = (($CSSlineheight + 0.0) / $shrin_k);
  12629. } // convert to number
  12630. // ??? If lineheight is a factor e.g. 1.3 ?? use factor x 1em or ? use 'normal' lineheight * factor ******************************
  12631. // Could depend on value for $text_height - a draft CSS value as set above for now
  12632. elseif ($CSSlineheight > 0) {
  12633. $lineheight = ($fontsize * $CSSlineheight);
  12634. } else {
  12635. $lineheight = ($fontsize * $this->normalLineheight);
  12636. }
  12637. // In general, calculate the "leading" - the difference between the fontheight and the lineheight
  12638. // and add half to the top and half to the bottom. BUT
  12639. // If an inline element has a font-size less than the block element, and the line-height is set as an em or % value
  12640. // it will add too much leading below the font and expand the height of the line - so just use the block element exttop/extbottom:
  12641. if (preg_match('/mm/', $CSSlineheight) && $ypos['boxtop'] < $blockYpos['boxtop'] && $ypos['boxbottom'] > $blockYpos['boxbottom']) {
  12642. $ypos['exttop'] = $blockYpos['exttop'];
  12643. $ypos['extbottom'] = $blockYpos['extbottom'];
  12644. } else {
  12645. $leading += ($lineheight - $fontheight);
  12646. $ypos['exttop'] = $ypos['boxtop'] + $leading / 2;
  12647. $ypos['extbottom'] = $ypos['boxbottom'] - $leading / 2;
  12648. }
  12649. // TEMP ONLY FOR DEBUGGING *********************************
  12650. //$ypos['lineheight'] = $lineheight;
  12651. //$ypos['fontheight'] = $fontheight;
  12652. //$ypos['leading'] = $leading;
  12653. return $ypos;
  12654. }
  12655. /* Called from WriteFlowingBlock() and finishFlowingBlock()
  12656. Determines the line hieght and glyph/writing position
  12657. for each element in the line to be written */
  12658. function _setInlineBlockHeights(&$lineBox, &$stackHeight, &$content, &$font, $is_table)
  12659. {
  12660. if ($this->shrin_k > 1) {
  12661. $shrin_k = $this->shrin_k;
  12662. } else {
  12663. $shrin_k = 1;
  12664. }
  12665. $ypos = array();
  12666. $bordypos = array();
  12667. $bgypos = array();
  12668. if ($is_table) {
  12669. // FOR TABLE
  12670. $fontsize = $this->FontSize;
  12671. $fontkey = $this->FontFamily . $this->FontStyle;
  12672. $fontdesc = $this->fonts[$fontkey]['desc'];
  12673. $CSSlineheight = $this->cellLineHeight;
  12674. $line_stacking_strategy = $this->cellLineStackingStrategy; // inline-line-height [default] | block-line-height | max-height | grid-height
  12675. $line_stacking_shift = $this->cellLineStackingShift; // consider-shifts [default] | disregard-shifts
  12676. } else {
  12677. // FOR BLOCK FONT
  12678. $fontsize = $this->blk[$this->blklvl]['InlineProperties']['size'];
  12679. $fontkey = $this->blk[$this->blklvl]['InlineProperties']['family'] . $this->blk[$this->blklvl]['InlineProperties']['style'];
  12680. $fontdesc = $this->fonts[$fontkey]['desc'];
  12681. $CSSlineheight = $this->blk[$this->blklvl]['line_height'];
  12682. // inline-line-height | block-line-height | max-height | grid-height
  12683. $line_stacking_strategy = (isset($this->blk[$this->blklvl]['line_stacking_strategy']) ? $this->blk[$this->blklvl]['line_stacking_strategy'] : 'inline-line-height');
  12684. // consider-shifts | disregard-shifts
  12685. $line_stacking_shift = (isset($this->blk[$this->blklvl]['line_stacking_shift']) ? $this->blk[$this->blklvl]['line_stacking_shift'] : 'consider-shifts');
  12686. }
  12687. $boxLineHeight = $this->_computeLineheight($CSSlineheight, $fontsize);
  12688. // First, set a "strut" using block font at index $lineBox[-1]
  12689. $ypos[-1] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight);
  12690. // for the block element - always taking the block EXTENDED progression including leading - which may be negative
  12691. if ($line_stacking_strategy == 'block-line-height') {
  12692. $topy = $ypos[-1]['exttop'];
  12693. $bottomy = $ypos[-1]['extbottom'];
  12694. } else {
  12695. $topy = 0;
  12696. $bottomy = 0;
  12697. }
  12698. // Get text-middle for aligning images/objects
  12699. $midpoint = $ypos[-1]['boxtop'] - (($ypos[-1]['boxtop'] - $ypos[-1]['boxbottom']) / 2);
  12700. // for images / inline objects / replaced elements
  12701. $mta = 0; // Maximum top-aligned
  12702. $mba = 0; // Maximum bottom-aligned
  12703. foreach ($content as $k => $chunk) {
  12704. if (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'listmarker') {
  12705. $ypos[$k] = $ypos[-1];
  12706. // UPDATE Maximums
  12707. if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements
  12708. if ($ypos[$k]['boxtop'] > $topy)
  12709. $topy = $ypos[$k]['boxtop'];
  12710. if ($ypos[$k]['boxbottom'] < $bottomy)
  12711. $bottomy = $ypos[$k]['boxbottom'];
  12712. }
  12713. else {
  12714. if ($ypos[$k]['exttop'] > $topy)
  12715. $topy = $ypos[$k]['exttop'];
  12716. if ($ypos[$k]['extbottom'] < $bottomy)
  12717. $bottomy = $ypos[$k]['extbottom'];
  12718. }
  12719. }
  12720. elseif (isset($this->objectbuffer[$k]) && $this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  12721. $fontsize = $font[$k]['size'];
  12722. $fontdesc = $font[$k]['curr']['desc'];
  12723. $lh = 1;
  12724. $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $lh, $ypos[-1]); // Lineheight=1 fixed
  12725. } elseif (isset($this->objectbuffer[$k])) {
  12726. $oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
  12727. $va = $this->objectbuffer[$k]['vertical-align'];
  12728. if ($va == 'BS') { // (BASELINE default)
  12729. if ($oh > $topy)
  12730. $topy = $oh;
  12731. }
  12732. elseif ($va == 'M') {
  12733. if (($midpoint + $oh / 2) > $topy)
  12734. $topy = $midpoint + $oh / 2;
  12735. if (($midpoint - $oh / 2) < $bottomy)
  12736. $bottomy = $midpoint - $oh / 2;
  12737. }
  12738. elseif ($va == 'TT') {
  12739. if (($ypos[-1]['boxtop'] - $oh) < $bottomy) {
  12740. $bottomy = $ypos[-1]['boxtop'] - $oh;
  12741. $topy = max($topy, $ypos[-1]['boxtop']);
  12742. }
  12743. } elseif ($va == 'TB') {
  12744. if (($ypos[-1]['boxbottom'] + $oh) > $topy) {
  12745. $topy = $ypos[-1]['boxbottom'] + $oh;
  12746. $bottomy = min($bottomy, $ypos[-1]['boxbottom']);
  12747. }
  12748. } elseif ($va == 'T') {
  12749. if ($oh > $mta)
  12750. $mta = $oh;
  12751. }
  12752. elseif ($va == 'B') {
  12753. if ($oh > $mba)
  12754. $mba = $oh;
  12755. }
  12756. }
  12757. elseif ($content[$k] || $content[$k] === '0') {
  12758. // FOR FLOWING BLOCK
  12759. $fontsize = $font[$k]['size'];
  12760. $fontdesc = $font[$k]['curr']['desc'];
  12761. // In future could set CSS line-height from inline elements; for now, use block level:
  12762. $ypos[$k] = $this->_setLineYpos($fontsize, $fontdesc, $CSSlineheight, $ypos[-1]);
  12763. if (isset($font[$k]['textparam']['text-baseline']) && $font[$k]['textparam']['text-baseline'] != 0) {
  12764. $ypos[$k]['baseline-shift'] = $font[$k]['textparam']['text-baseline'];
  12765. }
  12766. // DO ALIGNMENT FOR BASELINES *******************
  12767. // Until most fonts have OpenType BASE tables, this won't work
  12768. // $ypos[$k] compared to $ypos[-1] or $ypos[$k-1] using $dominant_baseline and $baseline_table
  12769. // UPDATE Maximums
  12770. if ($line_stacking_strategy == 'block-line-height' || $line_stacking_strategy == 'grid-height' || $line_stacking_strategy == 'max-height') { // don't include extended block progression of all inline elements
  12771. if ($line_stacking_shift == 'disregard-shifts') {
  12772. if ($ypos[$k]['boxtop'] > $topy)
  12773. $topy = $ypos[$k]['boxtop'];
  12774. if ($ypos[$k]['boxbottom'] < $bottomy)
  12775. $bottomy = $ypos[$k]['boxbottom'];
  12776. }
  12777. else {
  12778. if (($ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift']) > $topy)
  12779. $topy = $ypos[$k]['boxtop'] + $ypos[$k]['baseline-shift'];
  12780. if (($ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift']) < $bottomy)
  12781. $bottomy = $ypos[$k]['boxbottom'] + $ypos[$k]['baseline-shift'];
  12782. }
  12783. }
  12784. else {
  12785. if ($line_stacking_shift == 'disregard-shifts') {
  12786. if ($ypos[$k]['exttop'] > $topy)
  12787. $topy = $ypos[$k]['exttop'];
  12788. if ($ypos[$k]['extbottom'] < $bottomy)
  12789. $bottomy = $ypos[$k]['extbottom'];
  12790. }
  12791. else {
  12792. if (($ypos[$k]['exttop'] + $ypos[$k]['baseline-shift']) > $topy)
  12793. $topy = $ypos[$k]['exttop'] + $ypos[$k]['baseline-shift'];
  12794. if (($ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift']) < $bottomy)
  12795. $bottomy = $ypos[$k]['extbottom'] + $ypos[$k]['baseline-shift'];
  12796. }
  12797. }
  12798. // If BORDER set on inline element
  12799. if (isset($font[$k]['bord']) && $font[$k]['bord']) {
  12800. $bordfontsize = $font[$k]['textparam']['bord-decoration']['fontsize'] / $shrin_k;
  12801. $bordfontkey = $font[$k]['textparam']['bord-decoration']['fontkey'];
  12802. if ($bordfontkey != $fontkey || $bordfontsize != $fontsize || isset($font[$k]['textparam']['bord-decoration']['baseline'])) {
  12803. $bordfontdesc = $this->fonts[$bordfontkey]['desc'];
  12804. $bordypos[$k] = $this->_setLineYpos($bordfontsize, $bordfontdesc, $CSSlineheight, $ypos[-1]);
  12805. if (isset($font[$k]['textparam']['bord-decoration']['baseline']) && $font[$k]['textparam']['bord-decoration']['baseline'] != 0) {
  12806. $bordypos[$k]['baseline-shift'] = $font[$k]['textparam']['bord-decoration']['baseline'] / $shrin_k;
  12807. }
  12808. }
  12809. }
  12810. // If BACKGROUND set on inline element
  12811. if (isset($font[$k]['spanbgcolor']) && $font[$k]['spanbgcolor']) {
  12812. $bgfontsize = $font[$k]['textparam']['bg-decoration']['fontsize'] / $shrin_k;
  12813. $bgfontkey = $font[$k]['textparam']['bg-decoration']['fontkey'];
  12814. if ($bgfontkey != $fontkey || $bgfontsize != $fontsize || isset($font[$k]['textparam']['bg-decoration']['baseline'])) {
  12815. $bgfontdesc = $this->fonts[$bgfontkey]['desc'];
  12816. $bgypos[$k] = $this->_setLineYpos($bgfontsize, $bgfontdesc, $CSSlineheight, $ypos[-1]);
  12817. if (isset($font[$k]['textparam']['bg-decoration']['baseline']) && $font[$k]['textparam']['bg-decoration']['baseline'] != 0) {
  12818. $bgypos[$k]['baseline-shift'] = $font[$k]['textparam']['bg-decoration']['baseline'] / $shrin_k;
  12819. }
  12820. }
  12821. }
  12822. }
  12823. }
  12824. // TOP or BOTTOM aligned images
  12825. if ($mta > ($topy - $bottomy)) {
  12826. if (($topy - $mta) < $bottomy)
  12827. $bottomy = $topy - $mta;
  12828. }
  12829. if ($mba > ($topy - $bottomy)) {
  12830. if (($bottomy + $mba) > $topy)
  12831. $topy = $bottomy + $mba;
  12832. }
  12833. if ($line_stacking_strategy == 'block-line-height') { // fixed height set by block element (whether present or not)
  12834. $topy = $ypos[-1]['exttop'];
  12835. $bottomy = $ypos[-1]['extbottom'];
  12836. }
  12837. $inclusiveHeight = $topy - $bottomy;
  12838. // SET $stackHeight taking note of line_stacking_strategy
  12839. // NB inclusive height already takes account of need to consider block progression height (excludes leading set by lineheight)
  12840. // or extended block progression height (includes leading set by lineheight)
  12841. if ($line_stacking_strategy == 'block-line-height') { // fixed = extended block progression height of block element
  12842. $stackHeight = $boxLineHeight;
  12843. } elseif ($line_stacking_strategy == 'max-height') { // smallest height which includes extended block progression height of block element
  12844. // and block progression heights of inline elements (NOT extended)
  12845. $stackHeight = $inclusiveHeight;
  12846. } elseif ($line_stacking_strategy == 'grid-height') { // smallest multiple of block element lineheight to include
  12847. // block progression heights of inline elements (NOT extended)
  12848. $stackHeight = $boxLineHeight;
  12849. while ($stackHeight < $inclusiveHeight) {
  12850. $stackHeight += $boxLineHeight;
  12851. }
  12852. } else { // 'inline-line-height' = default // smallest height which includes extended block progression height of block element
  12853. // AND extended block progression heights of inline elements
  12854. $stackHeight = $inclusiveHeight;
  12855. }
  12856. $diff = $stackHeight - $inclusiveHeight;
  12857. $topy += $diff / 2;
  12858. $bottomy -= $diff / 2;
  12859. // ADJUST $ypos => lineBox using $stackHeight; lineBox are all offsets from the top of stackHeight in mm
  12860. // and SET IMAGE OFFSETS
  12861. $lineBox[-1]['boxtop'] = $topy - $ypos[-1]['boxtop'];
  12862. $lineBox[-1]['boxbottom'] = $topy - $ypos[-1]['boxbottom'];
  12863. // $lineBox[-1]['exttop'] = $topy - $ypos[-1]['exttop'];
  12864. // $lineBox[-1]['extbottom'] = $topy - $ypos[-1]['extbottom'];
  12865. $lineBox[-1]['glyphYorigin'] = $topy - $ypos[-1]['glyphYorigin'];
  12866. $lineBox[-1]['baseline-shift'] = $ypos[-1]['baseline-shift'];
  12867. $midpoint = $lineBox[-1]['boxbottom'] - (($lineBox[-1]['boxbottom'] - $lineBox[-1]['boxtop']) / 2);
  12868. foreach ($content as $k => $chunk) {
  12869. if (isset($this->objectbuffer[$k])) {
  12870. $oh = $this->objectbuffer[$k]['OUTER-HEIGHT'];
  12871. // LIST MARKERS
  12872. if ($this->objectbuffer[$k]['type'] == 'listmarker') {
  12873. $oh = $fontsize;
  12874. } elseif ($this->objectbuffer[$k]['type'] == 'dottab') { // mPDF 6 DOTTAB
  12875. $oh = $font[$k]['size']; // == $this->objectbuffer[$k]['fontsize']/_MPDFK;
  12876. $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
  12877. $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
  12878. $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
  12879. $lineBox[$k]['baseline-shift'] = 0;
  12880. // continue;
  12881. }
  12882. $va = $this->objectbuffer[$k]['vertical-align']; // = $objattr['vertical-align'] = set as M,T,B,S
  12883. if ($va == 'BS') { // (BASELINE default)
  12884. $lineBox[$k]['top'] = $lineBox[-1]['glyphYorigin'] - $oh;
  12885. } elseif ($va == 'M') {
  12886. $lineBox[$k]['top'] = $midpoint - $oh / 2;
  12887. } elseif ($va == 'TT') {
  12888. $lineBox[$k]['top'] = $lineBox[-1]['boxtop'];
  12889. } elseif ($va == 'TB') {
  12890. $lineBox[$k]['top'] = $lineBox[-1]['boxbottom'] - $oh;
  12891. } elseif ($va == 'T') {
  12892. $lineBox[$k]['top'] = 0;
  12893. } elseif ($va == 'B') {
  12894. $lineBox[$k]['top'] = $stackHeight - $oh;
  12895. }
  12896. } elseif ($content[$k] || $content[$k] === '0') {
  12897. $lineBox[$k]['boxtop'] = $topy - $ypos[$k]['boxtop'];
  12898. $lineBox[$k]['boxbottom'] = $topy - $ypos[$k]['boxbottom'];
  12899. // $lineBox[$k]['exttop'] = $topy - $ypos[$k]['exttop'];
  12900. // $lineBox[$k]['extbottom'] = $topy - $ypos[$k]['extbottom'];
  12901. $lineBox[$k]['glyphYorigin'] = $topy - $ypos[$k]['glyphYorigin'];
  12902. $lineBox[$k]['baseline-shift'] = $ypos[$k]['baseline-shift'];
  12903. if (isset($bordypos[$k]['boxtop'])) {
  12904. $lineBox[$k]['border-boxtop'] = $topy - $bordypos[$k]['boxtop'];
  12905. $lineBox[$k]['border-boxbottom'] = $topy - $bordypos[$k]['boxbottom'];
  12906. $lineBox[$k]['border-baseline-shift'] = $bordypos[$k]['baseline-shift'];
  12907. }
  12908. if (isset($bgypos[$k]['boxtop'])) {
  12909. $lineBox[$k]['background-boxtop'] = $topy - $bgypos[$k]['boxtop'];
  12910. $lineBox[$k]['background-boxbottom'] = $topy - $bgypos[$k]['boxbottom'];
  12911. $lineBox[$k]['background-baseline-shift'] = $bgypos[$k]['baseline-shift'];
  12912. }
  12913. }
  12914. }
  12915. }
  12916. function SetBasePath($str = '')
  12917. {
  12918. if (isset($_SERVER['HTTP_HOST'])) {
  12919. $host = $_SERVER['HTTP_HOST'];
  12920. } elseif (isset($_SERVER['SERVER_NAME'])) {
  12921. $host = $_SERVER['SERVER_NAME'];
  12922. } else {
  12923. $host = '';
  12924. }
  12925. if (!$str) {
  12926. if ($_SERVER['SCRIPT_NAME']) {
  12927. $currentPath = dirname($_SERVER['SCRIPT_NAME']);
  12928. } else {
  12929. $currentPath = dirname($_SERVER['PHP_SELF']);
  12930. }
  12931. $currentPath = str_replace("\\", "/", $currentPath);
  12932. if ($currentPath == '/') {
  12933. $currentPath = '';
  12934. }
  12935. if ($host) { // mPDF 6
  12936. if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) {
  12937. $currpath = 'https://' . $host . $currentPath . '/';
  12938. } else {
  12939. $currpath = 'http://' . $host . $currentPath . '/';
  12940. }
  12941. } else {
  12942. $currpath = '';
  12943. }
  12944. $this->basepath = $currpath;
  12945. $this->basepathIsLocal = true;
  12946. return;
  12947. }
  12948. $str = preg_replace('/\?.*/', '', $str);
  12949. if (!preg_match('/(http|https|ftp):\/\/.*\//i', $str)) {
  12950. $str .= '/';
  12951. }
  12952. $str .= 'xxx'; // in case $str ends in / e.g. http://www.bbc.co.uk/
  12953. $this->basepath = dirname($str) . "/"; // returns e.g. e.g. http://www.google.com/dir1/dir2/dir3/
  12954. $this->basepath = str_replace("\\", "/", $this->basepath); //If on Windows
  12955. $tr = parse_url($this->basepath);
  12956. if (isset($tr['host']) && ($tr['host'] == $host)) {
  12957. $this->basepathIsLocal = true;
  12958. } else {
  12959. $this->basepathIsLocal = false;
  12960. }
  12961. }
  12962. function GetFullPath(&$path, $basepath = '')
  12963. {
  12964. // When parsing CSS need to pass temporary basepath - so links are relative to current stylesheet
  12965. if (!$basepath) {
  12966. $basepath = $this->basepath;
  12967. }
  12968. //Fix path value
  12969. $path = str_replace("\\", "/", $path); //If on Windows
  12970. // mPDF 5.7.2
  12971. if (substr($path, 0, 2) == "//") {
  12972. $tr = parse_url($basepath);
  12973. $path = $tr['scheme'] . ':' . $path; // mPDF 6
  12974. }
  12975. $regexp = '|^./|'; // Inadvertently corrects "./path/etc" and "//www.domain.com/etc"
  12976. $path = preg_replace($regexp, '', $path);
  12977. if (substr($path, 0, 1) == '#') {
  12978. return;
  12979. }
  12980. if (preg_match('@^(mailto|tel|fax):.*@i', $path)) {
  12981. return;
  12982. }
  12983. if (substr($path, 0, 3) == "../") { //It is a Relative Link
  12984. $backtrackamount = substr_count($path, "../");
  12985. $maxbacktrack = substr_count($basepath, "/") - 3;
  12986. $filepath = str_replace("../", '', $path);
  12987. $path = $basepath;
  12988. //If it is an invalid relative link, then make it go to directory root
  12989. if ($backtrackamount > $maxbacktrack)
  12990. $backtrackamount = $maxbacktrack;
  12991. //Backtrack some directories
  12992. for ($i = 0; $i < $backtrackamount + 1; $i++)
  12993. $path = substr($path, 0, strrpos($path, "/"));
  12994. $path = $path . "/" . $filepath; //Make it an absolute path
  12995. }
  12996. elseif (strpos($path, ":/") === false || strpos($path, ":/") > 10) { //It is a Local Link
  12997. if (substr($path, 0, 1) == "/") {
  12998. $tr = parse_url($basepath);
  12999. // mPDF 5.7.2
  13000. $root = '';
  13001. if (!empty($tr['scheme'])) {
  13002. $root .= $tr['scheme'] . '://';
  13003. }
  13004. $root .= isset($tr['host']) ? $tr['host'] : '';
  13005. $root .= ((isset($tr['port']) && $tr['port']) ? (':' . $tr['port']) : ''); // mPDF 5.7.3
  13006. $path = $root . $path;
  13007. } else {
  13008. $path = $basepath . $path;
  13009. }
  13010. }
  13011. //Do nothing if it is an Absolute Link
  13012. }
  13013. // Used for external CSS files
  13014. function _get_file($path)
  13015. {
  13016. // If local file try using local path (? quicker, but also allowed even if allow_url_fopen false)
  13017. $contents = '';
  13018. // mPDF 5.7.3
  13019. if (strpos($path, "//") === false) {
  13020. $path = preg_replace('/\.css\?.*$/', '.css', $path);
  13021. }
  13022. $contents = @file_get_contents($path);
  13023. if ($contents) {
  13024. return $contents;
  13025. }
  13026. if ($this->basepathIsLocal) {
  13027. $tr = parse_url($path);
  13028. $lp = getenv("SCRIPT_NAME");
  13029. $ap = realpath($lp);
  13030. $ap = str_replace("\\", "/", $ap);
  13031. $docroot = substr($ap, 0, strpos($ap, $lp));
  13032. // WriteHTML parses all paths to full URLs; may be local file name
  13033. if ($tr['scheme'] && $tr['host'] && $_SERVER["DOCUMENT_ROOT"]) {
  13034. $localpath = $_SERVER["DOCUMENT_ROOT"] . $tr['path'];
  13035. }
  13036. // DOCUMENT_ROOT is not returned on IIS
  13037. elseif ($docroot) {
  13038. $localpath = $docroot . $tr['path'];
  13039. } else {
  13040. $localpath = $path;
  13041. }
  13042. $contents = @file_get_contents($localpath);
  13043. }
  13044. // if not use full URL
  13045. elseif (!$contents && !ini_get('allow_url_fopen') && function_exists("curl_init")) {
  13046. $ch = curl_init($path);
  13047. curl_setopt($ch, CURLOPT_HEADER, 0);
  13048. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  13049. $contents = curl_exec($ch);
  13050. curl_close($ch);
  13051. }
  13052. return $contents;
  13053. }
  13054. function docPageNum($num = 0, $extras = false)
  13055. {
  13056. if ($num < 1) {
  13057. $num = $this->page;
  13058. }
  13059. $type = $this->defaultPageNumStyle; // set default Page Number Style
  13060. $ppgno = $num;
  13061. $suppress = 0;
  13062. $offset = 0;
  13063. $lastreset = 0;
  13064. foreach ($this->PageNumSubstitutions AS $psarr) {
  13065. if ($num >= $psarr['from']) {
  13066. if ($psarr['reset']) {
  13067. if ($psarr['reset'] > 1) {
  13068. $offset = $psarr['reset'] - 1;
  13069. }
  13070. $ppgno = $num - $psarr['from'] + 1 + $offset;
  13071. $lastreset = $psarr['from'];
  13072. }
  13073. if ($psarr['type']) {
  13074. $type = $psarr['type'];
  13075. }
  13076. if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
  13077. $suppress = 1;
  13078. } elseif (strtoupper($psarr['suppress']) == 'OFF') {
  13079. $suppress = 0;
  13080. }
  13081. }
  13082. }
  13083. if ($suppress) {
  13084. return '';
  13085. }
  13086. $ppgno = $this->_getStyledNumber($ppgno, $type);
  13087. if ($extras) {
  13088. $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
  13089. }
  13090. return $ppgno;
  13091. }
  13092. function docPageNumTotal($num = 0, $extras = false)
  13093. {
  13094. if ($num < 1) {
  13095. $num = $this->page;
  13096. }
  13097. $type = $this->defaultPageNumStyle; // set default Page Number Style
  13098. $ppgstart = 1;
  13099. $ppgend = count($this->pages) + 1;
  13100. $suppress = 0;
  13101. $offset = 0;
  13102. foreach ($this->PageNumSubstitutions AS $psarr) {
  13103. if ($num >= $psarr['from']) {
  13104. if ($psarr['reset']) {
  13105. if ($psarr['reset'] > 1) {
  13106. $offset = $psarr['reset'] - 1;
  13107. }
  13108. $ppgstart = $psarr['from'] + $offset;
  13109. $ppgend = count($this->pages) + 1 + $offset;
  13110. }
  13111. if ($psarr['type']) {
  13112. $type = $psarr['type'];
  13113. }
  13114. if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
  13115. $suppress = 1;
  13116. } elseif (strtoupper($psarr['suppress']) == 'OFF') {
  13117. $suppress = 0;
  13118. }
  13119. }
  13120. if ($num < $psarr['from']) {
  13121. if ($psarr['reset']) {
  13122. $ppgend = $psarr['from'] + $offset;
  13123. break;
  13124. }
  13125. }
  13126. }
  13127. if ($suppress) {
  13128. return '';
  13129. }
  13130. $ppgno = $ppgend - $ppgstart + $offset;
  13131. $ppgno = $this->_getStyledNumber($ppgno, $type);
  13132. if ($extras) {
  13133. $ppgno = $this->pagenumPrefix . $ppgno . $this->pagenumSuffix;
  13134. }
  13135. return $ppgno;
  13136. }
  13137. // mPDF 6
  13138. function _getStyledNumber($ppgno, $type, $listmarker = false)
  13139. {
  13140. if ($listmarker) {
  13141. $reverse = true; // Reverse RTL numerals (Hebrew) when using for list
  13142. $checkfont = true; // Using list - font is set, so check if character is available
  13143. } else {
  13144. $reverse = false; // For pagenumbers, RTL numerals (Hebrew) will get reversed later by bidi
  13145. $checkfont = false; // For pagenumbers - font is not set, so no check
  13146. }
  13147. $lowertype = strtolower($type);
  13148. if ($lowertype == 'upper-latin' || $lowertype == 'upper-alpha' || $type == 'A') {
  13149. $ppgno = $this->dec2alpha($ppgno, true);
  13150. } elseif ($lowertype == 'lower-latin' || $lowertype == 'lower-alpha' || $type == 'a') {
  13151. $ppgno = $this->dec2alpha($ppgno, false);
  13152. } elseif ($lowertype == 'upper-roman' || $type == 'I') {
  13153. $ppgno = $this->dec2roman($ppgno, true);
  13154. } elseif ($lowertype == 'lower-roman' || $type == 'i') {
  13155. $ppgno = $this->dec2roman($ppgno, false);
  13156. } elseif ($lowertype == 'hebrew') {
  13157. $ppgno = $this->dec2hebrew($ppgno, $reverse);
  13158. } elseif (preg_match('/(arabic-indic|bengali|devanagari|gujarati|gurmukhi|kannada|malayalam|oriya|persian|tamil|telugu|thai|urdu|cambodian|khmer|lao)/i', $lowertype, $m)) {
  13159. switch ($m[1]) { //Format type
  13160. case 'arabic-indic': $cp = 0x0660;
  13161. break;
  13162. case 'persian':
  13163. case 'urdu': $cp = 0x06F0;
  13164. break;
  13165. case 'bengali': $cp = 0x09E6;
  13166. break;
  13167. case 'devanagari': $cp = 0x0966;
  13168. break;
  13169. case 'gujarati': $cp = 0x0AE6;
  13170. break;
  13171. case 'gurmukhi': $cp = 0x0A66;
  13172. break;
  13173. case 'kannada': $cp = 0x0CE6;
  13174. break;
  13175. case 'malayalam': $cp = 0x0D66;
  13176. break;
  13177. case 'oriya': $cp = 0x0B66;
  13178. break;
  13179. case 'telugu': $cp = 0x0C66;
  13180. break;
  13181. case 'tamil': $cp = 0x0BE6;
  13182. break;
  13183. case 'thai': $cp = 0x0E50;
  13184. break;
  13185. case 'khmer':
  13186. case 'cambodian': $cp = 0x17E0;
  13187. break;
  13188. case 'lao': $cp = 0x0ED0;
  13189. break;
  13190. }
  13191. $ppgno = $this->dec2other($ppgno, $cp, $checkfont);
  13192. } elseif ($lowertype == 'cjk-decimal') {
  13193. $ppgno = $this->dec2cjk($ppgno);
  13194. }
  13195. return $ppgno;
  13196. }
  13197. function docPageSettings($num = 0)
  13198. {
  13199. // Returns current type (numberstyle), suppression state for this page number;
  13200. // reset is only returned if set for this page number
  13201. if ($num < 1) {
  13202. $num = $this->page;
  13203. }
  13204. $type = $this->defaultPageNumStyle; // set default Page Number Style
  13205. $ppgno = $num;
  13206. $suppress = 0;
  13207. $offset = 0;
  13208. $reset = '';
  13209. foreach ($this->PageNumSubstitutions AS $psarr) {
  13210. if ($num >= $psarr['from']) {
  13211. if ($psarr['reset']) {
  13212. if ($psarr['reset'] > 1) {
  13213. $offset = $psarr['reset'] - 1;
  13214. }
  13215. $ppgno = $num - $psarr['from'] + 1 + $offset;
  13216. }
  13217. if ($psarr['type']) {
  13218. $type = $psarr['type'];
  13219. }
  13220. if (strtoupper($psarr['suppress']) == 'ON' || $psarr['suppress'] == 1) {
  13221. $suppress = 1;
  13222. } elseif (strtoupper($psarr['suppress']) == 'OFF') {
  13223. $suppress = 0;
  13224. }
  13225. }
  13226. if ($num == $psarr['from']) {
  13227. $reset = $psarr['reset'];
  13228. }
  13229. }
  13230. if ($suppress) {
  13231. $suppress = 'on';
  13232. } else {
  13233. $suppress = 'off';
  13234. }
  13235. return array($type, $suppress, $reset);
  13236. }
  13237. function RestartDocTemplate()
  13238. {
  13239. $this->docTemplateStart = $this->page;
  13240. }
  13241. //Page header
  13242. function Header($content = '')
  13243. {
  13244. $this->cMarginL = 0;
  13245. $this->cMarginR = 0;
  13246. if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLHeaderE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLHeader) || (!$this->mirrorMargins && $this->HTMLHeader)) {
  13247. $this->writeHTMLHeaders();
  13248. return;
  13249. }
  13250. }
  13251. /* -- TABLES -- */
  13252. function TableHeaderFooter($content = '', $tablestartpage = '', $tablestartcolumn = '', $horf = 'H', $level, $firstSpread = true, $finalSpread = true)
  13253. {
  13254. if (($horf == 'H' || $horf == 'F') && !empty($content)) { // mPDF 5.7.2
  13255. $table = &$this->table[1][1];
  13256. // mPDF 5.7.2
  13257. if ($horf == 'F') { // Table Footer
  13258. $firstrow = count($table['cells']) - $table['footernrows'];
  13259. $lastrow = count($table['cells']) - 1;
  13260. } else { // Table Header
  13261. $firstrow = 0;
  13262. $lastrow = $table['headernrows'] - 1;
  13263. }
  13264. if (empty($content[$firstrow])) {
  13265. if ($this->debug) {
  13266. throw new MpdfException("&lt;tfoot&gt; must precede &lt;tbody&gt; in a table");
  13267. } else {
  13268. return;
  13269. }
  13270. }
  13271. // Advance down page by half width of top border
  13272. if ($horf == 'H') { // Only if header
  13273. if ($table['borders_separate']) {
  13274. $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
  13275. } else {
  13276. $adv = $table['max_cell_border_width']['T'] / 2;
  13277. }
  13278. if ($adv) {
  13279. if ($this->table_rotate) {
  13280. $this->y += ($adv);
  13281. } else {
  13282. $this->DivLn($adv, $this->blklvl, true);
  13283. }
  13284. }
  13285. }
  13286. $topy = $content[$firstrow][0]['y'] - $this->y;
  13287. for ($i = $firstrow; $i <= $lastrow; $i++) {
  13288. $y = $this->y;
  13289. /* -- COLUMNS -- */
  13290. // If outside columns, this is done in PaintDivBB
  13291. if ($this->ColActive) {
  13292. //OUTER FILL BGCOLOR of DIVS
  13293. if ($this->blklvl > 0) {
  13294. $firstblockfill = $this->GetFirstBlockFill();
  13295. if ($firstblockfill && $this->blklvl >= $firstblockfill) {
  13296. $divh = $content[$i][0]['h'];
  13297. $bak_x = $this->x;
  13298. $this->DivLn($divh, -3, false);
  13299. // Reset current block fill
  13300. $bcor = $this->blk[$this->blklvl]['bgcolorarray'];
  13301. $this->SetFColor($bcor);
  13302. $this->x = $bak_x;
  13303. }
  13304. }
  13305. }
  13306. /* -- END COLUMNS -- */
  13307. $colctr = 0;
  13308. foreach ($content[$i] as $tablehf) {
  13309. $colctr++;
  13310. $y = $tablehf['y'] - $topy;
  13311. $this->y = $y;
  13312. //Set some cell values
  13313. $x = $tablehf['x'];
  13314. if (($this->mirrorMargins) && ($tablestartpage == 'ODD') && (($this->page) % 2 == 0)) { // EVEN
  13315. $x = $x + $this->MarginCorrection;
  13316. } elseif (($this->mirrorMargins) && ($tablestartpage == 'EVEN') && (($this->page) % 2 == 1)) { // ODD
  13317. $x = $x + $this->MarginCorrection;
  13318. }
  13319. /* -- COLUMNS -- */
  13320. // Added to correct for Columns
  13321. if ($this->ColActive) {
  13322. if ($this->directionality == 'rtl') { // *OTL*
  13323. $x -= ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  13324. } // *OTL*
  13325. else { // *OTL*
  13326. $x += ($this->CurrCol - $tablestartcolumn) * ($this->ColWidth + $this->ColGap);
  13327. } // *OTL*
  13328. }
  13329. /* -- END COLUMNS -- */
  13330. if ($colctr == 1) {
  13331. $x0 = $x;
  13332. }
  13333. // mPDF ITERATION
  13334. if ($this->iterationCounter) {
  13335. foreach ($tablehf['textbuffer'] AS $k => $t) {
  13336. if (!is_array($t[0]) && preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
  13337. $vname = '__' . $m[1] . '_';
  13338. if (!isset($this->$vname)) {
  13339. $this->$vname = 1;
  13340. } else {
  13341. $this->$vname++;
  13342. }
  13343. $tablehf['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $tablehf['textbuffer'][$k][0]);
  13344. }
  13345. }
  13346. }
  13347. $w = $tablehf['w'];
  13348. $h = $tablehf['h'];
  13349. $va = $tablehf['va'];
  13350. $R = $tablehf['R'];
  13351. $direction = $tablehf['direction'];
  13352. $mih = $tablehf['mih'];
  13353. $border = $tablehf['border'];
  13354. $border_details = $tablehf['border_details'];
  13355. $padding = $tablehf['padding'];
  13356. $this->tabletheadjustfinished = true;
  13357. $textbuffer = $tablehf['textbuffer'];
  13358. //Align
  13359. $align = $tablehf['a'];
  13360. $this->cellTextAlign = $align;
  13361. $this->cellLineHeight = $tablehf['cellLineHeight'];
  13362. $this->cellLineStackingStrategy = $tablehf['cellLineStackingStrategy'];
  13363. $this->cellLineStackingShift = $tablehf['cellLineStackingShift'];
  13364. $this->x = $x;
  13365. if ($this->ColActive) {
  13366. if ($table['borders_separate']) {
  13367. $tablefill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
  13368. if ($tablefill) {
  13369. $color = $this->ConvertColor($tablefill);
  13370. if ($color) {
  13371. $xadj = ($table['border_spacing_H'] / 2);
  13372. $yadj = ($table['border_spacing_V'] / 2);
  13373. $wadj = $table['border_spacing_H'];
  13374. $hadj = $table['border_spacing_V'];
  13375. if ($i == $firstrow && $horf == 'H') { // Top
  13376. $yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  13377. $hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  13378. }
  13379. if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1)) || (!isset($tablehf['rowspan']) && ($i + 1) == ($lastrow + 1))) && $horf == 'F') { // Bottom
  13380. $hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
  13381. }
  13382. if ($colctr == 1) { // Left
  13383. $xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  13384. $wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  13385. }
  13386. if ($colctr == count($content[$i])) { // Right
  13387. $wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
  13388. }
  13389. $this->SetFColor($color);
  13390. $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
  13391. }
  13392. }
  13393. }
  13394. }
  13395. if ($table['empty_cells'] != 'hide' || !empty($textbuffer) || !$table['borders_separate']) {
  13396. $paintcell = true;
  13397. } else {
  13398. $paintcell = false;
  13399. }
  13400. //Vertical align
  13401. if ($R && INTVAL($R) > 0 && isset($va) && $va != 'B') {
  13402. $va = 'B';
  13403. }
  13404. if (!isset($va) || empty($va) || $va == 'M')
  13405. $this->y += ($h - $mih) / 2;
  13406. elseif (isset($va) && $va == 'B')
  13407. $this->y += $h - $mih;
  13408. //TABLE ROW OR CELL FILL BGCOLOR
  13409. $fill = 0;
  13410. if (isset($tablehf['bgcolor']) && $tablehf['bgcolor'] && $tablehf['bgcolor'] != 'transparent') {
  13411. $fill = $tablehf['bgcolor'];
  13412. $leveladj = 6;
  13413. } elseif (isset($content[$i][0]['trbgcolor']) && $content[$i][0]['trbgcolor'] && $content[$i][0]['trbgcolor'] != 'transparent') { // Row color
  13414. $fill = $content[$i][0]['trbgcolor'];
  13415. $leveladj = 3;
  13416. }
  13417. if ($fill && $paintcell) {
  13418. $color = $this->ConvertColor($fill);
  13419. if ($color) {
  13420. if ($table['borders_separate']) {
  13421. if ($this->ColActive) {
  13422. $this->SetFColor($color);
  13423. $this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F');
  13424. } else {
  13425. $this->tableBackgrounds[$level * 9 + $leveladj][] = array('gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color);
  13426. }
  13427. } else {
  13428. if ($this->ColActive) {
  13429. $this->SetFColor($color);
  13430. $this->Rect($x, $y, $w, $h, 'F');
  13431. } else {
  13432. $this->tableBackgrounds[$level * 9 + $leveladj][] = array('gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color);
  13433. }
  13434. }
  13435. }
  13436. }
  13437. /* -- BACKGROUNDS -- */
  13438. if (isset($tablehf['gradient']) && $tablehf['gradient'] && $paintcell) {
  13439. $g = $this->grad->parseBackgroundGradient($tablehf['gradient']);
  13440. if ($g) {
  13441. if ($table['borders_separate']) {
  13442. $px = $x + ($table['border_spacing_H'] / 2);
  13443. $py = $y + ($table['border_spacing_V'] / 2);
  13444. $pw = $w - $table['border_spacing_H'];
  13445. $ph = $h - $table['border_spacing_V'];
  13446. } else {
  13447. $px = $x;
  13448. $py = $y;
  13449. $pw = $w;
  13450. $ph = $h;
  13451. }
  13452. if ($this->ColActive) {
  13453. $this->grad->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  13454. } else {
  13455. $this->tableBackgrounds[$level * 9 + 7][] = array('gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  13456. }
  13457. }
  13458. }
  13459. if (isset($tablehf['background-image']) && $paintcell) {
  13460. if ($tablehf['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $tablehf['background-image']['gradient'])) {
  13461. $g = $this->grad->parseMozGradient($tablehf['background-image']['gradient']);
  13462. if ($g) {
  13463. if ($table['borders_separate']) {
  13464. $px = $x + ($table['border_spacing_H'] / 2);
  13465. $py = $y + ($table['border_spacing_V'] / 2);
  13466. $pw = $w - $table['border_spacing_H'];
  13467. $ph = $h - $table['border_spacing_V'];
  13468. } else {
  13469. $px = $x;
  13470. $py = $y;
  13471. $pw = $w;
  13472. $ph = $h;
  13473. }
  13474. if ($this->ColActive) {
  13475. $this->grad->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  13476. } else {
  13477. $this->tableBackgrounds[$level * 9 + 7][] = array('gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  13478. }
  13479. }
  13480. } elseif ($tablehf['background-image']['image_id']) { // Background pattern
  13481. $n = count($this->patterns) + 1;
  13482. if ($table['borders_separate']) {
  13483. $px = $x + ($table['border_spacing_H'] / 2);
  13484. $py = $y + ($table['border_spacing_V'] / 2);
  13485. $pw = $w - $table['border_spacing_H'];
  13486. $ph = $h - $table['border_spacing_V'];
  13487. } else {
  13488. $px = $x;
  13489. $py = $y;
  13490. $pw = $w;
  13491. $ph = $h;
  13492. }
  13493. if ($this->ColActive) {
  13494. list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($tablehf['background-image']['orig_w'], $tablehf['background-image']['orig_h'], $pw, $ph, $tablehf['background-image']['resize'], $tablehf['background-image']['x_repeat'], $tablehf['background-image']['y_repeat']);
  13495. $this->patterns[$n] = array('x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'itype' => $tablehf['background-image']['itype']);
  13496. if ($tablehf['background-image']['opacity'] > 0 && $tablehf['background-image']['opacity'] < 1) {
  13497. $opac = $this->SetAlpha($tablehf['background-image']['opacity'], 'Normal', true);
  13498. } else {
  13499. $opac = '';
  13500. }
  13501. $this->_out(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * _MPDFK, ($this->h - $py) * _MPDFK, $pw * _MPDFK, -$ph * _MPDFK));
  13502. } else {
  13503. $this->tableBackgrounds[$level * 9 + 8][] = array('x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $tablehf['background-image']['image_id'], 'orig_w' => $tablehf['background-image']['orig_w'], 'orig_h' => $tablehf['background-image']['orig_h'], 'x_pos' => $tablehf['background-image']['x_pos'], 'y_pos' => $tablehf['background-image']['y_pos'], 'x_repeat' => $tablehf['background-image']['x_repeat'], 'y_repeat' => $tablehf['background-image']['y_repeat'], 'clippath' => '', 'resize' => $tablehf['background-image']['resize'], 'opacity' => $tablehf['background-image']['opacity'], 'itype' => $tablehf['background-image']['itype']);
  13504. }
  13505. }
  13506. }
  13507. /* -- END BACKGROUNDS -- */
  13508. //Cell Border
  13509. if ($table['borders_separate'] && $paintcell && $border) {
  13510. $this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($border_details['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($border_details['T']['w'] / 2), $w - $table['border_spacing_H'] - ($border_details['L']['w'] / 2) - ($border_details['R']['w'] / 2), $h - $table['border_spacing_V'] - ($border_details['T']['w'] / 2) - ($border_details['B']['w'] / 2), $border, $border_details, false, $table['borders_separate']);
  13511. } elseif ($paintcell && $border) {
  13512. $this->_tableRect($x, $y, $w, $h, $border, $border_details, true, $table['borders_separate']); // true causes buffer
  13513. }
  13514. //Print cell content
  13515. if (!empty($textbuffer)) {
  13516. if ($horf == 'F' && preg_match('/{colsum([0-9]*)[_]*}/', $textbuffer[0][0], $m)) {
  13517. $rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$colctr - 1]);
  13518. $textbuffer[0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $textbuffer[0][0]);
  13519. }
  13520. if ($R) {
  13521. $cellPtSize = $textbuffer[0][11] / $this->shrin_k;
  13522. if (!$cellPtSize) {
  13523. $cellPtSize = $this->default_font_size;
  13524. }
  13525. $cellFontHeight = ($cellPtSize / _MPDFK);
  13526. $opx = $this->x;
  13527. $opy = $this->y;
  13528. $angle = INTVAL($R);
  13529. // Only allow 45 - 90 degrees (when bottom-aligned) or -90
  13530. if ($angle > 90) {
  13531. $angle = 90;
  13532. } elseif ($angle > 0 && (isset($va) && $va != 'B')) {
  13533. $angle = 90;
  13534. } elseif ($angle > 0 && $angle < 45) {
  13535. $angle = 45;
  13536. } elseif ($angle < 0) {
  13537. $angle = -90;
  13538. }
  13539. $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
  13540. if (isset($align) && $align == 'R') {
  13541. $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($padding['R'] + $border_details['R']['w']);
  13542. } elseif (!isset($align) || $align == 'C') {
  13543. $this->x += ($w / 2) + ($offset);
  13544. } else {
  13545. $this->x += ($offset) + ($cellFontHeight / 3) + ($padding['L'] + $border_details['L']['w']);
  13546. }
  13547. $str = '';
  13548. foreach ($tablehf['textbuffer'] AS $t) {
  13549. $str .= $t[0] . ' ';
  13550. }
  13551. $str = rtrim($str);
  13552. if (!isset($va) || $va == 'M') {
  13553. $this->y -= ($h - $mih) / 2; //Undo what was added earlier VERTICAL ALIGN
  13554. if ($angle > 0) {
  13555. $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']) + ($mih - ($padding['T'] + $border_details['T']['w'] + $border_details['B']['w'] + $padding['B']));
  13556. } elseif ($angle < 0) {
  13557. $this->y += (($h - $mih) / 2) + ($padding['T'] + $border_details['T']['w']);
  13558. }
  13559. } elseif (isset($va) && $va == 'B') {
  13560. $this->y -= $h - $mih; //Undo what was added earlier VERTICAL ALIGN
  13561. if ($angle > 0) {
  13562. $this->y += $h - ($border_details['B']['w'] + $padding['B']);
  13563. } elseif ($angle < 0) {
  13564. $this->y += $h - $mih + ($padding['T'] + $border_details['T']['w']);
  13565. }
  13566. } elseif (isset($va) && $va == 'T') {
  13567. if ($angle > 0) {
  13568. $this->y += $mih - ($border_details['B']['w'] + $padding['B']);
  13569. } elseif ($angle < 0) {
  13570. $this->y += ($padding['T'] + $border_details['T']['w']);
  13571. }
  13572. }
  13573. $this->Rotate($angle, $this->x, $this->y);
  13574. $s_fs = $this->FontSizePt;
  13575. $s_f = $this->FontFamily;
  13576. $s_st = $this->FontStyle;
  13577. if (!empty($textbuffer[0][3])) { //Font Color
  13578. $cor = $textbuffer[0][3];
  13579. $this->SetTColor($cor);
  13580. }
  13581. $this->SetFont($textbuffer[0][4], $textbuffer[0][2], $cellPtSize, true, true);
  13582. $this->magic_reverse_dir($str, $this->directionality, $textbuffer[0][18]);
  13583. $this->Text($this->x, $this->y, $str, $textbuffer[0][18], $textbuffer[0][8]); // textvar
  13584. $this->Rotate(0);
  13585. $this->SetFont($s_f, $s_st, $s_fs, true, true);
  13586. $this->SetTColor(0);
  13587. $this->x = $opx;
  13588. $this->y = $opy;
  13589. } else {
  13590. if ($table['borders_separate']) { // NB twice border width
  13591. $xadj = $border_details['L']['w'] + $padding['L'] + ($table['border_spacing_H'] / 2);
  13592. $wadj = $border_details['L']['w'] + $border_details['R']['w'] + $padding['L'] + $padding['R'] + $table['border_spacing_H'];
  13593. $yadj = $border_details['T']['w'] + $padding['T'] + ($table['border_spacing_H'] / 2);
  13594. } else {
  13595. $xadj = $border_details['L']['w'] / 2 + $padding['L'];
  13596. $wadj = ($border_details['L']['w'] + $border_details['R']['w']) / 2 + $padding['L'] + $padding['R'];
  13597. $yadj = $border_details['T']['w'] / 2 + $padding['T'];
  13598. }
  13599. $this->divwidth = $w - ($wadj);
  13600. $this->x += $xadj;
  13601. $this->y += $yadj;
  13602. $this->printbuffer($textbuffer, '', true, false, $direction);
  13603. }
  13604. }
  13605. $textbuffer = array();
  13606. /* -- BACKGROUNDS -- */
  13607. if (!$this->ColActive) {
  13608. if (isset($content[$i][0]['trgradients']) && ($colctr == 1 || $table['borders_separate'])) {
  13609. $g = $this->grad->parseBackgroundGradient($content[$i][0]['trgradients']);
  13610. if ($g) {
  13611. $gx = $x0;
  13612. $gy = $y;
  13613. $gh = $h;
  13614. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  13615. if ($table['borders_separate']) {
  13616. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  13617. $clx = $x + ($table['border_spacing_H'] / 2);
  13618. $cly = $y + ($table['border_spacing_V'] / 2);
  13619. $clw = $w - $table['border_spacing_H'];
  13620. $clh = $h - $table['border_spacing_V'];
  13621. // Set clipping path
  13622. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  13623. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s);
  13624. } else {
  13625. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  13626. }
  13627. }
  13628. }
  13629. if (isset($content[$i][0]['trbackground-images']) && ($colctr == 1 || $table['borders_separate'])) {
  13630. if ($content[$i][0]['trbackground-images']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $content[$i][0]['trbackground-images']['gradient'])) {
  13631. $g = $this->grad->parseMozGradient($content[$i][0]['trbackground-images']['gradient']);
  13632. if ($g) {
  13633. $gx = $x0;
  13634. $gy = $y;
  13635. $gh = $h;
  13636. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  13637. if ($table['borders_separate']) {
  13638. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  13639. $clx = $x + ($table['border_spacing_H'] / 2);
  13640. $cly = $y + ($table['border_spacing_V'] / 2);
  13641. $clw = $w - $table['border_spacing_H'];
  13642. $clh = $h - $table['border_spacing_V'];
  13643. // Set clipping path
  13644. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  13645. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s);
  13646. } else {
  13647. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  13648. }
  13649. }
  13650. } else {
  13651. $image_id = $content[$i][0]['trbackground-images']['image_id'];
  13652. $orig_w = $content[$i][0]['trbackground-images']['orig_w'];
  13653. $orig_h = $content[$i][0]['trbackground-images']['orig_h'];
  13654. $x_pos = $content[$i][0]['trbackground-images']['x_pos'];
  13655. $y_pos = $content[$i][0]['trbackground-images']['y_pos'];
  13656. $x_repeat = $content[$i][0]['trbackground-images']['x_repeat'];
  13657. $y_repeat = $content[$i][0]['trbackground-images']['y_repeat'];
  13658. $resize = $content[$i][0]['trbackground-images']['resize'];
  13659. $opacity = $content[$i][0]['trbackground-images']['opacity'];
  13660. $itype = $content[$i][0]['trbackground-images']['itype'];
  13661. $clippath = '';
  13662. $gx = $x0;
  13663. $gy = $y;
  13664. $gh = $h;
  13665. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  13666. if ($table['borders_separate']) {
  13667. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  13668. $clx = $x + ($table['border_spacing_H'] / 2);
  13669. $cly = $y + ($table['border_spacing_V'] / 2);
  13670. $clw = $w - $table['border_spacing_H'];
  13671. $clh = $h - $table['border_spacing_V'];
  13672. // Set clipping path
  13673. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  13674. $this->tableBackgrounds[$level * 9 + 5][] = array('x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  13675. } else {
  13676. $this->tableBackgrounds[$level * 9 + 5][] = array('x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  13677. }
  13678. }
  13679. }
  13680. }
  13681. /* -- END BACKGROUNDS -- */
  13682. // TABLE BORDER - if separate OR collapsed and only table border
  13683. if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
  13684. $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
  13685. $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
  13686. $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
  13687. $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
  13688. $tbx = $x;
  13689. $tby = $y;
  13690. $tbw = $w;
  13691. $tbh = $h;
  13692. $tab_bord = 0;
  13693. $corner = '';
  13694. if ($i == $firstrow && $horf == 'H') { // Top
  13695. $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
  13696. $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
  13697. $this->setBorder($tab_bord, _BORDER_TOP);
  13698. $corner .= 'T';
  13699. }
  13700. if (($i == ($lastrow) || (isset($tablehf['rowspan']) && ($i + $tablehf['rowspan']) == ($lastrow + 1))) && $horf == 'F') { // Bottom
  13701. $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
  13702. $this->setBorder($tab_bord, _BORDER_BOTTOM);
  13703. $corner .= 'B';
  13704. }
  13705. if ($colctr == 1 && $firstSpread) { // Left
  13706. $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
  13707. $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
  13708. $this->setBorder($tab_bord, _BORDER_LEFT);
  13709. $corner .= 'L';
  13710. }
  13711. if ($colctr == count($content[$i]) && $finalSpread) { // Right
  13712. $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
  13713. $this->setBorder($tab_bord, _BORDER_RIGHT);
  13714. $corner .= 'R';
  13715. }
  13716. $this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']);
  13717. }
  13718. }// end column $content
  13719. $this->y = $y + $h; //Update y coordinate
  13720. }// end row $i
  13721. unset($table);
  13722. $this->colsums = array();
  13723. }
  13724. }
  13725. /* -- END TABLES -- */
  13726. function SetHTMLHeader($header = '', $OE = '', $write = false)
  13727. {
  13728. $height = 0;
  13729. if (is_array($header) && isset($header['html']) && $header['html']) {
  13730. $Hhtml = $header['html'];
  13731. if ($this->setAutoTopMargin) {
  13732. if (isset($header['h'])) {
  13733. $height = $header['h'];
  13734. } else {
  13735. $height = $this->_gethtmlheight($Hhtml);
  13736. }
  13737. }
  13738. } elseif (!is_array($header) && $header) {
  13739. $Hhtml = $header;
  13740. if ($this->setAutoTopMargin) {
  13741. $height = $this->_gethtmlheight($Hhtml);
  13742. }
  13743. } else {
  13744. $Hhtml = '';
  13745. }
  13746. if ($OE != 'E') {
  13747. $OE = 'O';
  13748. }
  13749. if ($OE == 'E') {
  13750. if ($Hhtml) {
  13751. $this->HTMLHeaderE['html'] = $Hhtml;
  13752. $this->HTMLHeaderE['h'] = $height;
  13753. } else {
  13754. $this->HTMLHeaderE = '';
  13755. }
  13756. } else {
  13757. if ($Hhtml) {
  13758. $this->HTMLHeader['html'] = $Hhtml;
  13759. $this->HTMLHeader['h'] = $height;
  13760. } else {
  13761. $this->HTMLHeader = '';
  13762. }
  13763. }
  13764. if (!$this->mirrorMargins && $OE == 'E') {
  13765. return;
  13766. }
  13767. if ($Hhtml == '') {
  13768. return;
  13769. }
  13770. if ($this->setAutoTopMargin == 'pad') {
  13771. $this->tMargin = $this->margin_header + $height + $this->orig_tMargin;
  13772. if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
  13773. $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
  13774. }
  13775. } elseif ($this->setAutoTopMargin == 'stretch') {
  13776. $this->tMargin = max($this->orig_tMargin, $this->margin_header + $height + $this->autoMarginPadding);
  13777. if (isset($this->saveHTMLHeader[$this->page][$OE]['mt'])) {
  13778. $this->saveHTMLHeader[$this->page][$OE]['mt'] = $this->tMargin;
  13779. }
  13780. }
  13781. if ($write && $this->state != 0 && (($this->mirrorMargins && $OE == 'E' && ($this->page) % 2 == 0) || ($this->mirrorMargins && $OE != 'E' && ($this->page) % 2 == 1) || !$this->mirrorMargins)) {
  13782. $this->writeHTMLHeaders();
  13783. }
  13784. }
  13785. function SetHTMLFooter($footer = '', $OE = '')
  13786. {
  13787. $height = 0;
  13788. if (is_array($footer) && isset($footer['html']) && $footer['html']) {
  13789. $Fhtml = $footer['html'];
  13790. if ($this->setAutoBottomMargin) {
  13791. if (isset($footer['h'])) {
  13792. $height = $footer['h'];
  13793. } else {
  13794. $height = $this->_gethtmlheight($Fhtml);
  13795. }
  13796. }
  13797. } elseif (!is_array($footer) && $footer) {
  13798. $Fhtml = $footer;
  13799. if ($this->setAutoBottomMargin) {
  13800. $height = $this->_gethtmlheight($Fhtml);
  13801. }
  13802. } else {
  13803. $Fhtml = '';
  13804. }
  13805. if ($OE != 'E') {
  13806. $OE = 'O';
  13807. }
  13808. if ($OE == 'E') {
  13809. if ($Fhtml) {
  13810. $this->HTMLFooterE['html'] = $Fhtml;
  13811. $this->HTMLFooterE['h'] = $height;
  13812. } else {
  13813. $this->HTMLFooterE = '';
  13814. }
  13815. } else {
  13816. if ($Fhtml) {
  13817. $this->HTMLFooter['html'] = $Fhtml;
  13818. $this->HTMLFooter['h'] = $height;
  13819. } else {
  13820. $this->HTMLFooter = '';
  13821. }
  13822. }
  13823. if (!$this->mirrorMargins && $OE == 'E') {
  13824. return;
  13825. }
  13826. if ($Fhtml == '') {
  13827. return false;
  13828. }
  13829. if ($this->setAutoBottomMargin == 'pad') {
  13830. $this->bMargin = $this->margin_footer + $height + $this->orig_bMargin;
  13831. $this->PageBreakTrigger = $this->h - $this->bMargin;
  13832. if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
  13833. $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
  13834. }
  13835. } elseif ($this->setAutoBottomMargin == 'stretch') {
  13836. $this->bMargin = max($this->orig_bMargin, $this->margin_footer + $height + $this->autoMarginPadding);
  13837. $this->PageBreakTrigger = $this->h - $this->bMargin;
  13838. if (isset($this->saveHTMLHeader[$this->page][$OE]['mb'])) {
  13839. $this->saveHTMLHeader[$this->page][$OE]['mb'] = $this->bMargin;
  13840. }
  13841. }
  13842. }
  13843. function _getHtmlHeight($html)
  13844. {
  13845. $save_state = $this->state;
  13846. if ($this->state == 0) {
  13847. $this->AddPage($this->CurOrientation);
  13848. }
  13849. $this->state = 2;
  13850. $this->Reset();
  13851. $this->pageoutput[$this->page] = array();
  13852. $save_x = $this->x;
  13853. $save_y = $this->y;
  13854. $this->x = $this->lMargin;
  13855. $this->y = $this->margin_header;
  13856. $html = str_replace('{PAGENO}', $this->pagenumPrefix . $this->docPageNum($this->page) . $this->pagenumSuffix, $html);
  13857. $html = str_replace($this->aliasNbPgGp, $this->nbpgPrefix . $this->docPageNumTotal($this->page) . $this->nbpgSuffix, $html);
  13858. $html = str_replace($this->aliasNbPg, $this->page, $html);
  13859. $html = preg_replace_callback('/\{DATE\s+(.*?)\}/', array($this, 'date_callback'), $html); // mPDF 5.7
  13860. $this->HTMLheaderPageLinks = array();
  13861. $this->HTMLheaderPageAnnots = array();
  13862. $this->HTMLheaderPageForms = array();
  13863. $savepb = $this->pageBackgrounds;
  13864. $this->writingHTMLheader = true;
  13865. $this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
  13866. $this->writingHTMLheader = false;
  13867. $h = ($this->y - $this->margin_header);
  13868. $this->Reset();
  13869. // mPDF 5.7.2 - Clear in case Float used in Header/Footer
  13870. $this->blk[0]['blockContext'] = 0;
  13871. $this->blk[0]['float_endpos'] = 0;
  13872. $this->pageoutput[$this->page] = array();
  13873. $this->headerbuffer = '';
  13874. $this->pageBackgrounds = $savepb;
  13875. $this->x = $save_x;
  13876. $this->y = $save_y;
  13877. $this->state = $save_state;
  13878. if ($save_state == 0) {
  13879. unset($this->pages[1]);
  13880. $this->page = 0;
  13881. }
  13882. return $h;
  13883. }
  13884. // Called internally from Header
  13885. function writeHTMLHeaders()
  13886. {
  13887. if ($this->mirrorMargins && ($this->page) % 2 == 0) {
  13888. $OE = 'E';
  13889. } // EVEN
  13890. else {
  13891. $OE = 'O';
  13892. }
  13893. if ($OE == 'E') {
  13894. $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeaderE['html'];
  13895. } else {
  13896. $this->saveHTMLHeader[$this->page][$OE]['html'] = $this->HTMLHeader['html'];
  13897. }
  13898. if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
  13899. $this->saveHTMLHeader[$this->page][$OE]['rotate'] = true;
  13900. $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->tMargin;
  13901. $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->bMargin;
  13902. $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
  13903. $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
  13904. $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->h;
  13905. $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->w;
  13906. } else {
  13907. $this->saveHTMLHeader[$this->page][$OE]['ml'] = $this->lMargin;
  13908. $this->saveHTMLHeader[$this->page][$OE]['mr'] = $this->rMargin;
  13909. $this->saveHTMLHeader[$this->page][$OE]['mh'] = $this->margin_header;
  13910. $this->saveHTMLHeader[$this->page][$OE]['mf'] = $this->margin_footer;
  13911. $this->saveHTMLHeader[$this->page][$OE]['pw'] = $this->w;
  13912. $this->saveHTMLHeader[$this->page][$OE]['ph'] = $this->h;
  13913. }
  13914. }
  13915. function writeHTMLFooters()
  13916. {
  13917. if ($this->mirrorMargins && ($this->page) % 2 == 0) {
  13918. $OE = 'E';
  13919. } // EVEN
  13920. else {
  13921. $OE = 'O';
  13922. }
  13923. if ($OE == 'E') {
  13924. $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooterE['html'];
  13925. } else {
  13926. $this->saveHTMLFooter[$this->page][$OE]['html'] = $this->HTMLFooter['html'];
  13927. }
  13928. if ($this->forcePortraitHeaders && $this->CurOrientation == 'L' && $this->CurOrientation != $this->DefOrientation) {
  13929. $this->saveHTMLFooter[$this->page][$OE]['rotate'] = true;
  13930. $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->tMargin;
  13931. $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->bMargin;
  13932. $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->rMargin;
  13933. $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->lMargin;
  13934. $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
  13935. $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
  13936. $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->h;
  13937. $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->w;
  13938. } else {
  13939. $this->saveHTMLFooter[$this->page][$OE]['ml'] = $this->lMargin;
  13940. $this->saveHTMLFooter[$this->page][$OE]['mr'] = $this->rMargin;
  13941. $this->saveHTMLFooter[$this->page][$OE]['mt'] = $this->tMargin;
  13942. $this->saveHTMLFooter[$this->page][$OE]['mb'] = $this->bMargin;
  13943. $this->saveHTMLFooter[$this->page][$OE]['mh'] = $this->margin_header;
  13944. $this->saveHTMLFooter[$this->page][$OE]['mf'] = $this->margin_footer;
  13945. $this->saveHTMLFooter[$this->page][$OE]['pw'] = $this->w;
  13946. $this->saveHTMLFooter[$this->page][$OE]['ph'] = $this->h;
  13947. }
  13948. }
  13949. // mPDF 6
  13950. function _shareHeaderFooterWidth($cl, $cc, $cr)
  13951. { // mPDF 6
  13952. $l = mb_strlen($cl, 'UTF-8');
  13953. $c = mb_strlen($cc, 'UTF-8');
  13954. $r = mb_strlen($cr, 'UTF-8');
  13955. $s = max($l, $r);
  13956. $tw = $c + 2 * $s;
  13957. if ($tw > 0) {
  13958. return array(intval($s * 100 / $tw), intval($c * 100 / $tw), intval($s * 100 / $tw));
  13959. } else {
  13960. return array(33, 33, 33);
  13961. }
  13962. }
  13963. // mPDF 6
  13964. // Create an HTML header/footer from array (non-HTML header/footer)
  13965. function _createHTMLheaderFooter($arr, $hf)
  13966. {
  13967. $lContent = (isset($arr['L']['content']) ? $arr['L']['content'] : '');
  13968. $cContent = (isset($arr['C']['content']) ? $arr['C']['content'] : '');
  13969. $rContent = (isset($arr['R']['content']) ? $arr['R']['content'] : '');
  13970. list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($lContent, $cContent, $rContent);
  13971. if ($hf == 'H') {
  13972. $valign = 'bottom';
  13973. $vpadding = '0 0 ' . $this->header_line_spacing . 'em 0';
  13974. } else {
  13975. $valign = 'top';
  13976. $vpadding = '' . $this->footer_line_spacing . 'em 0 0 0';
  13977. }
  13978. if ($this->directionality == 'rtl') { // table columns get reversed so need different text-alignment
  13979. $talignL = 'right';
  13980. $talignR = 'left';
  13981. } else {
  13982. $talignL = 'left';
  13983. $talignR = 'right';
  13984. }
  13985. $html = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: ' . $valign . '; color: #000000; ';
  13986. if (isset($arr['line']) && $arr['line']) {
  13987. $html .= ' border-' . $valign . ': 0.1mm solid #000000;';
  13988. }
  13989. $html .= '">';
  13990. $html .= '<tr>';
  13991. $html .= '<td width="' . $lw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignL . '; ';
  13992. if (isset($arr['L']['font-family'])) {
  13993. $html .= ' font-family: ' . $arr['L']['font-family'] . ';';
  13994. }
  13995. if (isset($arr['L']['color'])) {
  13996. $html .= ' color: ' . $arr['L']['color'] . ';';
  13997. }
  13998. if (isset($arr['L']['font-size'])) {
  13999. $html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';
  14000. }
  14001. if (isset($arr['L']['font-style'])) {
  14002. if ($arr['L']['font-style'] == 'B' || $arr['L']['font-style'] == 'BI') {
  14003. $html .= ' font-weight: bold;';
  14004. }
  14005. if ($arr['L']['font-style'] == 'I' || $arr['L']['font-style'] == 'BI') {
  14006. $html .= ' font-style: italic;';
  14007. }
  14008. }
  14009. $html .= '">' . $lContent . '</td>';
  14010. $html .= '<td width="' . $cw . '%" style="padding: ' . $vpadding . '; text-align: center; ';
  14011. if (isset($arr['C']['font-family'])) {
  14012. $html .= ' font-family: ' . $arr['C']['font-family'] . ';';
  14013. }
  14014. if (isset($arr['C']['color'])) {
  14015. $html .= ' color: ' . $arr['C']['color'] . ';';
  14016. }
  14017. if (isset($arr['C']['font-size'])) {
  14018. $html .= ' font-size: ' . $arr['L']['font-size'] . 'pt;';
  14019. }
  14020. if (isset($arr['C']['font-style'])) {
  14021. if ($arr['C']['font-style'] == 'B' || $arr['C']['font-style'] == 'BI') {
  14022. $html .= ' font-weight: bold;';
  14023. }
  14024. if ($arr['C']['font-style'] == 'I' || $arr['C']['font-style'] == 'BI') {
  14025. $html .= ' font-style: italic;';
  14026. }
  14027. }
  14028. $html .= '">' . $cContent . '</td>';
  14029. $html .= '<td width="' . $rw . '%" style="padding: ' . $vpadding . '; text-align: ' . $talignR . '; ';
  14030. if (isset($arr['R']['font-family'])) {
  14031. $html .= ' font-family: ' . $arr['R']['font-family'] . ';';
  14032. }
  14033. if (isset($arr['R']['color'])) {
  14034. $html .= ' color: ' . $arr['R']['color'] . ';';
  14035. }
  14036. if (isset($arr['R']['font-size'])) {
  14037. $html .= ' font-size: ' . $arr['R']['font-size'] . 'pt;';
  14038. }
  14039. if (isset($arr['R']['font-style'])) {
  14040. if ($arr['R']['font-style'] == 'B' || $arr['R']['font-style'] == 'BI') {
  14041. $html .= ' font-weight: bold;';
  14042. }
  14043. if ($arr['R']['font-style'] == 'I' || $arr['R']['font-style'] == 'BI') {
  14044. $html .= ' font-style: italic;';
  14045. }
  14046. }
  14047. $html .= '">' . $rContent . '</td>';
  14048. $html .= '</tr></table>';
  14049. return $html;
  14050. }
  14051. function DefHeaderByName($name, $arr)
  14052. {
  14053. if (!$name) {
  14054. $name = '_nonhtmldefault';
  14055. }
  14056. $html = $this->_createHTMLheaderFooter($arr, 'H');
  14057. $this->pageHTMLheaders[$name]['html'] = $html;
  14058. $this->pageHTMLheaders[$name]['h'] = $this->_gethtmlheight($html);
  14059. }
  14060. function DefFooterByName($name, $arr)
  14061. {
  14062. if (!$name) {
  14063. $name = '_nonhtmldefault';
  14064. }
  14065. $html = $this->_createHTMLheaderFooter($arr, 'F');
  14066. $this->pageHTMLfooters[$name]['html'] = $html;
  14067. $this->pageHTMLfooters[$name]['h'] = $this->_gethtmlheight($html);
  14068. }
  14069. function SetHeaderByName($name, $side = 'O', $write = false)
  14070. {
  14071. if (!$name) {
  14072. $name = '_nonhtmldefault';
  14073. }
  14074. $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
  14075. }
  14076. function SetFooterByName($name, $side = 'O')
  14077. {
  14078. if (!$name) {
  14079. $name = '_nonhtmldefault';
  14080. }
  14081. $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
  14082. }
  14083. function DefHTMLHeaderByName($name, $html)
  14084. {
  14085. if (!$name) {
  14086. $name = '_default';
  14087. }
  14088. $this->pageHTMLheaders[$name]['html'] = $html;
  14089. $this->pageHTMLheaders[$name]['h'] = $this->_gethtmlheight($html);
  14090. }
  14091. function DefHTMLFooterByName($name, $html)
  14092. {
  14093. if (!$name) {
  14094. $name = '_default';
  14095. }
  14096. $this->pageHTMLfooters[$name]['html'] = $html;
  14097. $this->pageHTMLfooters[$name]['h'] = $this->_gethtmlheight($html);
  14098. }
  14099. function SetHTMLHeaderByName($name, $side = 'O', $write = false)
  14100. {
  14101. if (!$name) {
  14102. $name = '_default';
  14103. }
  14104. $this->SetHTMLHeader($this->pageHTMLheaders[$name], $side, $write);
  14105. }
  14106. function SetHTMLFooterByName($name, $side = 'O')
  14107. {
  14108. if (!$name) {
  14109. $name = '_default';
  14110. }
  14111. $this->SetHTMLFooter($this->pageHTMLfooters[$name], $side);
  14112. }
  14113. function SetHeader($Harray = array(), $side = '', $write = false)
  14114. {
  14115. $oddhtml = '';
  14116. $evenhtml = '';
  14117. if (is_string($Harray)) {
  14118. if (strlen($Harray) == 0) {
  14119. $oddhtml = '';
  14120. $evenhtml = '';
  14121. } elseif (strpos($Harray, '|') !== false) {
  14122. $hdet = explode('|', $Harray);
  14123. list($lw, $cw, $rw) = $this->_shareHeaderFooterWidth($hdet[0], $hdet[1], $hdet[2]);
  14124. $oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
  14125. if ($this->defaultheaderfontsize) {
  14126. $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  14127. }
  14128. if ($this->defaultheaderfontstyle) {
  14129. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  14130. $oddhtml .= ' font-weight: bold;';
  14131. }
  14132. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  14133. $oddhtml .= ' font-style: italic;';
  14134. }
  14135. }
  14136. if ($this->defaultheaderline) {
  14137. $oddhtml .= ' border-bottom: 0.1mm solid #000000;';
  14138. }
  14139. $oddhtml .= '">';
  14140. $oddhtml .= '<tr>';
  14141. $oddhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[0] . '</td>';
  14142. $oddhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
  14143. $oddhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[2] . '</td>';
  14144. $oddhtml .= '</tr></table>';
  14145. $evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: bottom; color: #000000; ';
  14146. if ($this->defaultheaderfontsize) {
  14147. $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  14148. }
  14149. if ($this->defaultheaderfontstyle) {
  14150. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  14151. $evenhtml .= ' font-weight: bold;';
  14152. }
  14153. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  14154. $evenhtml .= ' font-style: italic;';
  14155. }
  14156. }
  14157. if ($this->defaultheaderline) {
  14158. $evenhtml .= ' border-bottom: 0.1mm solid #000000;';
  14159. }
  14160. $evenhtml .= '">';
  14161. $evenhtml .= '<tr>';
  14162. $evenhtml .= '<td width="' . $rw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: left; ">' . $hdet[2] . '</td>';
  14163. $evenhtml .= '<td width="' . $cw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: center; ">' . $hdet[1] . '</td>';
  14164. $evenhtml .= '<td width="' . $lw . '%" style="padding: 0 0 ' . $this->header_line_spacing . 'em 0; text-align: right; ">' . $hdet[0] . '</td>';
  14165. $evenhtml .= '</tr></table>';
  14166. } else {
  14167. $oddhtml = '<div style="margin: 0; color: #000000; ';
  14168. if ($this->defaultheaderfontsize) {
  14169. $oddhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  14170. }
  14171. if ($this->defaultheaderfontstyle) {
  14172. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  14173. $oddhtml .= ' font-weight: bold;';
  14174. }
  14175. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  14176. $oddhtml .= ' font-style: italic;';
  14177. }
  14178. }
  14179. if ($this->defaultheaderline) {
  14180. $oddhtml .= ' border-bottom: 0.1mm solid #000000;';
  14181. }
  14182. $oddhtml .= 'text-align: right; ">' . $Harray . '</div>';
  14183. $evenhtml = '<div style="margin: 0; color: #000000; ';
  14184. if ($this->defaultheaderfontsize) {
  14185. $evenhtml .= ' font-size: ' . $this->defaultheaderfontsize . 'pt;';
  14186. }
  14187. if ($this->defaultheaderfontstyle) {
  14188. if ($this->defaultheaderfontstyle == 'B' || $this->defaultheaderfontstyle == 'BI') {
  14189. $evenhtml .= ' font-weight: bold;';
  14190. }
  14191. if ($this->defaultheaderfontstyle == 'I' || $this->defaultheaderfontstyle == 'BI') {
  14192. $evenhtml .= ' font-style: italic;';
  14193. }
  14194. }
  14195. if ($this->defaultheaderline) {
  14196. $evenhtml .= ' border-bottom: 0.1mm solid #000000;';
  14197. }
  14198. $evenhtml .= 'text-align: left; ">' . $Harray . '</div>';
  14199. }
  14200. } elseif (is_array($Harray) && !empty($Harray)) {
  14201. if ($side == 'O') {
  14202. $odd = $Harray;
  14203. } elseif ($side == 'E') {
  14204. $even = $Harray;
  14205. } else {
  14206. $odd = $Harray['odd'];
  14207. $even = $Harray['even'];
  14208. }
  14209. $oddhtml = $this->_createHTMLheaderFooter($odd, 'H');
  14210. $evenhtml = $this->_createHTMLheaderFooter($even, 'H');
  14211. }
  14212. if ($side == 'E') {
  14213. $this->SetHTMLHeader($evenhtml, 'E', $write);
  14214. } elseif ($side == 'O') {
  14215. $this->SetHTMLHeader($oddhtml, 'O', $write);
  14216. } else {
  14217. $this->SetHTMLHeader($oddhtml, 'O', $write);
  14218. $this->SetHTMLHeader($evenhtml, 'E', $write);
  14219. }
  14220. }
  14221. function SetFooter($Farray = array(), $side = '')
  14222. {
  14223. $oddhtml = '';
  14224. $evenhtml = '';
  14225. if (is_string($Farray)) {
  14226. if (strlen($Farray) == 0) {
  14227. $oddhtml = '';
  14228. $evenhtml = '';
  14229. } elseif (strpos($Farray, '|') !== false) {
  14230. $hdet = explode('|', $Farray);
  14231. $oddhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
  14232. if ($this->defaultfooterfontsize) {
  14233. $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  14234. }
  14235. if ($this->defaultfooterfontstyle) {
  14236. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  14237. $oddhtml .= ' font-weight: bold;';
  14238. }
  14239. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  14240. $oddhtml .= ' font-style: italic;';
  14241. }
  14242. }
  14243. if ($this->defaultfooterline) {
  14244. $oddhtml .= ' border-top: 0.1mm solid #000000;';
  14245. }
  14246. $oddhtml .= '">';
  14247. $oddhtml .= '<tr>';
  14248. $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[0] . '</td>';
  14249. $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
  14250. $oddhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[2] . '</td>';
  14251. $oddhtml .= '</tr></table>';
  14252. $evenhtml = '<table width="100%" style="border-collapse: collapse; margin: 0; vertical-align: top; color: #000000; ';
  14253. if ($this->defaultfooterfontsize) {
  14254. $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  14255. }
  14256. if ($this->defaultfooterfontstyle) {
  14257. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  14258. $evenhtml .= ' font-weight: bold;';
  14259. }
  14260. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  14261. $evenhtml .= ' font-style: italic;';
  14262. }
  14263. }
  14264. if ($this->defaultfooterline) {
  14265. $evenhtml .= ' border-top: 0.1mm solid #000000;';
  14266. }
  14267. $evenhtml .= '">';
  14268. $evenhtml .= '<tr>';
  14269. $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: left; ">' . $hdet[2] . '</td>';
  14270. $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: center; ">' . $hdet[1] . '</td>';
  14271. $evenhtml .= '<td width="33%" style="padding: ' . $this->footer_line_spacing . 'em 0 0 0; text-align: right; ">' . $hdet[0] . '</td>';
  14272. $evenhtml .= '</tr></table>';
  14273. } else {
  14274. $oddhtml = '<div style="margin: 0; color: #000000; ';
  14275. if ($this->defaultfooterfontsize) {
  14276. $oddhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  14277. }
  14278. if ($this->defaultfooterfontstyle) {
  14279. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  14280. $oddhtml .= ' font-weight: bold;';
  14281. }
  14282. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  14283. $oddhtml .= ' font-style: italic;';
  14284. }
  14285. }
  14286. if ($this->defaultfooterline) {
  14287. $oddhtml .= ' border-top: 0.1mm solid #000000;';
  14288. }
  14289. $oddhtml .= 'text-align: right; ">' . $Farray . '</div>';
  14290. $evenhtml = '<div style="margin: 0; color: #000000; ';
  14291. if ($this->defaultfooterfontsize) {
  14292. $evenhtml .= ' font-size: ' . $this->defaultfooterfontsize . 'pt;';
  14293. }
  14294. if ($this->defaultfooterfontstyle) {
  14295. if ($this->defaultfooterfontstyle == 'B' || $this->defaultfooterfontstyle == 'BI') {
  14296. $evenhtml .= ' font-weight: bold;';
  14297. }
  14298. if ($this->defaultfooterfontstyle == 'I' || $this->defaultfooterfontstyle == 'BI') {
  14299. $evenhtml .= ' font-style: italic;';
  14300. }
  14301. }
  14302. if ($this->defaultfooterline) {
  14303. $evenhtml .= ' border-top: 0.1mm solid #000000;';
  14304. }
  14305. $evenhtml .= 'text-align: left; ">' . $Farray . '</div>';
  14306. }
  14307. } elseif (is_array($Farray)) {
  14308. if ($side == 'O') {
  14309. $odd = $Farray;
  14310. } elseif ($side == 'E') {
  14311. $even = $Farray;
  14312. } else {
  14313. if (isset($Farray['odd']))
  14314. $odd = $Farray['odd'];
  14315. if (isset($Farray['even']))
  14316. $even = $Farray['even'];
  14317. }
  14318. if (isset($odd))
  14319. $oddhtml = $this->_createHTMLheaderFooter($odd, 'F');
  14320. if (isset($even))
  14321. $evenhtml = $this->_createHTMLheaderFooter($even, 'F');
  14322. }
  14323. /* -- HTMLfooterS-FOOTERS -- */
  14324. if ($side == 'E') {
  14325. $this->SetHTMLfooter($evenhtml, 'E');
  14326. } elseif ($side == 'O') {
  14327. $this->SetHTMLfooter($oddhtml, 'O');
  14328. } else {
  14329. $this->SetHTMLfooter($oddhtml, 'O');
  14330. $this->SetHTMLfooter($evenhtml, 'E');
  14331. }
  14332. /* -- END HTMLfooterS-FOOTERS -- */
  14333. }
  14334. /* -- WATERMARK -- */
  14335. function SetWatermarkText($txt = '', $alpha = -1)
  14336. {
  14337. if ($alpha >= 0)
  14338. $this->watermarkTextAlpha = $alpha;
  14339. $this->watermarkText = $txt;
  14340. }
  14341. function SetWatermarkImage($src, $alpha = -1, $size = 'D', $pos = 'F')
  14342. {
  14343. if ($alpha >= 0)
  14344. $this->watermarkImageAlpha = $alpha;
  14345. $this->watermarkImage = $src;
  14346. $this->watermark_size = $size;
  14347. $this->watermark_pos = $pos;
  14348. }
  14349. /* -- END WATERMARK -- */
  14350. //Page footer
  14351. function Footer()
  14352. {
  14353. /* -- CSS-PAGE -- */
  14354. // PAGED MEDIA - CROP / CROSS MARKS from @PAGE
  14355. if ($this->show_marks == 'CROP' || $this->show_marks == 'CROPCROSS') {
  14356. // Show TICK MARKS
  14357. $this->SetLineWidth(0.1); // = 0.1 mm
  14358. $this->SetDColor($this->ConvertColor(0));
  14359. $l = $this->cropMarkLength;
  14360. $m = $this->cropMarkMargin; // Distance of crop mark from margin
  14361. $b = $this->nonPrintMargin; // Non-printable border at edge of paper sheet
  14362. $ax1 = $b;
  14363. $bx = $this->page_box['outer_width_LR'] - $m;
  14364. $ax = max($ax1, $bx - $l);
  14365. $cx1 = $this->w - $b;
  14366. $dx = $this->w - $this->page_box['outer_width_LR'] + $m;
  14367. $cx = min($cx1, $dx + $l);
  14368. $ay1 = $b;
  14369. $by = $this->page_box['outer_width_TB'] - $m;
  14370. $ay = max($ay1, $by - $l);
  14371. $cy1 = $this->h - $b;
  14372. $dy = $this->h - $this->page_box['outer_width_TB'] + $m;
  14373. $cy = min($cy1, $dy + $l);
  14374. $this->Line($ax, $this->page_box['outer_width_TB'], $bx, $this->page_box['outer_width_TB']);
  14375. $this->Line($cx, $this->page_box['outer_width_TB'], $dx, $this->page_box['outer_width_TB']);
  14376. $this->Line($ax, $this->h - $this->page_box['outer_width_TB'], $bx, $this->h - $this->page_box['outer_width_TB']);
  14377. $this->Line($cx, $this->h - $this->page_box['outer_width_TB'], $dx, $this->h - $this->page_box['outer_width_TB']);
  14378. $this->Line($this->page_box['outer_width_LR'], $ay, $this->page_box['outer_width_LR'], $by);
  14379. $this->Line($this->page_box['outer_width_LR'], $cy, $this->page_box['outer_width_LR'], $dy);
  14380. $this->Line($this->w - $this->page_box['outer_width_LR'], $ay, $this->w - $this->page_box['outer_width_LR'], $by);
  14381. $this->Line($this->w - $this->page_box['outer_width_LR'], $cy, $this->w - $this->page_box['outer_width_LR'], $dy);
  14382. if ($this->printers_info) {
  14383. $hd = date('Y-m-d H:i') . ' Page ' . $this->page . ' of {nb}';
  14384. $this->SetTColor($this->ConvertColor(0));
  14385. $this->SetFont('arial', '', 7.5, true, true);
  14386. $this->x = $this->page_box['outer_width_LR'] + 1.5;
  14387. $this->y = 1;
  14388. $this->Cell($headerpgwidth, $this->FontSize, $hd, 0, 0, 'L', 0, '', 0, 0, 0, 'M');
  14389. $this->SetFont($this->default_font, '', $this->original_default_font_size);
  14390. }
  14391. }
  14392. if ($this->show_marks == 'CROSS' || $this->show_marks == 'CROPCROSS') {
  14393. $this->SetLineWidth(0.1); // = 0.1 mm
  14394. $this->SetDColor($this->ConvertColor(0));
  14395. $l = 14 / 2; // longer length of the cross line (half)
  14396. $w = 6 / 2; // shorter width of the cross line (half)
  14397. $r = 1.2; // radius of circle
  14398. $m = $this->crossMarkMargin; // Distance of cross mark from margin
  14399. $x1 = $this->page_box['outer_width_LR'] - $m;
  14400. $x2 = $this->w - $this->page_box['outer_width_LR'] + $m;
  14401. $y1 = $this->page_box['outer_width_TB'] - $m;
  14402. $y2 = $this->h - $this->page_box['outer_width_TB'] + $m;
  14403. // Left
  14404. $this->Circle($x1, $this->h / 2, $r, 'S');
  14405. $this->Line($x1 - $w, $this->h / 2, $x1 + $w, $this->h / 2);
  14406. $this->Line($x1, $this->h / 2 - $l, $x1, $this->h / 2 + $l);
  14407. // Right
  14408. $this->Circle($x2, $this->h / 2, $r, 'S');
  14409. $this->Line($x2 - $w, $this->h / 2, $x2 + $w, $this->h / 2);
  14410. $this->Line($x2, $this->h / 2 - $l, $x2, $this->h / 2 + $l);
  14411. // Top
  14412. $this->Circle($this->w / 2, $y1, $r, 'S');
  14413. $this->Line($this->w / 2, $y1 - $w, $this->w / 2, $y1 + $w);
  14414. $this->Line($this->w / 2 - $l, $y1, $this->w / 2 + $l, $y1);
  14415. // Bottom
  14416. $this->Circle($this->w / 2, $y2, $r, 'S');
  14417. $this->Line($this->w / 2, $y2 - $w, $this->w / 2, $y2 + $w);
  14418. $this->Line($this->w / 2 - $l, $y2, $this->w / 2 + $l, $y2);
  14419. }
  14420. /* -- END CSS-PAGE -- */
  14421. // mPDF 6
  14422. // If @page set non-HTML headers/footers named, they were not read until later in the HTML code - so now set them
  14423. if ($this->page == 1) {
  14424. if ($this->firstPageBoxHeader) {
  14425. if (isset($this->pageHTMLheaders[$this->firstPageBoxHeader])) {
  14426. $this->HTMLHeader = $this->pageHTMLheaders[$this->firstPageBoxHeader];
  14427. }
  14428. $this->Header();
  14429. }
  14430. if ($this->firstPageBoxFooter) {
  14431. if (isset($this->pageHTMLfooters[$this->firstPageBoxFooter])) {
  14432. $this->HTMLFooter = $this->pageHTMLfooters[$this->firstPageBoxFooter];
  14433. }
  14434. }
  14435. $this->firstPageBoxHeader = '';
  14436. $this->firstPageBoxFooter = '';
  14437. }
  14438. if (($this->mirrorMargins && ($this->page % 2 == 0) && $this->HTMLFooterE) || ($this->mirrorMargins && ($this->page % 2 == 1) && $this->HTMLFooter) || (!$this->mirrorMargins && $this->HTMLFooter)) {
  14439. $this->writeHTMLFooters();
  14440. }
  14441. /* -- WATERMARK -- */
  14442. if (($this->watermarkText) && ($this->showWatermarkText)) {
  14443. $this->watermark($this->watermarkText, 45, 120, $this->watermarkTextAlpha); // Watermark text
  14444. }
  14445. if (($this->watermarkImage) && ($this->showWatermarkImage)) {
  14446. $this->watermarkImg($this->watermarkImage, $this->watermarkImageAlpha); // Watermark image
  14447. }
  14448. /* -- END WATERMARK -- */
  14449. }
  14450. /* -- HYPHENATION -- */
  14451. ///////////////////
  14452. ///////////////////
  14453. // HYPHENATION
  14454. ///////////////////
  14455. ///////////////////
  14456. // Word hyphenation
  14457. // mPDF 6
  14458. function hyphenateWord($word, $currptr)
  14459. {
  14460. // Do everything inside this function in utf-8
  14461. // Don't hyphenate web addresses
  14462. if (preg_match('/^(http:|www\.)/', $word)) {
  14463. return -1;
  14464. }
  14465. $ptr = -1;
  14466. // Get dictionary
  14467. if (!$this->loadedSHYdictionary) {
  14468. if (file_exists(_MPDF_PATH . 'patterns/dictionary.txt')) {
  14469. $this->SHYdictionary = file(_MPDF_PATH . 'patterns/dictionary.txt', FILE_SKIP_EMPTY_LINES);
  14470. foreach ($this->SHYdictionary as $entry) {
  14471. $entry = trim($entry);
  14472. $poss = array();
  14473. $offset = 0;
  14474. $p = true;
  14475. $wl = mb_strlen($entry, 'UTF-8');
  14476. while ($offset < $wl) {
  14477. $p = mb_strpos($entry, '/', $offset, 'UTF-8');
  14478. if ($p !== false) {
  14479. $poss[] = $p - count($poss);
  14480. } else {
  14481. break;
  14482. }
  14483. $offset = $p + 1;
  14484. }
  14485. if (count($poss)) {
  14486. $this->SHYdictionaryWords[str_replace('/', '', mb_strtolower($entry))] = $poss;
  14487. }
  14488. }
  14489. }
  14490. $this->loadedSHYdictionary = true;
  14491. }
  14492. if (!in_array($this->SHYlang, $this->SHYlanguages)) {
  14493. return -1;
  14494. }
  14495. // If no pattern loaded or not the best one
  14496. if (count($this->SHYpatterns) < 1 || ($this->loadedSHYpatterns && $this->loadedSHYpatterns != $this->SHYlang)) {
  14497. include(_MPDF_PATH . "patterns/" . $this->SHYlang . ".php");
  14498. $patterns = explode(' ', $patterns);
  14499. $new_patterns = array();
  14500. for ($i = 0; $i < count($patterns); $i++) {
  14501. $value = $patterns[$i];
  14502. $new_patterns[preg_replace('/[0-9]/', '', $value)] = $value;
  14503. }
  14504. $this->SHYpatterns = $new_patterns;
  14505. $this->loadedSHYpatterns = $this->SHYlang;
  14506. }
  14507. if ($this->usingCoreFont) {
  14508. $word = mb_convert_encoding($word, 'UTF-8', $this->mb_enc);
  14509. }
  14510. $prepre = '';
  14511. $postpost = '';
  14512. $startpunctuation = "\xc2\xab\xc2\xbf\xe2\x80\x98\xe2\x80\x9b\xe2\x80\x9c\xe2\x80\x9f";
  14513. $endpunctuation = "\xe2\x80\x9e\xe2\x80\x9d\xe2\x80\x9a\xe2\x80\x99\xc2\xbb";
  14514. if (preg_match('/^(["\'' . $startpunctuation . '])+(.{' . $this->SHYcharmin . ',})$/u', $word, $m)) {
  14515. $prepre = $m[1];
  14516. $word = $m[2];
  14517. }
  14518. if (preg_match('/^(.{' . $this->SHYcharmin . ',})([\'\.,;:!?"' . $endpunctuation . ']+)$/u', $word, $m)) {
  14519. $word = $m[1];
  14520. $postpost = $m[2];
  14521. }
  14522. if (mb_strlen($word, 'UTF-8') < $this->SHYcharmin) {
  14523. return -1;
  14524. }
  14525. $success = false;
  14526. $preprelen = mb_strlen($prepre);
  14527. if (isset($this->SHYdictionaryWords[mb_strtolower($word)])) {
  14528. foreach ($this->SHYdictionaryWords[mb_strtolower($word)] AS $i) {
  14529. if (($i + $preprelen) >= $currptr) {
  14530. break;
  14531. }
  14532. $ptr = $i + $preprelen;
  14533. $success = true;
  14534. }
  14535. }
  14536. if (!$success) {
  14537. $text_word = '_' . $word . '_';
  14538. $word_length = mb_strlen($text_word, 'UTF-8');
  14539. $text_word = mb_strtolower($text_word, 'UTF-8');
  14540. $hyphenated_word = array();
  14541. $numb3rs = array('0' => true, '1' => true, '2' => true, '3' => true, '4' => true, '5' => true, '6' => true, '7' => true, '8' => true, '9' => true);
  14542. for ($position = 0; $position <= ($word_length - $this->SHYcharmin); $position++) {
  14543. $maxwins = min(($word_length - $position), $this->SHYcharmax);
  14544. for ($win = $this->SHYcharmin; $win <= $maxwins; $win++) {
  14545. if (isset($this->SHYpatterns[mb_substr($text_word, $position, $win, 'UTF-8')])) {
  14546. $pattern = $this->SHYpatterns[mb_substr($text_word, $position, $win, 'UTF-8')];
  14547. $digits = 1;
  14548. $pattern_length = mb_strlen($pattern, 'UTF-8');
  14549. for ($i = 0; $i < $pattern_length; $i++) {
  14550. $char = $pattern[$i];
  14551. if (isset($numb3rs[$char])) {
  14552. $zero = ($i == 0) ? $position - 1 : $position + $i - $digits;
  14553. if (!isset($hyphenated_word[$zero]) || $hyphenated_word[$zero] != $char)
  14554. $hyphenated_word[$zero] = $char;
  14555. $digits++;
  14556. }
  14557. }
  14558. }
  14559. }
  14560. }
  14561. for ($i = $this->SHYleftmin; $i <= (mb_strlen($word, 'UTF-8') - $this->SHYrightmin); $i++) {
  14562. if (isset($hyphenated_word[$i]) && $hyphenated_word[$i] % 2 != 0) {
  14563. if (($i + $preprelen) > $currptr) {
  14564. break;
  14565. }
  14566. $ptr = $i + $preprelen;
  14567. }
  14568. }
  14569. }
  14570. return $ptr;
  14571. }
  14572. /* -- END HYPHENATION -- */
  14573. /* -- HTML-CSS -- */
  14574. ///////////////////
  14575. /// HTML parser ///
  14576. ///////////////////
  14577. function WriteHTML($html, $sub = 0, $init = true, $close = true)
  14578. {
  14579. // $sub - 0 = default; 1=headerCSS only; 2=HTML body (parts) only; 3 - HTML parses only
  14580. // 4 - writes HTML headers/Fixed pos DIVs - stores in buffer - for single page only
  14581. // $close - if false Leaves buffers etc. in current state, so that it can continue a block etc.
  14582. // $init - Clears and sets buffers to Top level block etc.
  14583. /* Check $html is an integer, float, string, boolean or class with __toString(), otherwise throw exception */
  14584. if (is_scalar($html) === false) {
  14585. if(!is_object($html) || ! method_exists($html, '__toString')) {
  14586. throw new MpdfException('WriteHTML() requires $html be an integer, float, string, boolean or an object with the __toString() magic method.');
  14587. }
  14588. }
  14589. /* Cast $html as a string */
  14590. $html = (string) $html;
  14591. if ($this->progressBar) {
  14592. $this->UpdateProgressBar(1, 0, 'Parsing CSS & Headers');
  14593. } // *PROGRESS-BAR*
  14594. if ($init) {
  14595. $this->headerbuffer = '';
  14596. $this->textbuffer = array();
  14597. $this->fixedPosBlockSave = array();
  14598. }
  14599. if ($sub == 1) {
  14600. $html = '<style> ' . $html . ' </style>';
  14601. } // stylesheet only
  14602. if ($this->allow_charset_conversion) {
  14603. if ($sub < 1) {
  14604. $this->ReadCharset($html);
  14605. }
  14606. if ($this->charset_in && $sub != 4) {
  14607. $success = iconv($this->charset_in, 'UTF-8//TRANSLIT', $html);
  14608. if ($success) {
  14609. $html = $success;
  14610. }
  14611. }
  14612. }
  14613. $html = $this->purify_utf8($html, false);
  14614. if ($init) {
  14615. $this->blklvl = 0;
  14616. $this->lastblocklevelchange = 0;
  14617. $this->blk = array();
  14618. $this->initialiseBlock($this->blk[0]);
  14619. $this->blk[0]['width'] = & $this->pgwidth;
  14620. $this->blk[0]['inner_width'] = & $this->pgwidth;
  14621. $this->blk[0]['blockContext'] = $this->blockContext;
  14622. }
  14623. $zproperties = array();
  14624. if ($sub < 2) {
  14625. $this->ReadMetaTags($html);
  14626. if (preg_match('/<base[^>]*href=["\']([^"\'>]*)["\']/i', $html, $m)) {
  14627. $this->SetBasePath($m[1]);
  14628. }
  14629. $html = $this->cssmgr->ReadCSS($html);
  14630. if ($this->autoLangToFont && !$this->usingCoreFont && preg_match('/<html [^>]*lang=[\'\"](.*?)[\'\"]/ism', $html, $m)) {
  14631. $html_lang = $m[1];
  14632. }
  14633. if (preg_match('/<html [^>]*dir=[\'\"]\s*rtl\s*[\'\"]/ism', $html)) {
  14634. $zproperties['DIRECTION'] = 'rtl';
  14635. }
  14636. // allow in-line CSS for body tag to be parsed // Get <body> tag inline CSS
  14637. if (preg_match('/<body([^>]*)>(.*?)<\/body>/ism', $html, $m) || preg_match('/<body([^>]*)>(.*)$/ism', $html, $m)) {
  14638. $html = $m[2];
  14639. // Changed to allow style="background: url('bg.jpg')"
  14640. if (preg_match('/style=[\"](.*?)[\"]/ism', $m[1], $mm) || preg_match('/style=[\'](.*?)[\']/ism', $m[1], $mm)) {
  14641. $zproperties = $this->cssmgr->readInlineCSS($mm[1]);
  14642. }
  14643. if (preg_match('/dir=[\'\"]\s*rtl\s*[\'\"]/ism', $m[1])) {
  14644. $zproperties['DIRECTION'] = 'rtl';
  14645. }
  14646. if (isset($html_lang) && $html_lang) {
  14647. $zproperties['LANG'] = $html_lang;
  14648. }
  14649. if ($this->autoLangToFont && !$this->onlyCoreFonts && preg_match('/lang=[\'\"](.*?)[\'\"]/ism', $m[1], $mm)) {
  14650. $zproperties['LANG'] = $mm[1];
  14651. }
  14652. }
  14653. }
  14654. $properties = $this->cssmgr->MergeCSS('BLOCK', 'BODY', '');
  14655. if ($zproperties) {
  14656. $properties = $this->cssmgr->array_merge_recursive_unique($properties, $zproperties);
  14657. }
  14658. if (isset($properties['DIRECTION']) && $properties['DIRECTION']) {
  14659. $this->cssmgr->CSS['BODY']['DIRECTION'] = $properties['DIRECTION'];
  14660. }
  14661. if (!isset($this->cssmgr->CSS['BODY']['DIRECTION'])) {
  14662. $this->cssmgr->CSS['BODY']['DIRECTION'] = $this->directionality;
  14663. } else {
  14664. $this->SetDirectionality($this->cssmgr->CSS['BODY']['DIRECTION']);
  14665. }
  14666. $this->setCSS($properties, '', 'BODY');
  14667. $this->blk[0]['InlineProperties'] = $this->saveInlineProperties();
  14668. if ($sub == 1) {
  14669. return '';
  14670. }
  14671. if (!isset($this->cssmgr->CSS['BODY'])) {
  14672. $this->cssmgr->CSS['BODY'] = array();
  14673. }
  14674. /* -- BACKGROUNDS -- */
  14675. if (isset($properties['BACKGROUND-GRADIENT'])) {
  14676. $this->bodyBackgroundGradient = $properties['BACKGROUND-GRADIENT'];
  14677. }
  14678. if (isset($properties['BACKGROUND-IMAGE']) && $properties['BACKGROUND-IMAGE']) {
  14679. $ret = $this->SetBackground($properties, $this->pgwidth);
  14680. if ($ret) {
  14681. $this->bodyBackgroundImage = $ret;
  14682. }
  14683. }
  14684. /* -- END BACKGROUNDS -- */
  14685. /* -- CSS-PAGE -- */
  14686. // If page-box is set
  14687. if ($this->state == 0 && ((isset($this->cssmgr->CSS['@PAGE']) && $this->cssmgr->CSS['@PAGE']) || (isset($this->cssmgr->CSS['@PAGE>>PSEUDO>>FIRST']) && $this->cssmgr->CSS['@PAGE>>PSEUDO>>FIRST']))) { // mPDF 5.7.3
  14688. $this->page_box['current'] = '';
  14689. $this->page_box['using'] = true;
  14690. list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', false, 'O');
  14691. $this->DefOrientation = $this->CurOrientation = $pborientation;
  14692. $this->orig_lMargin = $this->DeflMargin = $pbmgl;
  14693. $this->orig_rMargin = $this->DefrMargin = $pbmgr;
  14694. $this->orig_tMargin = $this->tMargin = $pbmgt;
  14695. $this->orig_bMargin = $this->bMargin = $pbmgb;
  14696. $this->orig_hMargin = $this->margin_header = $pbmgh;
  14697. $this->orig_fMargin = $this->margin_footer = $pbmgf;
  14698. list($pborientation, $pbmgl, $pbmgr, $pbmgt, $pbmgb, $pbmgh, $pbmgf, $hname, $fname, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat) = $this->SetPagedMediaCSS('', true, 'O'); // first page
  14699. $this->show_marks = $marks;
  14700. if ($hname)
  14701. $this->firstPageBoxHeader = $hname;
  14702. if ($fname)
  14703. $this->firstPageBoxFooter = $fname;
  14704. }
  14705. /* -- END CSS-PAGE -- */
  14706. $parseonly = false;
  14707. $this->bufferoutput = false;
  14708. if ($sub == 3) {
  14709. $parseonly = true;
  14710. // Close any open block tags
  14711. $arr = array();
  14712. $ai = 0;
  14713. for ($b = $this->blklvl; $b > 0; $b--) {
  14714. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  14715. }
  14716. // Output any text left in buffer
  14717. if (count($this->textbuffer)) {
  14718. $this->printbuffer($this->textbuffer);
  14719. }
  14720. $this->textbuffer = array();
  14721. } elseif ($sub == 4) {
  14722. // Close any open block tags
  14723. $arr = array();
  14724. $ai = 0;
  14725. for ($b = $this->blklvl; $b > 0; $b--) {
  14726. $this->tag->CloseTag($this->blk[$b]['tag'], $arr, $ai);
  14727. }
  14728. // Output any text left in buffer
  14729. if (count($this->textbuffer)) {
  14730. $this->printbuffer($this->textbuffer);
  14731. }
  14732. $this->bufferoutput = true;
  14733. $this->textbuffer = array();
  14734. $this->headerbuffer = '';
  14735. $properties = $this->cssmgr->MergeCSS('BLOCK', 'BODY', '');
  14736. $this->setCSS($properties, '', 'BODY');
  14737. }
  14738. mb_internal_encoding('UTF-8');
  14739. $html = $this->AdjustHTML($html, $this->tabSpaces); //Try to make HTML look more like XHTML
  14740. if ($this->autoScriptToLang) {
  14741. $html = $this->markScriptToLang($html);
  14742. }
  14743. preg_match_all('/<htmlpageheader([^>]*)>(.*?)<\/htmlpageheader>/si', $html, $h);
  14744. for ($i = 0; $i < count($h[1]); $i++) {
  14745. if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $h[1][$i], $n)) {
  14746. $this->pageHTMLheaders[$n[1]]['html'] = $h[2][$i];
  14747. $this->pageHTMLheaders[$n[1]]['h'] = $this->_gethtmlheight($h[2][$i]);
  14748. }
  14749. }
  14750. preg_match_all('/<htmlpagefooter([^>]*)>(.*?)<\/htmlpagefooter>/si', $html, $f);
  14751. for ($i = 0; $i < count($f[1]); $i++) {
  14752. if (preg_match('/name=[\'|\"](.*?)[\'|\"]/', $f[1][$i], $n)) {
  14753. $this->pageHTMLfooters[$n[1]]['html'] = $f[2][$i];
  14754. $this->pageHTMLfooters[$n[1]]['h'] = $this->_gethtmlheight($f[2][$i]);
  14755. }
  14756. }
  14757. $html = preg_replace('/<htmlpageheader.*?<\/htmlpageheader>/si', '', $html);
  14758. $html = preg_replace('/<htmlpagefooter.*?<\/htmlpagefooter>/si', '', $html);
  14759. if ($this->state == 0 && $sub != 1 && $sub != 3 && $sub != 4) {
  14760. $this->AddPage($this->CurOrientation);
  14761. }
  14762. if (isset($hname) && preg_match('/^html_(.*)$/i', $hname, $n))
  14763. $this->SetHTMLHeader($this->pageHTMLheaders[$n[1]], 'O', true);
  14764. if (isset($fname) && preg_match('/^html_(.*)$/i', $fname, $n))
  14765. $this->SetHTMLFooter($this->pageHTMLfooters[$n[1]], 'O');
  14766. $html = str_replace('<?', '< ', $html); //Fix '<?XML' bug from HTML code generated by MS Word
  14767. $this->checkSIP = false;
  14768. $this->checkSMP = false;
  14769. $this->checkCJK = false;
  14770. if ($this->onlyCoreFonts) {
  14771. $html = $this->SubstituteChars($html);
  14772. } else {
  14773. if (preg_match("/([" . $this->pregRTLchars . "])/u", $html)) {
  14774. $this->biDirectional = true;
  14775. } // *OTL*
  14776. if (preg_match("/([\x{20000}-\x{2FFFF}])/u", $html)) {
  14777. $this->checkSIP = true;
  14778. }
  14779. if (preg_match("/([\x{10000}-\x{1FFFF}])/u", $html)) {
  14780. $this->checkSMP = true;
  14781. }
  14782. /* -- CJK-FONTS -- */
  14783. if (preg_match("/([" . $this->pregCJKchars . "])/u", $html)) {
  14784. $this->checkCJK = true;
  14785. }
  14786. /* -- END CJK-FONTS -- */
  14787. }
  14788. // Don't allow non-breaking spaces that are converted to substituted chars or will break anyway and mess up table width calc.
  14789. $html = str_replace('<tta>160</tta>', chr(32), $html);
  14790. $html = str_replace('</tta><tta>', '|', $html);
  14791. $html = str_replace('</tts><tts>', '|', $html);
  14792. $html = str_replace('</ttz><ttz>', '|', $html);
  14793. //Add new supported tags in the DisableTags function
  14794. $html = strip_tags($html, $this->enabledtags); //remove all unsupported tags, but the ones inside the 'enabledtags' string
  14795. //Explode the string in order to parse the HTML code
  14796. $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  14797. // ? more accurate regexp that allows e.g. <a name="Silly <name>">
  14798. // if changing - also change in fn.SubstituteChars()
  14799. // $a = preg_split ('/<((?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  14800. if ($this->mb_enc) {
  14801. mb_internal_encoding($this->mb_enc);
  14802. }
  14803. $pbc = 0;
  14804. if ($this->progressBar) {
  14805. $this->UpdateProgressBar(1, 0);
  14806. } // *PROGRESS-BAR*
  14807. $this->subPos = -1;
  14808. $cnt = count($a);
  14809. for ($i = 0; $i < $cnt; $i++) {
  14810. $e = $a[$i];
  14811. if ($i % 2 == 0) {
  14812. //TEXT
  14813. if ($this->blk[$this->blklvl]['hide']) {
  14814. continue;
  14815. }
  14816. if ($this->inlineDisplayOff) {
  14817. continue;
  14818. }
  14819. if ($this->inMeter) {
  14820. continue;
  14821. }
  14822. if ($this->inFixedPosBlock) {
  14823. $this->fixedPosBlock .= $e;
  14824. continue;
  14825. } // *CSS-POSITION*
  14826. if (strlen($e) == 0) {
  14827. continue;
  14828. }
  14829. if ($this->ignorefollowingspaces && !$this->ispre) {
  14830. if (strlen(ltrim($e)) == 0) {
  14831. continue;
  14832. }
  14833. if ($this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats' && substr($e, 0, 1) == ' ') {
  14834. $this->ignorefollowingspaces = false;
  14835. $e = ltrim($e);
  14836. }
  14837. }
  14838. $this->OTLdata = NULL; // mPDF 5.7.1
  14839. $e = strcode2utf($e);
  14840. $e = $this->lesser_entity_decode($e);
  14841. if ($this->usingCoreFont) {
  14842. // If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
  14843. if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->subPos < $i && !$this->specialcontent) {
  14844. $cnt += $this->SubstituteCharsNonCore($a, $i, $e);
  14845. }
  14846. // CONVERT ENCODING
  14847. $e = mb_convert_encoding($e, $this->mb_enc, 'UTF-8');
  14848. if ($this->textvar & FT_UPPERCASE) {
  14849. $e = mb_strtoupper($e, $this->mb_enc);
  14850. } // mPDF 5.7.1
  14851. elseif ($this->textvar & FT_LOWERCASE) {
  14852. $e = mb_strtolower($e, $this->mb_enc);
  14853. } // mPDF 5.7.1
  14854. elseif ($this->textvar & FT_CAPITALIZE) {
  14855. $e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
  14856. } // mPDF 5.7.1
  14857. } else {
  14858. if ($this->checkSIP && $this->CurrentFont['sipext'] && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
  14859. $cnt += $this->SubstituteCharsSIP($a, $i, $e);
  14860. }
  14861. if ($this->useSubstitutions && !$this->onlyCoreFonts && $this->CurrentFont['type'] != 'Type0' && $this->subPos < $i && (!$this->specialcontent || !$this->useActiveForms)) {
  14862. $cnt += $this->SubstituteCharsMB($a, $i, $e);
  14863. }
  14864. if ($this->textvar & FT_UPPERCASE) {
  14865. $e = mb_strtoupper($e, $this->mb_enc);
  14866. } elseif ($this->textvar & FT_LOWERCASE) {
  14867. $e = mb_strtolower($e, $this->mb_enc);
  14868. } elseif ($this->textvar & FT_CAPITALIZE) {
  14869. $e = mb_convert_case($e, MB_CASE_TITLE, "UTF-8");
  14870. }
  14871. /* -- OTL -- */
  14872. // Use OTL OpenType Table Layout - GSUB & GPOS
  14873. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL'] && (!$this->specialcontent || !$this->useActiveForms)) {
  14874. $e = $this->otl->applyOTL($e, $this->CurrentFont['useOTL']);
  14875. $this->OTLdata = $this->otl->OTLdata;
  14876. $this->otl->removeChar($e, $this->OTLdata, "\xef\xbb\xbf"); // Remove ZWNBSP (also Byte order mark FEFF)
  14877. }
  14878. /* -- END OTL -- */ else { // *OTL*
  14879. // removes U+200E/U+200F LTR and RTL mark and U+200C/U+200D Zero-width Joiner and Non-joiner
  14880. $e = preg_replace("/[\xe2\x80\x8c\xe2\x80\x8d\xe2\x80\x8e\xe2\x80\x8f]/u", '', $e);
  14881. $e = preg_replace("/[\xef\xbb\xbf]/u", '', $e); // Remove ZWNBSP (also Byte order mark FEFF)
  14882. } // *OTL*
  14883. }
  14884. if (($this->tts) || ($this->ttz) || ($this->tta)) {
  14885. $es = explode('|', $e);
  14886. $e = '';
  14887. foreach ($es AS $val) {
  14888. $e .= chr($val);
  14889. }
  14890. }
  14891. // FORM ELEMENTS
  14892. if ($this->specialcontent) {
  14893. /* -- FORMS -- */
  14894. //SELECT tag (form element)
  14895. if ($this->specialcontent == "type=select") {
  14896. $e = ltrim($e);
  14897. if (!empty($this->OTLdata)) {
  14898. $this->otl->trimOTLdata($this->OTLdata, true, false);
  14899. } // *OTL*
  14900. $stringwidth = $this->GetStringWidth($e);
  14901. if (!isset($this->selectoption['MAXWIDTH']) || $stringwidth > $this->selectoption['MAXWIDTH']) {
  14902. $this->selectoption['MAXWIDTH'] = $stringwidth;
  14903. }
  14904. if (!isset($this->selectoption['SELECTED']) || $this->selectoption['SELECTED'] == '') {
  14905. $this->selectoption['SELECTED'] = $e;
  14906. if (!empty($this->OTLdata)) {
  14907. $this->selectoption['SELECTED-OTLDATA'] = $this->OTLdata;
  14908. } // *OTL*
  14909. }
  14910. // Active Forms
  14911. if (isset($this->selectoption['ACTIVE']) && $this->selectoption['ACTIVE']) {
  14912. $this->selectoption['ITEMS'][] = array('exportValue' => $this->selectoption['currentVAL'], 'content' => $e, 'selected' => $this->selectoption['currentSEL']);
  14913. }
  14914. $this->OTLdata = array();
  14915. }
  14916. // TEXTAREA
  14917. else {
  14918. $objattr = unserialize($this->specialcontent);
  14919. $objattr['text'] = $e;
  14920. $objattr['OTLdata'] = $this->OTLdata;
  14921. $this->OTLdata = array();
  14922. $te = "\xbb\xa4\xactype=textarea,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
  14923. if ($this->tdbegin) {
  14924. $this->_saveCellTextBuffer($te, $this->HREF);
  14925. } else {
  14926. $this->_saveTextBuffer($te, $this->HREF);
  14927. }
  14928. }
  14929. /* -- END FORMS -- */
  14930. }
  14931. // TABLE
  14932. elseif ($this->tableLevel) {
  14933. /* -- TABLES -- */
  14934. if ($this->tdbegin) {
  14935. if (($this->ignorefollowingspaces) && !$this->ispre) {
  14936. $e = ltrim($e);
  14937. if (!empty($this->OTLdata)) {
  14938. $this->otl->trimOTLdata($this->OTLdata, true, false);
  14939. } // *OTL*
  14940. }
  14941. if ($e || $e === '0') {
  14942. if ($this->blockjustfinished && $this->cell[$this->row][$this->col]['s'] > 0) {
  14943. $this->_saveCellTextBuffer("\n");
  14944. if (!isset($this->cell[$this->row][$this->col]['maxs'])) {
  14945. $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
  14946. } elseif ($this->cell[$this->row][$this->col]['maxs'] < $this->cell[$this->row][$this->col]['s']) {
  14947. $this->cell[$this->row][$this->col]['maxs'] = $this->cell[$this->row][$this->col]['s'];
  14948. }
  14949. $this->cell[$this->row][$this->col]['s'] = 0; // reset
  14950. }
  14951. $this->blockjustfinished = false;
  14952. if (!isset($this->cell[$this->row][$this->col]['R']) || !$this->cell[$this->row][$this->col]['R']) {
  14953. if (isset($this->cell[$this->row][$this->col]['s'])) {
  14954. $this->cell[$this->row][$this->col]['s'] += $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
  14955. } else {
  14956. $this->cell[$this->row][$this->col]['s'] = $this->GetStringWidth($e, false, $this->OTLdata, $this->textvar);
  14957. }
  14958. if (!empty($this->spanborddet)) {
  14959. $this->cell[$this->row][$this->col]['s'] += (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0) + (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  14960. }
  14961. }
  14962. $this->_saveCellTextBuffer($e, $this->HREF);
  14963. if (substr($this->cell[$this->row][$this->col]['a'], 0, 1) == 'D') {
  14964. $dp = $this->decimal_align[substr($this->cell[$this->row][$this->col]['a'], 0, 2)];
  14965. $s = preg_split('/' . preg_quote($dp, '/') . '/', $e, 2); // ? needs to be /u if not core
  14966. $s0 = $this->GetStringWidth($s[0], false);
  14967. if (isset($s[1]) && $s[1]) {
  14968. $s1 = $this->GetStringWidth(($s[1] . $dp), false);
  14969. } else
  14970. $s1 = 0;
  14971. if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'])) {
  14972. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = $s0;
  14973. } else {
  14974. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0'] = max($s0, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs0']);
  14975. }
  14976. if (!isset($this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'])) {
  14977. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = $s1;
  14978. } else {
  14979. $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1'] = max($s1, $this->table[$this->tableLevel][$this->tbctr[$this->tableLevel]]['decimal_align'][$this->col]['maxs1']);
  14980. }
  14981. }
  14982. if ($this->tableLevel == 1 && $this->useGraphs) {
  14983. $this->graphs[$this->currentGraphId]['data'][$this->row][$this->col] = $e;
  14984. }
  14985. $this->nestedtablejustfinished = false;
  14986. $this->linebreakjustfinished = false;
  14987. }
  14988. }
  14989. /* -- END TABLES -- */
  14990. }
  14991. // ALL ELSE
  14992. else {
  14993. if ($this->ignorefollowingspaces && !$this->ispre) {
  14994. $e = ltrim($e);
  14995. if (!empty($this->OTLdata)) {
  14996. $this->otl->trimOTLdata($this->OTLdata, true, false);
  14997. } // *OTL*
  14998. }
  14999. if ($e || $e === '0')
  15000. $this->_saveTextBuffer($e, $this->HREF);
  15001. }
  15002. if ($e || $e === '0')
  15003. $this->ignorefollowingspaces = false; // mPDF 6
  15004. if (substr($e, -1, 1) == ' ' && !$this->ispre && $this->FontFamily != 'csymbol' && $this->FontFamily != 'czapfdingbats') {
  15005. $this->ignorefollowingspaces = true;
  15006. }
  15007. } else { // TAG **
  15008. if (isset($e[0]) && $e[0] == '/') {
  15009. /* -- PROGRESS-BAR -- */
  15010. if ($this->progressBar) { // 10% increments
  15011. if (intval($i * 10 / $cnt) != $pbc) {
  15012. $pbc = intval($i * 10 / $cnt);
  15013. $this->UpdateProgressBar(1, $pbc * 10, $tag);
  15014. }
  15015. }
  15016. /* -- END PROGRESS-BAR -- */
  15017. $endtag = trim(strtoupper(substr($e, 1)));
  15018. /* -- CSS-POSITION -- */
  15019. // mPDF 6
  15020. if ($this->inFixedPosBlock) {
  15021. if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
  15022. $this->fixedPosBlockDepth--;
  15023. }
  15024. if ($this->fixedPosBlockDepth == 0) {
  15025. $this->fixedPosBlockSave[] = array($this->fixedPosBlock, $this->fixedPosBlockBBox, $this->page);
  15026. $this->fixedPosBlock = '';
  15027. $this->inFixedPosBlock = false;
  15028. continue;
  15029. }
  15030. $this->fixedPosBlock .= '<' . $e . '>';
  15031. continue;
  15032. }
  15033. /* -- END CSS-POSITION -- */
  15034. // mPDF 6
  15035. // Correct for tags where HTML5 specifies optional end tags (see also OpenTag() )
  15036. if ($this->allow_html_optional_endtags && !$parseonly) {
  15037. if (isset($this->blk[$this->blklvl]['tag'])) {
  15038. $closed = false;
  15039. // li end tag may be omitted if there is no more content in the parent element
  15040. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'LI' && $endtag != 'LI' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  15041. $this->tag->CloseTag('LI', $a, $i);
  15042. $closed = true;
  15043. }
  15044. // dd end tag may be omitted if there is no more content in the parent element
  15045. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'DD' && $endtag != 'DD' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  15046. $this->tag->CloseTag('DD', $a, $i);
  15047. $closed = true;
  15048. }
  15049. // p end tag may be omitted if there is no more content in the parent element and the parent element is not an A element [??????]
  15050. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'P' && $endtag != 'P' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  15051. $this->tag->CloseTag('P', $a, $i);
  15052. $closed = true;
  15053. }
  15054. // option end tag may be omitted if there is no more content in the parent element
  15055. if (!$closed && $this->blk[$this->blklvl]['tag'] == 'OPTION' && $endtag != 'OPTION' && (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags))) {
  15056. $this->tag->CloseTag('OPTION', $a, $i);
  15057. $closed = true;
  15058. }
  15059. }
  15060. /* -- TABLES -- */
  15061. // Check for Table tags where HTML specifies optional end tags,
  15062. if ($endtag == 'TABLE') {
  15063. if ($this->lastoptionaltag == 'THEAD' || $this->lastoptionaltag == 'TBODY' || $this->lastoptionaltag == 'TFOOT') {
  15064. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  15065. }
  15066. if ($this->lastoptionaltag == 'TR') {
  15067. $this->tag->CloseTag('TR', $a, $i);
  15068. }
  15069. if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
  15070. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  15071. $this->tag->CloseTag('TR', $a, $i);
  15072. }
  15073. }
  15074. if ($endtag == 'THEAD' || $endtag == 'TBODY' || $endtag == 'TFOOT') {
  15075. if ($this->lastoptionaltag == 'TR') {
  15076. $this->tag->CloseTag('TR', $a, $i);
  15077. }
  15078. if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
  15079. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  15080. $this->tag->CloseTag('TR', $a, $i);
  15081. }
  15082. }
  15083. if ($endtag == 'TR') {
  15084. if ($this->lastoptionaltag == 'TD' || $this->lastoptionaltag == 'TH') {
  15085. $this->tag->CloseTag($this->lastoptionaltag, $a, $i);
  15086. }
  15087. }
  15088. /* -- END TABLES -- */
  15089. }
  15090. // mPDF 6
  15091. if ($this->blk[$this->blklvl]['hide']) {
  15092. if (in_array($endtag, $this->outerblocktags) || in_array($endtag, $this->innerblocktags)) {
  15093. unset($this->blk[$this->blklvl]);
  15094. $this->blklvl--;
  15095. }
  15096. continue;
  15097. }
  15098. // mPDF 6
  15099. $this->tag->CloseTag($endtag, $a, $i); // mPDF 6
  15100. } else { // OPENING TAG
  15101. if ($this->blk[$this->blklvl]['hide']) {
  15102. if (strpos($e, ' ')) {
  15103. $te = strtoupper(substr($e, 0, strpos($e, ' ')));
  15104. } else {
  15105. $te = strtoupper($e);
  15106. }
  15107. // mPDF 6
  15108. if ($te == 'THEAD' || $te == 'TBODY' || $te == 'TFOOT' || $te == 'TR' || $te == 'TD' || $te == 'TH') {
  15109. $this->lastoptionaltag = $te;
  15110. }
  15111. if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
  15112. $this->blklvl++;
  15113. $this->blk[$this->blklvl]['hide'] = true;
  15114. $this->blk[$this->blklvl]['tag'] = $te; // mPDF 6
  15115. }
  15116. continue;
  15117. }
  15118. /* -- CSS-POSITION -- */
  15119. if ($this->inFixedPosBlock) {
  15120. if (strpos($e, ' ')) {
  15121. $te = strtoupper(substr($e, 0, strpos($e, ' ')));
  15122. } else {
  15123. $te = strtoupper($e);
  15124. }
  15125. $this->fixedPosBlock .= '<' . $e . '>';
  15126. if (in_array($te, $this->outerblocktags) || in_array($te, $this->innerblocktags)) {
  15127. $this->fixedPosBlockDepth++;
  15128. }
  15129. continue;
  15130. }
  15131. /* -- END CSS-POSITION -- */
  15132. $regexp = '|=\'(.*?)\'|s'; // eliminate single quotes, if any
  15133. $e = preg_replace($regexp, "=\"\$1\"", $e);
  15134. // changes anykey=anyvalue to anykey="anyvalue" (only do this inside [some] tags)
  15135. if (substr($e, 0, 10) != 'pageheader' && substr($e, 0, 10) != 'pagefooter' && substr($e, 0, 12) != 'tocpagebreak' && substr($e, 0, 10) != 'indexentry' && substr($e, 0, 8) != 'tocentry') { // mPDF 6 (ZZZ99H)
  15136. $regexp = '| (\\w+?)=([^\\s>"]+)|si';
  15137. $e = preg_replace($regexp, " \$1=\"\$2\"", $e);
  15138. }
  15139. $e = preg_replace('/ (\\S+?)\s*=\s*"/i', " \\1=\"", $e);
  15140. //Fix path values, if needed
  15141. $orig_srcpath = '';
  15142. if ((stristr($e, "href=") !== false) or ( stristr($e, "src=") !== false)) {
  15143. $regexp = '/ (href|src)\s*=\s*"(.*?)"/i';
  15144. preg_match($regexp, $e, $auxiliararray);
  15145. if (isset($auxiliararray[2])) {
  15146. $path = $auxiliararray[2];
  15147. } else {
  15148. $path = '';
  15149. }
  15150. if (trim($path) != '' && !(stristr($e, "src=") !== false && substr($path, 0, 4) == 'var:') && substr($path, 0, 1) != '@') {
  15151. $path = htmlspecialchars_decode($path); // mPDF 5.7.4 URLs
  15152. $orig_srcpath = $path;
  15153. $this->GetFullPath($path);
  15154. $regexp = '/ (href|src)="(.*?)"/i';
  15155. $e = preg_replace($regexp, ' \\1="' . $path . '"', $e);
  15156. }
  15157. }//END of Fix path values
  15158. //Extract attributes
  15159. $contents = array();
  15160. $contents1 = array();
  15161. $contents2 = array();
  15162. // Changed to allow style="background: url('bg.jpg')"
  15163. // Changed to improve performance; maximum length of \S (attribute) = 16
  15164. // Increase allowed attribute name to 32 - cutting off "toc-even-header-name" etc.
  15165. preg_match_all('/\\S{1,32}=["][^"]*["]/', $e, $contents1);
  15166. preg_match_all('/\\S{1,32}=[\'][^\']*[\']/i', $e, $contents2);
  15167. $contents = array_merge($contents1, $contents2);
  15168. preg_match('/\\S+/', $e, $a2);
  15169. $tag = (isset($a2[0]) ? strtoupper($a2[0]) : '');
  15170. $attr = array();
  15171. if ($orig_srcpath) {
  15172. $attr['ORIG_SRC'] = $orig_srcpath;
  15173. }
  15174. if (!empty($contents)) {
  15175. foreach ($contents[0] as $v) {
  15176. // Changed to allow style="background: url('bg.jpg')"
  15177. if (preg_match('/^([^=]*)=["]?([^"]*)["]?$/', $v, $a3) || preg_match('/^([^=]*)=[\']?([^\']*)[\']?$/', $v, $a3)) {
  15178. if (strtoupper($a3[1]) == 'ID' || strtoupper($a3[1]) == 'CLASS') { // 4.2.013 Omits STYLE
  15179. $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
  15180. }
  15181. // includes header-style-right etc. used for <pageheader>
  15182. elseif (preg_match('/^(HEADER|FOOTER)-STYLE/i', $a3[1])) {
  15183. $attr[strtoupper($a3[1])] = trim(strtoupper($a3[2]));
  15184. } else {
  15185. $attr[strtoupper($a3[1])] = trim($a3[2]);
  15186. }
  15187. }
  15188. }
  15189. }
  15190. $this->tag->OpenTag($tag, $attr, $a, $i); // mPDF 6
  15191. /* -- CSS-POSITION -- */
  15192. if ($this->inFixedPosBlock) {
  15193. $this->fixedPosBlockBBox = array($tag, $attr, $this->x, $this->y);
  15194. $this->fixedPosBlock = '';
  15195. $this->fixedPosBlockDepth = 1;
  15196. }
  15197. /* -- END CSS-POSITION -- */
  15198. if (preg_match('/\/$/', $e)) {
  15199. $this->tag->CloseTag($tag, $a, $i);
  15200. }
  15201. }
  15202. } // end TAG
  15203. } //end of foreach($a as $i=>$e)
  15204. if ($close) {
  15205. // Close any open block tags
  15206. for ($b = $this->blklvl; $b > 0; $b--) {
  15207. $this->tag->CloseTag($this->blk[$b]['tag'], $a, $i);
  15208. }
  15209. // Output any text left in buffer
  15210. if (count($this->textbuffer) && !$parseonly) {
  15211. $this->printbuffer($this->textbuffer);
  15212. }
  15213. if (!$parseonly)
  15214. $this->textbuffer = array();
  15215. /* -- CSS-FLOAT -- */
  15216. // If ended with a float, need to move to end page
  15217. $currpos = $this->page * 1000 + $this->y;
  15218. if (isset($this->blk[$this->blklvl]['float_endpos']) && $this->blk[$this->blklvl]['float_endpos'] > $currpos) {
  15219. $old_page = $this->page;
  15220. $new_page = intval($this->blk[$this->blklvl]['float_endpos'] / 1000);
  15221. if ($old_page != $new_page) {
  15222. $s = $this->PrintPageBackgrounds();
  15223. // Writes after the marker so not overwritten later by page background etc.
  15224. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  15225. $this->pageBackgrounds = array();
  15226. $this->page = $new_page;
  15227. $this->ResetMargins();
  15228. $this->Reset();
  15229. $this->pageoutput[$this->page] = array();
  15230. }
  15231. $this->y = (($this->blk[$this->blklvl]['float_endpos'] * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
  15232. }
  15233. /* -- END CSS-FLOAT -- */
  15234. /* -- CSS-IMAGE-FLOAT -- */
  15235. $this->printfloatbuffer();
  15236. /* -- END CSS-IMAGE-FLOAT -- */
  15237. //Create Internal Links, if needed
  15238. if (!empty($this->internallink)) {
  15239. foreach ($this->internallink as $k => $v) {
  15240. if (strpos($k, "#") !== false) {
  15241. continue;
  15242. } //ignore
  15243. $ypos = $v['Y'];
  15244. $pagenum = $v['PAGE'];
  15245. $sharp = "#";
  15246. while (array_key_exists($sharp . $k, $this->internallink)) {
  15247. $internallink = $this->internallink[$sharp . $k];
  15248. $this->SetLink($internallink, $ypos, $pagenum);
  15249. $sharp .= "#";
  15250. }
  15251. }
  15252. }
  15253. $this->bufferoutput = false;
  15254. /* -- CSS-POSITION -- */
  15255. if (count($this->fixedPosBlockSave) && $sub != 4) {
  15256. foreach ($this->fixedPosBlockSave AS $fpbs) {
  15257. $old_page = $this->page;
  15258. $this->page = $fpbs[2];
  15259. $this->WriteFixedPosHTML($fpbs[0], 0, 0, 100, 100, 'auto', $fpbs[1]); // 0,0,10,10 are overwritten by bbox
  15260. $this->page = $old_page;
  15261. }
  15262. $this->fixedPosBlockSave = array();
  15263. }
  15264. /* -- END CSS-POSITION -- */
  15265. }
  15266. }
  15267. /* -- CSS-POSITION -- */
  15268. function WriteFixedPosHTML($html = '', $x, $y, $w, $h, $overflow = 'visible', $bounding = array())
  15269. {
  15270. // $overflow can be 'hidden', 'visible' or 'auto' - 'auto' causes autofit to size
  15271. // Annotations disabled - enabled in mPDF 5.0
  15272. // Links do work
  15273. // Will always go on current page (or start Page 1 if required)
  15274. // Probably INCOMPATIBLE WITH keep with table, columns etc.
  15275. // Called externally or interally via <div style="position: [fixed|absolute]">
  15276. // When used internally, $x $y $w $h and $overflow are all overridden by $bounding
  15277. $overflow = strtolower($overflow);
  15278. if ($this->state == 0) {
  15279. $this->AddPage($this->CurOrientation);
  15280. }
  15281. $save_y = $this->y;
  15282. $save_x = $this->x;
  15283. $this->fullImageHeight = $this->h;
  15284. $save_cols = false;
  15285. /* -- COLUMNS -- */
  15286. if ($this->ColActive) {
  15287. $save_cols = true;
  15288. $save_nbcol = $this->NbCol; // other values of gap and vAlign will not change by setting Columns off
  15289. $this->SetColumns(0);
  15290. }
  15291. /* -- END COLUMNS -- */
  15292. $save_annots = $this->title2annots; // *ANNOTATIONS*
  15293. $this->writingHTMLheader = true; // a FIX to stop pagebreaks etc.
  15294. $this->writingHTMLfooter = true;
  15295. $this->InFooter = true; // suppresses autopagebreaks
  15296. $save_bgs = $this->pageBackgrounds;
  15297. $checkinnerhtml = preg_replace('/\s/', '', $html);
  15298. if ($w > $this->w) {
  15299. $x = 0;
  15300. $w = $this->w;
  15301. }
  15302. if ($h > $this->h) {
  15303. $y = 0;
  15304. $h = $this->h;
  15305. }
  15306. if ($x > $this->w) {
  15307. $x = $this->w - $w;
  15308. }
  15309. if ($y > $this->h) {
  15310. $y = $this->h - $h;
  15311. }
  15312. if (!empty($bounding)) {
  15313. // $cont_ containing block = full physical page (position: absolute) or page inside margins (position: fixed)
  15314. // $bbox_ Bounding box is the <div> which is positioned absolutely/fixed
  15315. // top/left/right/bottom/width/height/background*/border*/padding*/margin* are taken from bounding
  15316. // font*[family/size/style/weight]/line-height/text*[align/decoration/transform/indent]/color are transferred to $inner
  15317. // as an enclosing <div> (after having checked ID/CLASS)
  15318. // $x, $y, $w, $h are inside of $bbox_ = containing box for $inner_
  15319. // $inner_ InnerHTML is the contents of that block to be output
  15320. $tag = $bounding[0];
  15321. $attr = $bounding[1];
  15322. $orig_x0 = $bounding[2];
  15323. $orig_y0 = $bounding[3];
  15324. // As in WriteHTML() initialising
  15325. $this->blklvl = 0;
  15326. $this->lastblocklevelchange = 0;
  15327. $this->blk = array();
  15328. $this->initialiseBlock($this->blk[0]);
  15329. $this->blk[0]['width'] = & $this->pgwidth;
  15330. $this->blk[0]['inner_width'] = & $this->pgwidth;
  15331. $this->blk[0]['blockContext'] = $this->blockContext;
  15332. $properties = $this->cssmgr->MergeCSS('BLOCK', 'BODY', '');
  15333. $this->setCSS($properties, '', 'BODY');
  15334. $this->blklvl = 1;
  15335. $this->initialiseBlock($this->blk[1]);
  15336. $this->blk[1]['tag'] = $tag;
  15337. $this->blk[1]['attr'] = $attr;
  15338. $this->Reset();
  15339. $p = $this->cssmgr->MergeCSS('BLOCK', $tag, $attr);
  15340. if (isset($p['ROTATE']) && ($p['ROTATE'] == 90 || $p['ROTATE'] == -90 || $p['ROTATE'] == 180)) {
  15341. $rotate = $p['ROTATE'];
  15342. } // mPDF 6
  15343. else {
  15344. $rotate = 0;
  15345. }
  15346. if (isset($p['OVERFLOW'])) {
  15347. $overflow = strtolower($p['OVERFLOW']);
  15348. }
  15349. if (strtolower($p['POSITION']) == 'fixed') {
  15350. $cont_w = $this->pgwidth; // $this->blk[0]['inner_width'];
  15351. $cont_h = $this->h - $this->tMargin - $this->bMargin;
  15352. $cont_x = $this->lMargin;
  15353. $cont_y = $this->tMargin;
  15354. } else {
  15355. $cont_w = $this->w; // ABSOLUTE;
  15356. $cont_h = $this->h;
  15357. $cont_x = 0;
  15358. $cont_y = 0;
  15359. }
  15360. // Pass on in-line properties to the innerhtml
  15361. $css = '';
  15362. if (isset($p['TEXT-ALIGN'])) {
  15363. $css .= 'text-align: ' . strtolower($p['TEXT-ALIGN']) . '; ';
  15364. }
  15365. if (isset($p['TEXT-TRANSFORM'])) {
  15366. $css .= 'text-transform: ' . strtolower($p['TEXT-TRANSFORM']) . '; ';
  15367. }
  15368. if (isset($p['TEXT-INDENT'])) {
  15369. $css .= 'text-indent: ' . strtolower($p['TEXT-INDENT']) . '; ';
  15370. }
  15371. if (isset($p['TEXT-DECORATION'])) {
  15372. $css .= 'text-decoration: ' . strtolower($p['TEXT-DECORATION']) . '; ';
  15373. }
  15374. if (isset($p['FONT-FAMILY'])) {
  15375. $css .= 'font-family: ' . strtolower($p['FONT-FAMILY']) . '; ';
  15376. }
  15377. if (isset($p['FONT-STYLE'])) {
  15378. $css .= 'font-style: ' . strtolower($p['FONT-STYLE']) . '; ';
  15379. }
  15380. if (isset($p['FONT-WEIGHT'])) {
  15381. $css .= 'font-weight: ' . strtolower($p['FONT-WEIGHT']) . '; ';
  15382. }
  15383. if (isset($p['FONT-SIZE'])) {
  15384. $css .= 'font-size: ' . strtolower($p['FONT-SIZE']) . '; ';
  15385. }
  15386. if (isset($p['LINE-HEIGHT'])) {
  15387. $css .= 'line-height: ' . strtolower($p['LINE-HEIGHT']) . '; ';
  15388. }
  15389. if (isset($p['TEXT-SHADOW'])) {
  15390. $css .= 'text-shadow: ' . strtolower($p['TEXT-SHADOW']) . '; ';
  15391. }
  15392. if (isset($p['LETTER-SPACING'])) {
  15393. $css .= 'letter-spacing: ' . strtolower($p['LETTER-SPACING']) . '; ';
  15394. }
  15395. // mPDF 6
  15396. if (isset($p['FONT-VARIANT-POSITION'])) {
  15397. $css .= 'font-variant-position: ' . strtolower($p['FONT-VARIANT-POSITION']) . '; ';
  15398. }
  15399. if (isset($p['FONT-VARIANT-CAPS'])) {
  15400. $css .= 'font-variant-caps: ' . strtolower($p['FONT-VARIANT-CAPS']) . '; ';
  15401. }
  15402. if (isset($p['FONT-VARIANT-LIGATURES'])) {
  15403. $css .= 'font-variant-ligatures: ' . strtolower($p['FONT-VARIANT-LIGATURES']) . '; ';
  15404. }
  15405. if (isset($p['FONT-VARIANT-NUMERIC'])) {
  15406. $css .= 'font-variant-numeric: ' . strtolower($p['FONT-VARIANT-NUMERIC']) . '; ';
  15407. }
  15408. if (isset($p['FONT-VARIANT-ALTERNATES'])) {
  15409. $css .= 'font-variant-alternates: ' . strtolower($p['FONT-VARIANT-ALTERNATES']) . '; ';
  15410. }
  15411. if (isset($p['FONT-FEATURE-SETTINGS'])) {
  15412. $css .= 'font-feature-settings: ' . strtolower($p['FONT-FEATURE-SETTINGS']) . '; ';
  15413. }
  15414. if (isset($p['FONT-LANGUAGE-OVERRIDE'])) {
  15415. $css .= 'font-language-override: ' . strtolower($p['FONT-LANGUAGE-OVERRIDE']) . '; ';
  15416. }
  15417. if (isset($p['FONT-KERNING'])) {
  15418. $css .= 'font-kerning: ' . strtolower($p['FONT-KERNING']) . '; ';
  15419. }
  15420. if (isset($p['COLOR'])) {
  15421. $css .= 'color: ' . strtolower($p['COLOR']) . '; ';
  15422. }
  15423. if (isset($p['Z-INDEX'])) {
  15424. $css .= 'z-index: ' . $p['Z-INDEX'] . '; ';
  15425. }
  15426. if ($css) {
  15427. $html = '<div style="' . $css . '">' . $html . '</div>';
  15428. }
  15429. // Copy over (only) the properties to set for border and background
  15430. $pb = array();
  15431. $pb['MARGIN-TOP'] = (isset($p['MARGIN-TOP']) ? $p['MARGIN-TOP'] : '');
  15432. $pb['MARGIN-RIGHT'] = (isset($p['MARGIN-RIGHT']) ? $p['MARGIN-RIGHT'] : '');
  15433. $pb['MARGIN-BOTTOM'] = (isset($p['MARGIN-BOTTOM']) ? $p['MARGIN-BOTTOM'] : '');
  15434. $pb['MARGIN-LEFT'] = (isset($p['MARGIN-LEFT']) ? $p['MARGIN-LEFT'] : '');
  15435. $pb['PADDING-TOP'] = (isset($p['PADDING-TOP']) ? $p['PADDING-TOP'] : '');
  15436. $pb['PADDING-RIGHT'] = (isset($p['PADDING-RIGHT']) ? $p['PADDING-RIGHT'] : '');
  15437. $pb['PADDING-BOTTOM'] = (isset($p['PADDING-BOTTOM']) ? $p['PADDING-BOTTOM'] : '');
  15438. $pb['PADDING-LEFT'] = (isset($p['PADDING-LEFT']) ? $p['PADDING-LEFT'] : '');
  15439. $pb['BORDER-TOP'] = (isset($p['BORDER-TOP']) ? $p['BORDER-TOP'] : '');
  15440. $pb['BORDER-RIGHT'] = (isset($p['BORDER-RIGHT']) ? $p['BORDER-RIGHT'] : '');
  15441. $pb['BORDER-BOTTOM'] = (isset($p['BORDER-BOTTOM']) ? $p['BORDER-BOTTOM'] : '');
  15442. $pb['BORDER-LEFT'] = (isset($p['BORDER-LEFT']) ? $p['BORDER-LEFT'] : '');
  15443. if (isset($p['BORDER-TOP-LEFT-RADIUS-H'])) {
  15444. $pb['BORDER-TOP-LEFT-RADIUS-H'] = $p['BORDER-TOP-LEFT-RADIUS-H'];
  15445. }
  15446. if (isset($p['BORDER-TOP-LEFT-RADIUS-V'])) {
  15447. $pb['BORDER-TOP-LEFT-RADIUS-V'] = $p['BORDER-TOP-LEFT-RADIUS-V'];
  15448. }
  15449. if (isset($p['BORDER-TOP-RIGHT-RADIUS-H'])) {
  15450. $pb['BORDER-TOP-RIGHT-RADIUS-H'] = $p['BORDER-TOP-RIGHT-RADIUS-H'];
  15451. }
  15452. if (isset($p['BORDER-TOP-RIGHT-RADIUS-V'])) {
  15453. $pb['BORDER-TOP-RIGHT-RADIUS-V'] = $p['BORDER-TOP-RIGHT-RADIUS-V'];
  15454. }
  15455. if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-H'])) {
  15456. $pb['BORDER-BOTTOM-LEFT-RADIUS-H'] = $p['BORDER-BOTTOM-LEFT-RADIUS-H'];
  15457. }
  15458. if (isset($p['BORDER-BOTTOM-LEFT-RADIUS-V'])) {
  15459. $pb['BORDER-BOTTOM-LEFT-RADIUS-V'] = $p['BORDER-BOTTOM-LEFT-RADIUS-V'];
  15460. }
  15461. if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-H'])) {
  15462. $pb['BORDER-BOTTOM-RIGHT-RADIUS-H'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-H'];
  15463. }
  15464. if (isset($p['BORDER-BOTTOM-RIGHT-RADIUS-V'])) {
  15465. $pb['BORDER-BOTTOM-RIGHT-RADIUS-V'] = $p['BORDER-BOTTOM-RIGHT-RADIUS-V'];
  15466. }
  15467. if (isset($p['BACKGROUND-COLOR'])) {
  15468. $pb['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
  15469. }
  15470. if (isset($p['BOX-SHADOW'])) {
  15471. $pb['BOX-SHADOW'] = $p['BOX-SHADOW'];
  15472. }
  15473. /* -- BACKGROUNDS -- */
  15474. if (isset($p['BACKGROUND-IMAGE'])) {
  15475. $pb['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
  15476. }
  15477. if (isset($p['BACKGROUND-IMAGE-RESIZE'])) {
  15478. $pb['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
  15479. }
  15480. if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
  15481. $pb['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
  15482. }
  15483. if (isset($p['BACKGROUND-REPEAT'])) {
  15484. $pb['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
  15485. }
  15486. if (isset($p['BACKGROUND-POSITION'])) {
  15487. $pb['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
  15488. }
  15489. if (isset($p['BACKGROUND-GRADIENT'])) {
  15490. $pb['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
  15491. }
  15492. if (isset($p['BACKGROUND-SIZE'])) {
  15493. $pb['BACKGROUND-SIZE'] = $p['BACKGROUND-SIZE'];
  15494. }
  15495. if (isset($p['BACKGROUND-ORIGIN'])) {
  15496. $pb['BACKGROUND-ORIGIN'] = $p['BACKGROUND-ORIGIN'];
  15497. }
  15498. if (isset($p['BACKGROUND-CLIP'])) {
  15499. $pb['BACKGROUND-CLIP'] = $p['BACKGROUND-CLIP'];
  15500. }
  15501. /* -- END BACKGROUNDS -- */
  15502. $this->setCSS($pb, 'BLOCK', $tag);
  15503. //================================================================
  15504. $bbox_br = $this->blk[1]['border_right']['w'];
  15505. $bbox_bl = $this->blk[1]['border_left']['w'];
  15506. $bbox_bt = $this->blk[1]['border_top']['w'];
  15507. $bbox_bb = $this->blk[1]['border_bottom']['w'];
  15508. $bbox_pr = $this->blk[1]['padding_right'];
  15509. $bbox_pl = $this->blk[1]['padding_left'];
  15510. $bbox_pt = $this->blk[1]['padding_top'];
  15511. $bbox_pb = $this->blk[1]['padding_bottom'];
  15512. $bbox_mr = $this->blk[1]['margin_right'];
  15513. if (isset($p['MARGIN-RIGHT']) && strtolower($p['MARGIN-RIGHT']) == 'auto') {
  15514. $bbox_mr = 'auto';
  15515. }
  15516. $bbox_ml = $this->blk[1]['margin_left'];
  15517. if (isset($p['MARGIN-LEFT']) && strtolower($p['MARGIN-LEFT']) == 'auto') {
  15518. $bbox_ml = 'auto';
  15519. }
  15520. $bbox_mt = $this->blk[1]['margin_top'];
  15521. if (isset($p['MARGIN-TOP']) && strtolower($p['MARGIN-TOP']) == 'auto') {
  15522. $bbox_mt = 'auto';
  15523. }
  15524. $bbox_mb = $this->blk[1]['margin_bottom'];
  15525. if (isset($p['MARGIN-BOTTOM']) && strtolower($p['MARGIN-BOTTOM']) == 'auto') {
  15526. $bbox_mb = 'auto';
  15527. }
  15528. if (isset($p['LEFT']) && strtolower($p['LEFT']) != 'auto') {
  15529. $bbox_left = $this->ConvertSize($p['LEFT'], $cont_w, $this->FontSize, false);
  15530. } else {
  15531. $bbox_left = 'auto';
  15532. }
  15533. if (isset($p['TOP']) && strtolower($p['TOP']) != 'auto') {
  15534. $bbox_top = $this->ConvertSize($p['TOP'], $cont_h, $this->FontSize, false);
  15535. } else {
  15536. $bbox_top = 'auto';
  15537. }
  15538. if (isset($p['RIGHT']) && strtolower($p['RIGHT']) != 'auto') {
  15539. $bbox_right = $this->ConvertSize($p['RIGHT'], $cont_w, $this->FontSize, false);
  15540. } else {
  15541. $bbox_right = 'auto';
  15542. }
  15543. if (isset($p['BOTTOM']) && strtolower($p['BOTTOM']) != 'auto') {
  15544. $bbox_bottom = $this->ConvertSize($p['BOTTOM'], $cont_h, $this->FontSize, false);
  15545. } else {
  15546. $bbox_bottom = 'auto';
  15547. }
  15548. if (isset($p['WIDTH']) && strtolower($p['WIDTH']) != 'auto') {
  15549. $inner_w = $this->ConvertSize($p['WIDTH'], $cont_w, $this->FontSize, false);
  15550. } else {
  15551. $inner_w = 'auto';
  15552. }
  15553. if (isset($p['HEIGHT']) && strtolower($p['HEIGHT']) != 'auto') {
  15554. $inner_h = $this->ConvertSize($p['HEIGHT'], $cont_h, $this->FontSize, false);
  15555. } else {
  15556. $inner_h = 'auto';
  15557. }
  15558. // If bottom or right pos are set and not left / top - save this to adjust rotated block later
  15559. if ($rotate == 90 || $rotate == -90) { // mPDF 6
  15560. if ($bbox_left === 'auto' && $bbox_right !== 'auto') {
  15561. $rot_rpos = $bbox_right;
  15562. } else {
  15563. $rot_rpos = false;
  15564. }
  15565. if ($bbox_top === 'auto' && $bbox_bottom !== 'auto') {
  15566. $rot_bpos = $bbox_bottom;
  15567. } else {
  15568. $rot_bpos = false;
  15569. }
  15570. }
  15571. //================================================================
  15572. if ($checkinnerhtml == '' && $inner_h === 'auto') {
  15573. $inner_h = 0.0001;
  15574. }
  15575. if ($checkinnerhtml == '' && $inner_w === 'auto') {
  15576. $inner_w = 2 * $this->GetCharWidth('W', false);
  15577. }
  15578. //================================================================
  15579. // Algorithm from CSS2.1 See http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
  15580. // mPD 5.3.14
  15581. // Special case (not CSS) if all not specified, centre vertically on page
  15582. $bbox_top_orig = '';
  15583. if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_mt === 'auto' && $bbox_mb === 'auto') {
  15584. $bbox_top_orig = $bbox_top;
  15585. if ($bbox_mt === 'auto') {
  15586. $bbox_mt = 0;
  15587. }
  15588. if ($bbox_mb === 'auto') {
  15589. $bbox_mb = 0;
  15590. }
  15591. $bbox_top = $orig_y0 - $bbox_mt - $cont_y;
  15592. // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
  15593. }
  15594. // mPD 5.3.14
  15595. elseif ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom === 'auto') {
  15596. $bbox_top_orig = $bbox_top = $orig_y0 - $cont_y;
  15597. if ($bbox_mt === 'auto') {
  15598. $bbox_mt = 0;
  15599. }
  15600. if ($bbox_mb === 'auto') {
  15601. $bbox_mb = 0;
  15602. }
  15603. // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
  15604. } elseif ($bbox_top !== 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
  15605. if ($bbox_mt === 'auto' && $bbox_mb === 'auto') {
  15606. $x = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
  15607. $bbox_mt = $bbox_mb = ($x / 2);
  15608. } elseif ($bbox_mt === 'auto') {
  15609. $bbox_mt = $cont_h - $bbox_top - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  15610. } elseif ($bbox_mb === 'auto') {
  15611. $bbox_mb = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_bottom;
  15612. } else {
  15613. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
  15614. }
  15615. } else {
  15616. if ($bbox_mt === 'auto') {
  15617. $bbox_mt = 0;
  15618. }
  15619. if ($bbox_mb === 'auto') {
  15620. $bbox_mb = 0;
  15621. }
  15622. if ($bbox_top === 'auto' && $inner_h === 'auto' && $bbox_bottom !== 'auto') {
  15623. // solve for $bbox_top when content_h known - $inner_h=='auto' && $bbox_top =='auto'
  15624. } elseif ($bbox_top === 'auto' && $bbox_bottom === 'auto' && $inner_h !== 'auto') {
  15625. $bbox_top = $orig_y0 - $bbox_mt - $cont_y;
  15626. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
  15627. } elseif ($inner_h === 'auto' && $bbox_bottom === 'auto' && $bbox_top !== 'auto') {
  15628. // solve for $bbox_bottom when content_h known - $inner_h=='auto' && $bbox_bottom=='auto'
  15629. } elseif ($bbox_top === 'auto' && $inner_h !== 'auto' && $bbox_bottom !== 'auto') {
  15630. $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
  15631. } elseif ($inner_h === 'auto' && $bbox_top !== 'auto' && $bbox_bottom !== 'auto') {
  15632. $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mt - $bbox_bottom;
  15633. } elseif ($bbox_bottom === 'auto' && $bbox_top !== 'auto' && $inner_h !== 'auto') {
  15634. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mt;
  15635. }
  15636. }
  15637. // THEN DO SAME FOR WIDTH
  15638. // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
  15639. if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right === 'auto') {
  15640. if ($bbox_ml === 'auto') {
  15641. $bbox_ml = 0;
  15642. }
  15643. if ($bbox_mr === 'auto') {
  15644. $bbox_mr = 0;
  15645. }
  15646. // IF containing element RTL, should set $bbox_right
  15647. $bbox_left = $orig_x0 - $bbox_ml - $cont_x;
  15648. // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
  15649. } elseif ($bbox_left !== 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
  15650. if ($bbox_ml === 'auto' && $bbox_mr === 'auto') {
  15651. $x = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
  15652. $bbox_ml = $bbox_mr = ($x / 2);
  15653. } elseif ($bbox_ml === 'auto') {
  15654. $bbox_ml = $cont_w - $bbox_left - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
  15655. } elseif ($bbox_mr === 'auto') {
  15656. $bbox_mr = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_right;
  15657. } else {
  15658. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  15659. }
  15660. } else {
  15661. if ($bbox_ml === 'auto') {
  15662. $bbox_ml = 0;
  15663. }
  15664. if ($bbox_mr === 'auto') {
  15665. $bbox_mr = 0;
  15666. }
  15667. if ($bbox_left === 'auto' && $inner_w === 'auto' && $bbox_right !== 'auto') {
  15668. // solve for $bbox_left when content_w known - $inner_w=='auto' && $bbox_left =='auto'
  15669. } elseif ($bbox_left === 'auto' && $bbox_right === 'auto' && $inner_w !== 'auto') {
  15670. // IF containing element RTL, should set $bbox_right
  15671. $bbox_left = $orig_x0 - $bbox_ml - $cont_x;
  15672. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  15673. } elseif ($inner_w === 'auto' && $bbox_right === 'auto' && $bbox_left !== 'auto') {
  15674. // solve for $bbox_right when content_w known - $inner_w=='auto' && $bbox_right=='auto'
  15675. } elseif ($bbox_left === 'auto' && $inner_w !== 'auto' && $bbox_right !== 'auto') {
  15676. $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
  15677. } elseif ($inner_w === 'auto' && $bbox_left !== 'auto' && $bbox_right !== 'auto') {
  15678. $inner_w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
  15679. } elseif ($bbox_right === 'auto' && $bbox_left !== 'auto' && $inner_w !== 'auto') {
  15680. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  15681. }
  15682. }
  15683. //================================================================
  15684. //================================================================
  15685. /* -- BACKGROUNDS -- */
  15686. if (isset($pb['BACKGROUND-IMAGE']) && $pb['BACKGROUND-IMAGE']) {
  15687. $ret = $this->SetBackground($pb, $this->blk[1]['inner_width']);
  15688. if ($ret) {
  15689. $this->blk[1]['background-image'] = $ret;
  15690. }
  15691. }
  15692. /* -- END BACKGROUNDS -- */
  15693. //================================================================
  15694. $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
  15695. $h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  15696. $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
  15697. $w = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $bbox_pr - $bbox_br - $bbox_mr - $bbox_right;
  15698. // Set (temporary) values for x y w h to do first paint, if values are auto
  15699. if ($inner_h === 'auto' && $bbox_top === 'auto') {
  15700. $y = $cont_y + $bbox_mt + $bbox_bt + $bbox_pt;
  15701. $h = $cont_h - ($bbox_bottom + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
  15702. } elseif ($inner_h === 'auto' && $bbox_bottom === 'auto') {
  15703. $y = $cont_y + $bbox_top + $bbox_mt + $bbox_bt + $bbox_pt;
  15704. $h = $cont_h - ($bbox_top + $bbox_mt + $bbox_mb + $bbox_bt + $bbox_bb + $bbox_pt + $bbox_pb);
  15705. }
  15706. if ($inner_w === 'auto' && $bbox_left === 'auto') {
  15707. $x = $cont_x + $bbox_ml + $bbox_bl + $bbox_pl;
  15708. $w = $cont_w - ($bbox_right + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
  15709. } elseif ($inner_w === 'auto' && $bbox_right === 'auto') {
  15710. $x = $cont_x + $bbox_left + $bbox_ml + $bbox_bl + $bbox_pl;
  15711. $w = $cont_w - ($bbox_left + $bbox_ml + $bbox_mr + $bbox_bl + $bbox_br + $bbox_pl + $bbox_pr);
  15712. }
  15713. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  15714. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  15715. $saved_block1 = $this->blk[1];
  15716. unset($p);
  15717. unset($pb);
  15718. //================================================================
  15719. if ($inner_w === 'auto') { // do a first write
  15720. $this->lMargin = $x;
  15721. $this->rMargin = $this->w - $w - $x;
  15722. // SET POSITION & FONT VALUES
  15723. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  15724. $this->pageoutput[$this->page] = array();
  15725. $this->x = $x;
  15726. $this->y = $y;
  15727. $this->HTMLheaderPageLinks = array();
  15728. $this->HTMLheaderPageAnnots = array();
  15729. $this->HTMLheaderPageForms = array();
  15730. $this->pageBackgrounds = array();
  15731. $this->maxPosR = 0;
  15732. $this->maxPosL = $this->w; // For RTL
  15733. $this->WriteHTML($html, 4);
  15734. $inner_w = $this->maxPosR - $this->lMargin;
  15735. if ($bbox_right === 'auto') {
  15736. $bbox_right = $cont_w - $bbox_left - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml;
  15737. } elseif ($bbox_left === 'auto') {
  15738. $bbox_left = $cont_w - $bbox_ml - $bbox_bl - $bbox_pl - $inner_w - $bbox_pr - $bbox_br - $bbox_ml - $bbox_right;
  15739. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  15740. $inner_x = $bbox_x + $bbox_bl + $bbox_pl;
  15741. $x = $inner_x;
  15742. }
  15743. $w = $inner_w;
  15744. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  15745. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  15746. }
  15747. if ($inner_h === 'auto') { // do a first write
  15748. $this->lMargin = $x;
  15749. $this->rMargin = $this->w - $w - $x;
  15750. // SET POSITION & FONT VALUES
  15751. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  15752. $this->pageoutput[$this->page] = array();
  15753. $this->x = $x;
  15754. $this->y = $y;
  15755. $this->HTMLheaderPageLinks = array();
  15756. $this->HTMLheaderPageAnnots = array();
  15757. $this->HTMLheaderPageForms = array();
  15758. $this->pageBackgrounds = array();
  15759. $this->WriteHTML($html, 4);
  15760. $inner_h = $this->y - $y;
  15761. if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
  15762. if (($this->y + $bbox_pb + $bbox_bb) > ($cont_y + $cont_h)) {
  15763. $adj = ($this->y + $bbox_pb + $bbox_bb) - ($cont_y + $cont_h);
  15764. $inner_h -= $adj;
  15765. }
  15766. }
  15767. if ($bbox_bottom === 'auto' && $bbox_top_orig === 'auto') {
  15768. $bbox_bottom = $bbox_top = ($cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb) / 2;
  15769. if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
  15770. if ($bbox_top < 0) {
  15771. $bbox_top = 0;
  15772. $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  15773. }
  15774. }
  15775. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  15776. $inner_y = $bbox_y + $bbox_bt + $bbox_pt;
  15777. $y = $inner_y;
  15778. } elseif ($bbox_bottom === 'auto') {
  15779. $bbox_bottom = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb;
  15780. } elseif ($bbox_top === 'auto') {
  15781. $bbox_top = $cont_h - $bbox_mt - $bbox_bt - $bbox_pt - $inner_h - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  15782. if ($overflow != 'hidden' && $overflow != 'visible') { // constrained
  15783. if ($bbox_top < 0) {
  15784. $bbox_top = 0;
  15785. $inner_h = $cont_h - $bbox_top - $bbox_mt - $bbox_bt - $bbox_pt - $bbox_pb - $bbox_bb - $bbox_mb - $bbox_bottom;
  15786. }
  15787. }
  15788. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  15789. $inner_y = $bbox_y + $bbox_bt + $bbox_pt;
  15790. $y = $inner_y;
  15791. }
  15792. $h = $inner_h;
  15793. $bbox_y = $cont_y + $bbox_top + $bbox_mt;
  15794. $bbox_x = $cont_x + $bbox_left + $bbox_ml;
  15795. }
  15796. $inner_w = $w;
  15797. $inner_h = $h;
  15798. }
  15799. $this->lMargin = $x;
  15800. $this->rMargin = $this->w - $w - $x;
  15801. // SET POSITION & FONT VALUES
  15802. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  15803. $this->pageoutput[$this->page] = array();
  15804. $this->x = $x;
  15805. $this->y = $y;
  15806. $this->HTMLheaderPageLinks = array();
  15807. $this->HTMLheaderPageAnnots = array();
  15808. $this->HTMLheaderPageForms = array();
  15809. $this->pageBackgrounds = array();
  15810. $this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
  15811. $actual_h = $this->y - $y;
  15812. $use_w = $w;
  15813. $use_h = $h;
  15814. $ratio = $actual_h / $use_w;
  15815. if ($overflow != 'hidden' && $overflow != 'visible') {
  15816. $target = $h / $w;
  15817. if (($ratio / $target ) > 1) {
  15818. $nl = CEIL($actual_h / $this->lineheight);
  15819. $l = $use_w * $nl;
  15820. $est_w = sqrt(($l * $this->lineheight) / $target) * 0.8;
  15821. $use_w += ($est_w - $use_w) - ($w / 100);
  15822. }
  15823. $bpcstart = ($ratio / $target);
  15824. $bpcctr = 1;
  15825. while (($ratio / $target ) > 1) {
  15826. if ($this->progressBar) {
  15827. $this->UpdateProgressBar(4, intval(100 / ($ratio / $target)), ('Auto-sizing fixed-position block: ' . $bpcctr++));
  15828. } // *PROGRESS-BAR*
  15829. $this->x = $x;
  15830. $this->y = $y;
  15831. if (($ratio / $target) > 1.5 || ($ratio / $target) < 0.6) {
  15832. $use_w += ($w / $this->incrementFPR1);
  15833. } elseif (($ratio / $target) > 1.2 || ($ratio / $target) < 0.85) {
  15834. $use_w += ($w / $this->incrementFPR2);
  15835. } elseif (($ratio / $target) > 1.1 || ($ratio / $target) < 0.91) {
  15836. $use_w += ($w / $this->incrementFPR3);
  15837. } else {
  15838. $use_w += ($w / $this->incrementFPR4);
  15839. }
  15840. $use_h = $use_w * $target;
  15841. $this->rMargin = $this->w - $use_w - $x;
  15842. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  15843. $this->HTMLheaderPageLinks = array();
  15844. $this->HTMLheaderPageAnnots = array();
  15845. $this->HTMLheaderPageForms = array();
  15846. $this->pageBackgrounds = array();
  15847. $this->WriteHTML($html, 4); // parameter 4 saves output to $this->headerbuffer
  15848. $actual_h = $this->y - $y;
  15849. $ratio = $actual_h / $use_w;
  15850. }
  15851. if ($this->progressBar) {
  15852. $this->UpdateProgressBar(4, '100', ' ');
  15853. } // *PROGRESS-BAR*
  15854. }
  15855. $shrink_f = $w / $use_w;
  15856. //================================================================
  15857. $this->pages[$this->page] .= '___BEFORE_BORDERS___';
  15858. $block_s = $this->PrintPageBackgrounds(); // Save to print later inside clipping path
  15859. $this->pageBackgrounds = array();
  15860. //================================================================
  15861. if ($rotate == 90 || $rotate == -90) { // mPDF 6
  15862. $prerotw = $bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br;
  15863. $preroth = $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
  15864. $rot_start = " q\n";
  15865. if ($rotate == 90) {
  15866. if ($rot_rpos !== false) {
  15867. $adjw = $prerotw;
  15868. } // width before rotation
  15869. else {
  15870. $adjw = $preroth;
  15871. } // height before rotation
  15872. if ($rot_bpos !== false) {
  15873. $adjh = -$prerotw + $preroth;
  15874. } else {
  15875. $adjh = 0;
  15876. }
  15877. } else {
  15878. if ($rot_rpos !== false) {
  15879. $adjw = $prerotw - $preroth;
  15880. } else {
  15881. $adjw = 0;
  15882. }
  15883. if ($rot_bpos !== false) {
  15884. $adjh = $preroth;
  15885. } // height before rotation
  15886. else {
  15887. $adjh = $prerotw;
  15888. } // width before rotation
  15889. }
  15890. $rot_start .= $this->transformTranslate($adjw, $adjh, true) . "\n";
  15891. $rot_start .= $this->transformRotate($rotate, $bbox_x, $bbox_y, true) . "\n";
  15892. $rot_end = " Q\n";
  15893. } elseif ($rotate == 180) { // mPDF 6
  15894. $rot_start = " q\n";
  15895. $rot_start .= $this->transformTranslate($bbox_bl + $bbox_pl + $inner_w + $bbox_pr + $bbox_br, $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb, true) . "\n";
  15896. $rot_start .= $this->transformRotate(180, $bbox_x, $bbox_y, true) . "\n";
  15897. $rot_end = " Q\n";
  15898. } else {
  15899. $rot_start = '';
  15900. $rot_end = '';
  15901. }
  15902. //================================================================
  15903. if (!empty($bounding)) {
  15904. // WHEN HEIGHT // BOTTOM EDGE IS KNOWN and $this->y is set to the bottom
  15905. // Re-instate saved $this->blk[1]
  15906. $this->blk[1] = $saved_block1;
  15907. // These are only needed when painting border/background
  15908. $this->blk[1]['width'] = $bbox_w = $cont_w - $bbox_left - $bbox_ml - $bbox_mr - $bbox_right;
  15909. $this->blk[1]['x0'] = $bbox_x;
  15910. $this->blk[1]['y0'] = $bbox_y;
  15911. $this->blk[1]['startpage'] = $this->page;
  15912. $this->blk[1]['y1'] = $bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb;
  15913. $this->_out($rot_start);
  15914. $this->PaintDivBB('', 0, 1); // Prints borders and sets backgrounds in $this->pageBackgrounds
  15915. $this->_out($rot_end);
  15916. }
  15917. $s = $this->PrintPageBackgrounds();
  15918. $s = $rot_start . $s . $rot_end;
  15919. $this->pages[$this->page] = preg_replace('/___BEFORE_BORDERS___/', "\n" . $s . "\n", $this->pages[$this->page]);
  15920. $this->pageBackgrounds = array();
  15921. $this->_out($rot_start);
  15922. // Clipping Output
  15923. if ($overflow == 'hidden') {
  15924. //Bounding rectangle to clip
  15925. $clip_y1 = $this->y;
  15926. if (!empty($bounding) && ($this->y + $bbox_pb + $bbox_bb) > ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb )) {
  15927. $clip_y1 = ($bbox_y + $bbox_bt + $bbox_pt + $inner_h + $bbox_pb + $bbox_bb ) - ($bbox_pb + $bbox_bb);
  15928. }
  15929. //$op = 'W* n'; // Clipping
  15930. $op = 'W n'; // Clipping alternative mode
  15931. $this->_out("q");
  15932. $ch = $clip_y1 - $y;
  15933. $this->_out(sprintf('%.3F %.3F %.3F %.3F re %s', $x * _MPDFK, ($this->h - $y) * _MPDFK, $w * _MPDFK, -$ch * _MPDFK, $op));
  15934. if (!empty($block_s)) {
  15935. $tmp = "q\n" . sprintf('%.3F %.3F %.3F %.3F re %s', $x * _MPDFK, ($this->h - $y) * _MPDFK, $w * _MPDFK, -$ch * _MPDFK, $op);
  15936. $tmp .= "\n" . $block_s . "\nQ";
  15937. $block_s = $tmp;
  15938. }
  15939. }
  15940. if (!empty($block_s)) {
  15941. if ($shrink_f != 1) { // i.e. autofit has resized the box
  15942. $tmp = "q\n" . $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y, true);
  15943. $tmp .= "\n" . $block_s . "\nQ";
  15944. $block_s = $tmp;
  15945. }
  15946. $this->_out($block_s);
  15947. }
  15948. if ($shrink_f != 1) { // i.e. autofit has resized the box
  15949. $this->StartTransform();
  15950. $this->transformScale(($shrink_f * 100), ($shrink_f * 100), $x, $y);
  15951. }
  15952. $this->_out($this->headerbuffer);
  15953. if ($shrink_f != 1) { // i.e. autofit has resized the box
  15954. $this->StopTransform();
  15955. }
  15956. if ($overflow == 'hidden') {
  15957. //End clipping
  15958. $this->_out("Q");
  15959. }
  15960. $this->_out($rot_end);
  15961. // Page Links
  15962. foreach ($this->HTMLheaderPageLinks AS $lk) {
  15963. if ($rotate) {
  15964. $tmp = $lk[2]; // Switch h - w
  15965. $lk[2] = $lk[3];
  15966. $lk[3] = $tmp;
  15967. $lx1 = (($lk[0] / _MPDFK));
  15968. $ly1 = (($this->h - ($lk[1] / _MPDFK)));
  15969. if ($rotate == 90) {
  15970. $adjx = -($lx1 - $bbox_x) + ($preroth - ($ly1 - $bbox_y));
  15971. $adjy = -($ly1 - $bbox_y) + ($lx1 - $bbox_x);
  15972. $lk[2] = -$lk[2];
  15973. } elseif ($rotate == -90) {
  15974. $adjx = -($lx1 - $bbox_x) + ($ly1 - $bbox_y);
  15975. $adjy = -($ly1 - $bbox_y) - ($lx1 - $bbox_x) + $prerotw;
  15976. $lk[3] = -$lk[3];
  15977. }
  15978. if ($rot_rpos !== false) {
  15979. $adjx += $prerotw - $preroth;
  15980. }
  15981. if ($rot_bpos !== false) {
  15982. $adjy += $preroth - $prerotw;
  15983. }
  15984. $lx1 += $adjx;
  15985. $ly1 += $adjy;
  15986. $lk[0] = $lx1 * _MPDFK;
  15987. $lk[1] = ($this->h - $ly1) * _MPDFK;
  15988. }
  15989. if ($shrink_f != 1) { // i.e. autofit has resized the box
  15990. $lx1 = (($lk[0] / _MPDFK) - $x);
  15991. $lx2 = $x + ($lx1 * $shrink_f);
  15992. $lk[0] = $lx2 * _MPDFK;
  15993. $ly1 = (($this->h - ($lk[1] / _MPDFK)) - $y);
  15994. $ly2 = $y + ($ly1 * $shrink_f);
  15995. $lk[1] = ($this->h - $ly2) * _MPDFK;
  15996. $lk[2] *= $shrink_f; // width
  15997. $lk[3] *= $shrink_f; // height
  15998. }
  15999. $this->PageLinks[$this->page][] = $lk;
  16000. }
  16001. foreach ($this->HTMLheaderPageForms AS $n => $f) {
  16002. if ($shrink_f != 1) { // i.e. autofit has resized the box
  16003. $f['x'] = $x + (($f['x'] - $x) * $shrink_f);
  16004. $f['y'] = $y + (($f['y'] - $y) * $shrink_f);
  16005. $f['w'] *= $shrink_f;
  16006. $f['h'] *= $shrink_f;
  16007. $f['style']['fontsize'] *= $shrink_f;
  16008. }
  16009. $this->mpdfform->forms[$f['n']] = $f;
  16010. }
  16011. // Page Annotations
  16012. foreach ($this->HTMLheaderPageAnnots AS $lk) {
  16013. if ($rotate) {
  16014. if ($rotate == 90) {
  16015. $adjx = -($lk['x'] - $bbox_x) + ($preroth - ($lk['y'] - $bbox_y));
  16016. $adjy = -($lk['y'] - $bbox_y) + ($lk['x'] - $bbox_x);
  16017. } elseif ($rotate == -90) {
  16018. $adjx = -($lk['x'] - $bbox_x) + ($lk['y'] - $bbox_y);
  16019. $adjy = -($lk['y'] - $bbox_y) - ($lk['x'] - $bbox_x) + $prerotw;
  16020. }
  16021. if ($rot_rpos !== false) {
  16022. $adjx += $prerotw - $preroth;
  16023. }
  16024. if ($rot_bpos !== false) {
  16025. $adjy += $preroth - $prerotw;
  16026. }
  16027. $lk['x'] += $adjx;
  16028. $lk['y'] += $adjy;
  16029. }
  16030. if ($shrink_f != 1) { // i.e. autofit has resized the box
  16031. $lk['x'] = $x + (($lk['x'] - $x) * $shrink_f);
  16032. $lk['y'] = $y + (($lk['y'] - $y) * $shrink_f);
  16033. }
  16034. $this->PageAnnots[$this->page][] = $lk;
  16035. }
  16036. // Restore
  16037. $this->headerbuffer = '';
  16038. $this->HTMLheaderPageLinks = array();
  16039. $this->HTMLheaderPageAnnots = array();
  16040. $this->HTMLheaderPageForms = array();
  16041. $this->pageBackgrounds = $save_bgs;
  16042. $this->writingHTMLheader = false;
  16043. $this->writingHTMLfooter = false;
  16044. $this->fullImageHeight = false;
  16045. $this->ResetMargins();
  16046. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  16047. $this->SetXY($save_x, $save_y);
  16048. $this->title2annots = $save_annots; // *ANNOTATIONS*
  16049. $this->InFooter = false; // turns back on autopagebreaks
  16050. $this->pageoutput[$this->page] = array();
  16051. $this->pageoutput[$this->page]['Font'] = '';
  16052. /* -- COLUMNS -- */
  16053. if ($save_cols) {
  16054. $this->SetColumns($save_nbcol, $this->colvAlign, $this->ColGap);
  16055. }
  16056. /* -- END COLUMNS -- */
  16057. }
  16058. /* -- END CSS-POSITION -- */
  16059. function initialiseBlock(&$blk)
  16060. {
  16061. $blk['margin_top'] = 0;
  16062. $blk['margin_left'] = 0;
  16063. $blk['margin_bottom'] = 0;
  16064. $blk['margin_right'] = 0;
  16065. $blk['padding_top'] = 0;
  16066. $blk['padding_left'] = 0;
  16067. $blk['padding_bottom'] = 0;
  16068. $blk['padding_right'] = 0;
  16069. $blk['border_top']['w'] = 0;
  16070. $blk['border_left']['w'] = 0;
  16071. $blk['border_bottom']['w'] = 0;
  16072. $blk['border_right']['w'] = 0;
  16073. $blk['direction'] = 'ltr';
  16074. $blk['hide'] = false;
  16075. $blk['outer_left_margin'] = 0;
  16076. $blk['outer_right_margin'] = 0;
  16077. $blk['cascadeCSS'] = array();
  16078. $blk['block-align'] = false;
  16079. $blk['bgcolor'] = false;
  16080. $blk['page_break_after_avoid'] = false;
  16081. $blk['keep_block_together'] = false;
  16082. $blk['float'] = false;
  16083. $blk['line_height'] = '';
  16084. $blk['margin_collapse'] = false;
  16085. }
  16086. function border_details($bd)
  16087. {
  16088. $prop = preg_split('/\s+/', trim($bd));
  16089. if (isset($this->blk[$this->blklvl]['inner_width'])) {
  16090. $refw = $this->blk[$this->blklvl]['inner_width'];
  16091. } elseif (isset($this->blk[$this->blklvl - 1]['inner_width'])) {
  16092. $refw = $this->blk[$this->blklvl - 1]['inner_width'];
  16093. } else {
  16094. $refw = $this->w;
  16095. }
  16096. if (count($prop) == 1) {
  16097. $bsize = $this->ConvertSize($prop[0], $refw, $this->FontSize, false);
  16098. if ($bsize > 0) {
  16099. return array('s' => 1, 'w' => $bsize, 'c' => $this->ConvertColor(0), 'style' => 'solid');
  16100. } else {
  16101. return array('w' => 0, 's' => 0);
  16102. }
  16103. } elseif (count($prop) == 2) {
  16104. // 1px solid
  16105. if (in_array($prop[1], $this->borderstyles) || $prop[1] == 'none' || $prop[1] == 'hidden') {
  16106. $prop[2] = '';
  16107. }
  16108. // solid #000000
  16109. elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
  16110. $prop[0] = '';
  16111. $prop[1] = $prop[0];
  16112. $prop[2] = $prop[1];
  16113. }
  16114. // 1px #000000
  16115. else {
  16116. $prop[1] = '';
  16117. $prop[2] = $prop[1];
  16118. }
  16119. } elseif (count($prop) == 3) {
  16120. // Change #000000 1px solid to 1px solid #000000 (proper)
  16121. if (substr($prop[0], 0, 1) == '#') {
  16122. $tmp = $prop[0];
  16123. $prop[0] = $prop[1];
  16124. $prop[1] = $prop[2];
  16125. $prop[2] = $tmp;
  16126. }
  16127. // Change solid #000000 1px to 1px solid #000000 (proper)
  16128. elseif (substr($prop[0], 1, 1) == '#') {
  16129. $tmp = $prop[1];
  16130. $prop[0] = $prop[2];
  16131. $prop[1] = $prop[0];
  16132. $prop[2] = $tmp;
  16133. }
  16134. // Change solid 1px #000000 to 1px solid #000000 (proper)
  16135. elseif (in_array($prop[0], $this->borderstyles) || $prop[0] == 'none' || $prop[0] == 'hidden') {
  16136. $tmp = $prop[0];
  16137. $prop[0] = $prop[1];
  16138. $prop[1] = $tmp;
  16139. }
  16140. } else {
  16141. return array();
  16142. }
  16143. // Size
  16144. $bsize = $this->ConvertSize($prop[0], $refw, $this->FontSize, false);
  16145. //color
  16146. $coul = $this->ConvertColor($prop[2]); // returns array
  16147. // Style
  16148. $prop[1] = strtolower($prop[1]);
  16149. if (in_array($prop[1], $this->borderstyles) && $bsize > 0) {
  16150. $on = 1;
  16151. } elseif ($prop[1] == 'hidden') {
  16152. $on = 1;
  16153. $bsize = 0;
  16154. $coul = '';
  16155. } elseif ($prop[1] == 'none') {
  16156. $on = 0;
  16157. $bsize = 0;
  16158. $coul = '';
  16159. } else {
  16160. $on = 0;
  16161. $bsize = 0;
  16162. $coul = '';
  16163. $prop[1] = '';
  16164. }
  16165. return array('s' => $on, 'w' => $bsize, 'c' => $coul, 'style' => $prop[1]);
  16166. }
  16167. /* -- END HTML-CSS -- */
  16168. /* -- BORDER-RADIUS -- */
  16169. function _borderPadding($a, $b, &$px, &$py)
  16170. {
  16171. // $px and py are padding long axis (x) and short axis (y)
  16172. $added = 0; // extra padding
  16173. $x = $a - $px;
  16174. $y = $b - $py;
  16175. // Check if Falls within ellipse of border radius
  16176. if (( (($x + $added) * ($x + $added)) / ($a * $a) + (($y + $added) * ($y + $added)) / ($b * $b) ) <= 1) {
  16177. return false;
  16178. }
  16179. $t = atan2($y, $x);
  16180. $newx = $b / sqrt((($b * $b) / ($a * $a)) + ( tan($t) * tan($t) ));
  16181. $newy = $a / sqrt((($a * $a) / ($b * $b)) + ( (1 / tan($t)) * (1 / tan($t)) ));
  16182. $px = max($px, $a - $newx + $added);
  16183. $py = max($py, $b - $newy + $added);
  16184. }
  16185. /* -- END BORDER-RADIUS -- */
  16186. /* -- HTML-CSS -- */
  16187. /* -- CSS-PAGE -- */
  16188. function SetPagedMediaCSS($name = '', $first, $oddEven)
  16189. {
  16190. if ($oddEven == 'E') {
  16191. if ($this->directionality == 'rtl') {
  16192. $side = 'R';
  16193. } else {
  16194. $side = 'L';
  16195. }
  16196. } else {
  16197. if ($this->directionality == 'rtl') {
  16198. $side = 'L';
  16199. } else {
  16200. $side = 'R';
  16201. }
  16202. }
  16203. $name = strtoupper($name);
  16204. $p = array();
  16205. $p['SIZE'] = 'AUTO';
  16206. // Uses mPDF original margins as default
  16207. $p['MARGIN-RIGHT'] = strval($this->orig_rMargin) . 'mm';
  16208. $p['MARGIN-LEFT'] = strval($this->orig_lMargin) . 'mm';
  16209. $p['MARGIN-TOP'] = strval($this->orig_tMargin) . 'mm';
  16210. $p['MARGIN-BOTTOM'] = strval($this->orig_bMargin) . 'mm';
  16211. $p['MARGIN-HEADER'] = strval($this->orig_hMargin) . 'mm';
  16212. $p['MARGIN-FOOTER'] = strval($this->orig_fMargin) . 'mm';
  16213. // Basic page + selector
  16214. if (isset($this->cssmgr->CSS['@PAGE'])) {
  16215. $zp = $this->cssmgr->CSS['@PAGE'];
  16216. } else {
  16217. $zp = array();
  16218. }
  16219. if (is_array($zp) && !empty($zp)) {
  16220. $p = array_merge($p, $zp);
  16221. }
  16222. if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
  16223. $p['HEADER'] = $p['EVEN-HEADER-NAME'];
  16224. unset($p['EVEN-HEADER-NAME']);
  16225. }
  16226. if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
  16227. $p['HEADER'] = $p['ODD-HEADER-NAME'];
  16228. unset($p['ODD-HEADER-NAME']);
  16229. }
  16230. if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
  16231. $p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
  16232. unset($p['EVEN-FOOTER-NAME']);
  16233. }
  16234. if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
  16235. $p['FOOTER'] = $p['ODD-FOOTER-NAME'];
  16236. unset($p['ODD-FOOTER-NAME']);
  16237. }
  16238. // If right/Odd page
  16239. if (isset($this->cssmgr->CSS['@PAGE>>PSEUDO>>RIGHT']) && $side == 'R') {
  16240. $zp = $this->cssmgr->CSS['@PAGE>>PSEUDO>>RIGHT'];
  16241. } else {
  16242. $zp = array();
  16243. }
  16244. if (isset($zp['SIZE'])) {
  16245. unset($zp['SIZE']);
  16246. }
  16247. if (isset($zp['SHEET-SIZE'])) {
  16248. unset($zp['SHEET-SIZE']);
  16249. }
  16250. // Disallow margin-left or -right on :LEFT or :RIGHT
  16251. if (isset($zp['MARGIN-LEFT'])) {
  16252. unset($zp['MARGIN-LEFT']);
  16253. }
  16254. if (isset($zp['MARGIN-RIGHT'])) {
  16255. unset($zp['MARGIN-RIGHT']);
  16256. }
  16257. if (is_array($zp) && !empty($zp)) {
  16258. $p = array_merge($p, $zp);
  16259. }
  16260. // If left/Even page
  16261. if (isset($this->cssmgr->CSS['@PAGE>>PSEUDO>>LEFT']) && $side == 'L') {
  16262. $zp = $this->cssmgr->CSS['@PAGE>>PSEUDO>>LEFT'];
  16263. } else {
  16264. $zp = array();
  16265. }
  16266. if (isset($zp['SIZE'])) {
  16267. unset($zp['SIZE']);
  16268. }
  16269. if (isset($zp['SHEET-SIZE'])) {
  16270. unset($zp['SHEET-SIZE']);
  16271. }
  16272. // Disallow margin-left or -right on :LEFT or :RIGHT
  16273. if (isset($zp['MARGIN-LEFT'])) {
  16274. unset($zp['MARGIN-LEFT']);
  16275. }
  16276. if (isset($zp['MARGIN-RIGHT'])) {
  16277. unset($zp['MARGIN-RIGHT']);
  16278. }
  16279. if (is_array($zp) && !empty($zp)) {
  16280. $p = array_merge($p, $zp);
  16281. }
  16282. // If first page
  16283. if (isset($this->cssmgr->CSS['@PAGE>>PSEUDO>>FIRST']) && $first) {
  16284. $zp = $this->cssmgr->CSS['@PAGE>>PSEUDO>>FIRST'];
  16285. } else {
  16286. $zp = array();
  16287. }
  16288. if (isset($zp['SIZE'])) {
  16289. unset($zp['SIZE']);
  16290. }
  16291. if (isset($zp['SHEET-SIZE'])) {
  16292. unset($zp['SHEET-SIZE']);
  16293. }
  16294. // Disallow margin-left or -right on :FIRST // mPDF 5.7.3
  16295. if (isset($zp['MARGIN-LEFT'])) {
  16296. unset($zp['MARGIN-LEFT']);
  16297. }
  16298. if (isset($zp['MARGIN-RIGHT'])) {
  16299. unset($zp['MARGIN-RIGHT']);
  16300. }
  16301. if (is_array($zp) && !empty($zp)) {
  16302. $p = array_merge($p, $zp);
  16303. }
  16304. // If named page
  16305. if ($name) {
  16306. if (isset($this->cssmgr->CSS['@PAGE>>NAMED>>' . $name])) {
  16307. $zp = $this->cssmgr->CSS['@PAGE>>NAMED>>' . $name];
  16308. } else {
  16309. $zp = array();
  16310. }
  16311. if (is_array($zp) && !empty($zp)) {
  16312. $p = array_merge($p, $zp);
  16313. }
  16314. if (isset($p['EVEN-HEADER-NAME']) && $oddEven == 'E') {
  16315. $p['HEADER'] = $p['EVEN-HEADER-NAME'];
  16316. unset($p['EVEN-HEADER-NAME']);
  16317. }
  16318. if (isset($p['ODD-HEADER-NAME']) && $oddEven != 'E') {
  16319. $p['HEADER'] = $p['ODD-HEADER-NAME'];
  16320. unset($p['ODD-HEADER-NAME']);
  16321. }
  16322. if (isset($p['EVEN-FOOTER-NAME']) && $oddEven == 'E') {
  16323. $p['FOOTER'] = $p['EVEN-FOOTER-NAME'];
  16324. unset($p['EVEN-FOOTER-NAME']);
  16325. }
  16326. if (isset($p['ODD-FOOTER-NAME']) && $oddEven != 'E') {
  16327. $p['FOOTER'] = $p['ODD-FOOTER-NAME'];
  16328. unset($p['ODD-FOOTER-NAME']);
  16329. }
  16330. // If named right/Odd page
  16331. if (isset($this->cssmgr->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT']) && $side == 'R') {
  16332. $zp = $this->cssmgr->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>RIGHT'];
  16333. } else {
  16334. $zp = array();
  16335. }
  16336. if (isset($zp['SIZE'])) {
  16337. unset($zp['SIZE']);
  16338. }
  16339. if (isset($zp['SHEET-SIZE'])) {
  16340. unset($zp['SHEET-SIZE']);
  16341. }
  16342. // Disallow margin-left or -right on :LEFT or :RIGHT
  16343. if (isset($zp['MARGIN-LEFT'])) {
  16344. unset($zp['MARGIN-LEFT']);
  16345. }
  16346. if (isset($zp['MARGIN-RIGHT'])) {
  16347. unset($zp['MARGIN-RIGHT']);
  16348. }
  16349. if (is_array($zp) && !empty($zp)) {
  16350. $p = array_merge($p, $zp);
  16351. }
  16352. // If named left/Even page
  16353. if (isset($this->cssmgr->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT']) && $side == 'L') {
  16354. $zp = $this->cssmgr->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>LEFT'];
  16355. } else {
  16356. $zp = array();
  16357. }
  16358. if (isset($zp['SIZE'])) {
  16359. unset($zp['SIZE']);
  16360. }
  16361. if (isset($zp['SHEET-SIZE'])) {
  16362. unset($zp['SHEET-SIZE']);
  16363. }
  16364. // Disallow margin-left or -right on :LEFT or :RIGHT
  16365. if (isset($zp['MARGIN-LEFT'])) {
  16366. unset($zp['MARGIN-LEFT']);
  16367. }
  16368. if (isset($zp['MARGIN-RIGHT'])) {
  16369. unset($zp['MARGIN-RIGHT']);
  16370. }
  16371. if (is_array($zp) && !empty($zp)) {
  16372. $p = array_merge($p, $zp);
  16373. }
  16374. // If named first page
  16375. if (isset($this->cssmgr->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST']) && $first) {
  16376. $zp = $this->cssmgr->CSS['@PAGE>>NAMED>>' . $name . '>>PSEUDO>>FIRST'];
  16377. } else {
  16378. $zp = array();
  16379. }
  16380. if (isset($zp['SIZE'])) {
  16381. unset($zp['SIZE']);
  16382. }
  16383. if (isset($zp['SHEET-SIZE'])) {
  16384. unset($zp['SHEET-SIZE']);
  16385. }
  16386. // Disallow margin-left or -right on :FIRST // mPDF 5.7.3
  16387. if (isset($zp['MARGIN-LEFT'])) {
  16388. unset($zp['MARGIN-LEFT']);
  16389. }
  16390. if (isset($zp['MARGIN-RIGHT'])) {
  16391. unset($zp['MARGIN-RIGHT']);
  16392. }
  16393. if (is_array($zp) && !empty($zp)) {
  16394. $p = array_merge($p, $zp);
  16395. }
  16396. }
  16397. $orientation = $mgl = $mgr = $mgt = $mgb = $mgh = $mgf = '';
  16398. $header = $footer = '';
  16399. $resetpagenum = $pagenumstyle = $suppress = '';
  16400. $marks = '';
  16401. $bg = array();
  16402. $newformat = '';
  16403. if (isset($p['SHEET-SIZE']) && is_array($p['SHEET-SIZE'])) {
  16404. $newformat = $p['SHEET-SIZE'];
  16405. if ($newformat[0] > $newformat[1]) { // landscape
  16406. $newformat = array_reverse($newformat);
  16407. $p['ORIENTATION'] = 'L';
  16408. } else {
  16409. $p['ORIENTATION'] = 'P';
  16410. }
  16411. $this->_setPageSize($newformat, $p['ORIENTATION']);
  16412. }
  16413. if (isset($p['SIZE']) && is_array($p['SIZE']) && !$newformat) {
  16414. if ($p['SIZE']['W'] > $p['SIZE']['H']) {
  16415. $p['ORIENTATION'] = 'L';
  16416. } else {
  16417. $p['ORIENTATION'] = 'P';
  16418. }
  16419. }
  16420. if (is_array($p['SIZE'])) {
  16421. if ($p['SIZE']['W'] > $this->fw) {
  16422. $p['SIZE']['W'] = $this->fw;
  16423. } // mPD 4.2 use fw not fPt
  16424. if ($p['SIZE']['H'] > $this->fh) {
  16425. $p['SIZE']['H'] = $this->fh;
  16426. }
  16427. if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
  16428. $outer_width_LR = ($this->fw - $p['SIZE']['W']) / 2;
  16429. $outer_width_TB = ($this->fh - $p['SIZE']['H']) / 2;
  16430. } else {
  16431. $outer_width_LR = ($this->fh - $p['SIZE']['W']) / 2;
  16432. $outer_width_TB = ($this->fw - $p['SIZE']['H']) / 2;
  16433. }
  16434. $pgw = $p['SIZE']['W'];
  16435. $pgh = $p['SIZE']['H'];
  16436. } else { // AUTO LANDSCAPE PORTRAIT
  16437. $outer_width_LR = 0;
  16438. $outer_width_TB = 0;
  16439. if (!$newformat) {
  16440. if (strtoupper($p['SIZE']) == 'AUTO') {
  16441. $p['ORIENTATION'] = $this->DefOrientation;
  16442. } elseif (strtoupper($p['SIZE']) == 'LANDSCAPE') {
  16443. $p['ORIENTATION'] = 'L';
  16444. } else {
  16445. $p['ORIENTATION'] = 'P';
  16446. }
  16447. }
  16448. if (($p['ORIENTATION'] == $this->DefOrientation && !$newformat) || ($newformat && $p['ORIENTATION'] == 'P')) {
  16449. $pgw = $this->fw;
  16450. $pgh = $this->fh;
  16451. } else {
  16452. $pgw = $this->fh;
  16453. $pgh = $this->fw;
  16454. }
  16455. }
  16456. if (isset($p['HEADER']) && $p['HEADER']) {
  16457. $header = $p['HEADER'];
  16458. }
  16459. if (isset($p['FOOTER']) && $p['FOOTER']) {
  16460. $footer = $p['FOOTER'];
  16461. }
  16462. if (isset($p['RESETPAGENUM']) && $p['RESETPAGENUM']) {
  16463. $resetpagenum = $p['RESETPAGENUM'];
  16464. }
  16465. if (isset($p['PAGENUMSTYLE']) && $p['PAGENUMSTYLE']) {
  16466. $pagenumstyle = $p['PAGENUMSTYLE'];
  16467. }
  16468. if (isset($p['SUPPRESS']) && $p['SUPPRESS']) {
  16469. $suppress = $p['SUPPRESS'];
  16470. }
  16471. if (isset($p['MARKS'])) {
  16472. if (preg_match('/cross/i', $p['MARKS']) && preg_match('/crop/i', $p['MARKS'])) {
  16473. $marks = 'CROPCROSS';
  16474. } elseif (strtoupper($p['MARKS']) == 'CROP') {
  16475. $marks = 'CROP';
  16476. } elseif (strtoupper($p['MARKS']) == 'CROSS') {
  16477. $marks = 'CROSS';
  16478. }
  16479. }
  16480. if (isset($p['BACKGROUND-COLOR']) && $p['BACKGROUND-COLOR']) {
  16481. $bg['BACKGROUND-COLOR'] = $p['BACKGROUND-COLOR'];
  16482. }
  16483. /* -- BACKGROUNDS -- */
  16484. if (isset($p['BACKGROUND-GRADIENT']) && $p['BACKGROUND-GRADIENT']) {
  16485. $bg['BACKGROUND-GRADIENT'] = $p['BACKGROUND-GRADIENT'];
  16486. }
  16487. if (isset($p['BACKGROUND-IMAGE']) && $p['BACKGROUND-IMAGE']) {
  16488. $bg['BACKGROUND-IMAGE'] = $p['BACKGROUND-IMAGE'];
  16489. }
  16490. if (isset($p['BACKGROUND-REPEAT']) && $p['BACKGROUND-REPEAT']) {
  16491. $bg['BACKGROUND-REPEAT'] = $p['BACKGROUND-REPEAT'];
  16492. }
  16493. if (isset($p['BACKGROUND-POSITION']) && $p['BACKGROUND-POSITION']) {
  16494. $bg['BACKGROUND-POSITION'] = $p['BACKGROUND-POSITION'];
  16495. }
  16496. if (isset($p['BACKGROUND-IMAGE-RESIZE']) && $p['BACKGROUND-IMAGE-RESIZE']) {
  16497. $bg['BACKGROUND-IMAGE-RESIZE'] = $p['BACKGROUND-IMAGE-RESIZE'];
  16498. }
  16499. if (isset($p['BACKGROUND-IMAGE-OPACITY'])) {
  16500. $bg['BACKGROUND-IMAGE-OPACITY'] = $p['BACKGROUND-IMAGE-OPACITY'];
  16501. }
  16502. /* -- END BACKGROUNDS -- */
  16503. if (isset($p['MARGIN-LEFT'])) {
  16504. $mgl = $this->ConvertSize($p['MARGIN-LEFT'], $pgw) + $outer_width_LR;
  16505. }
  16506. if (isset($p['MARGIN-RIGHT'])) {
  16507. $mgr = $this->ConvertSize($p['MARGIN-RIGHT'], $pgw) + $outer_width_LR;
  16508. }
  16509. if (isset($p['MARGIN-BOTTOM'])) {
  16510. $mgb = $this->ConvertSize($p['MARGIN-BOTTOM'], $pgh) + $outer_width_TB;
  16511. }
  16512. if (isset($p['MARGIN-TOP'])) {
  16513. $mgt = $this->ConvertSize($p['MARGIN-TOP'], $pgh) + $outer_width_TB;
  16514. }
  16515. if (isset($p['MARGIN-HEADER'])) {
  16516. $mgh = $this->ConvertSize($p['MARGIN-HEADER'], $pgh) + $outer_width_TB;
  16517. }
  16518. if (isset($p['MARGIN-FOOTER'])) {
  16519. $mgf = $this->ConvertSize($p['MARGIN-FOOTER'], $pgh) + $outer_width_TB;
  16520. }
  16521. if (isset($p['ORIENTATION']) && $p['ORIENTATION']) {
  16522. $orientation = $p['ORIENTATION'];
  16523. }
  16524. $this->page_box['outer_width_LR'] = $outer_width_LR; // Used in MARKS:crop etc.
  16525. $this->page_box['outer_width_TB'] = $outer_width_TB;
  16526. return array($orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $header, $footer, $bg, $resetpagenum, $pagenumstyle, $suppress, $marks, $newformat);
  16527. }
  16528. /* -- END CSS-PAGE -- */
  16529. /* -- CSS-FLOAT -- */
  16530. // Added mPDF 3.0 Float DIV - CLEAR
  16531. function ClearFloats($clear, $blklvl = 0)
  16532. {
  16533. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($blklvl, true);
  16534. $end = $currpos = ($this->page * 1000 + $this->y);
  16535. if ($clear == 'BOTH' && ($l_exists || $r_exists)) {
  16536. $this->pageoutput[$this->page] = array();
  16537. $end = max($l_max, $r_max, $currpos);
  16538. } elseif ($clear == 'RIGHT' && $r_exists) {
  16539. $this->pageoutput[$this->page] = array();
  16540. $end = max($r_max, $currpos);
  16541. } elseif ($clear == 'LEFT' && $l_exists) {
  16542. $this->pageoutput[$this->page] = array();
  16543. $end = max($l_max, $currpos);
  16544. } else {
  16545. return;
  16546. }
  16547. $old_page = $this->page;
  16548. $new_page = intval($end / 1000);
  16549. if ($old_page != $new_page) {
  16550. $s = $this->PrintPageBackgrounds();
  16551. // Writes after the marker so not overwritten later by page background etc.
  16552. $this->pages[$this->page] = preg_replace('/(___BACKGROUND___PATTERNS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  16553. $this->pageBackgrounds = array();
  16554. $this->page = $new_page;
  16555. }
  16556. $this->ResetMargins();
  16557. $this->pageoutput[$this->page] = array();
  16558. $this->y = (($end * 1000) % 1000000) / 1000; // mod changes operands to integers before processing
  16559. }
  16560. // Added mPDF 3.0 Float DIV
  16561. function GetFloatDivInfo($blklvl = 0, $clear = false)
  16562. {
  16563. // If blklvl specified, only returns floats at that level - for ClearFloats
  16564. $l_exists = false;
  16565. $r_exists = false;
  16566. $l_max = 0;
  16567. $r_max = 0;
  16568. $l_width = 0;
  16569. $r_width = 0;
  16570. if (count($this->floatDivs)) {
  16571. $currpos = ($this->page * 1000 + $this->y);
  16572. foreach ($this->floatDivs AS $f) {
  16573. if (($clear && $f['blockContext'] == $this->blk[$blklvl]['blockContext']) || (!$clear && $currpos >= $f['startpos'] && $currpos < ($f['endpos'] - 0.001) && $f['blklvl'] > $blklvl && $f['blockContext'] == $this->blk[$blklvl]['blockContext'])) {
  16574. if ($f['side'] == 'L') {
  16575. $l_exists = true;
  16576. $l_max = max($l_max, $f['endpos']);
  16577. $l_width = max($l_width, $f['w']);
  16578. }
  16579. if ($f['side'] == 'R') {
  16580. $r_exists = true;
  16581. $r_max = max($r_max, $f['endpos']);
  16582. $r_width = max($r_width, $f['w']);
  16583. }
  16584. }
  16585. }
  16586. }
  16587. return array($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width);
  16588. }
  16589. /* -- END CSS-FLOAT -- */
  16590. // LIST MARKERS // mPDF 6 Lists
  16591. function _setListMarker($listitemtype, $listitemimage, $listitemposition)
  16592. {
  16593. // if position:inside (and NOT table) - output now as a textbuffer; (so if next is block, will move to new line)
  16594. // elseif position:outside (and NOT table) - output in front of first textbuffer output by setting listitem (cf. _saveTextBuffer)
  16595. $e = '';
  16596. $this->listitem = '';
  16597. $spacer = ' ';
  16598. // IMAGE
  16599. if ($listitemimage && $listitemimage != 'none') {
  16600. $listitemimage = trim(preg_replace('/url\(["\']*(.*?)["\']*\)/', '\\1', $listitemimage));
  16601. // ? Restrict maximum height/width of list marker??
  16602. $maxWidth = 100;
  16603. $maxHeight = 100;
  16604. $objattr = array();
  16605. $objattr['margin_top'] = 0;
  16606. $objattr['margin_bottom'] = 0;
  16607. $objattr['margin_left'] = 0;
  16608. $objattr['margin_right'] = 0;
  16609. $objattr['padding_top'] = 0;
  16610. $objattr['padding_bottom'] = 0;
  16611. $objattr['padding_left'] = 0;
  16612. $objattr['padding_right'] = 0;
  16613. $objattr['width'] = 0;
  16614. $objattr['height'] = 0;
  16615. $objattr['border_top']['w'] = 0;
  16616. $objattr['border_bottom']['w'] = 0;
  16617. $objattr['border_left']['w'] = 0;
  16618. $objattr['border_right']['w'] = 0;
  16619. $objattr['visibility'] = 'visible';
  16620. $srcpath = $listitemimage;
  16621. $orig_srcpath = $listitemimage;
  16622. $objattr['vertical-align'] = 'BS'; // vertical alignment of marker (baseline)
  16623. $w = 0;
  16624. $h = 0;
  16625. // Image file
  16626. $info = $this->_getImage($srcpath, true, true, $orig_srcpath);
  16627. if (!$info)
  16628. return;
  16629. if ($info['w'] == 0 && $info['h'] == 0) {
  16630. $info['h'] = $this->ConvertSize('1em', $this->blk[$this->blklvl]['inner_width'], $this->FontSize, false);
  16631. }
  16632. $objattr['file'] = $srcpath;
  16633. //Default width and height calculation if needed
  16634. if ($w == 0 and $h == 0) {
  16635. /* -- IMAGES-WMF -- */
  16636. if ($info['type'] == 'wmf') {
  16637. // WMF units are twips (1/20pt)
  16638. // divide by 20 to get points
  16639. // divide by k to get user units
  16640. $w = abs($info['w']) / (20 * _MPDFK);
  16641. $h = abs($info['h']) / (20 * _MPDFK);
  16642. } else
  16643. /* -- END IMAGES-WMF -- */
  16644. if ($info['type'] == 'svg') {
  16645. // SVG units are pixels
  16646. $w = abs($info['w']) / _MPDFK;
  16647. $h = abs($info['h']) / _MPDFK;
  16648. } else {
  16649. //Put image at default image dpi
  16650. $w = ($info['w'] / _MPDFK) * (72 / $this->img_dpi);
  16651. $h = ($info['h'] / _MPDFK) * (72 / $this->img_dpi);
  16652. }
  16653. }
  16654. // IF WIDTH OR HEIGHT SPECIFIED
  16655. if ($w == 0)
  16656. $w = abs($h * $info['w'] / $info['h']);
  16657. if ($h == 0)
  16658. $h = abs($w * $info['h'] / $info['w']);
  16659. if ($w > $maxWidth) {
  16660. $w = $maxWidth;
  16661. $h = abs($w * $info['h'] / $info['w']);
  16662. }
  16663. if ($h > $maxHeight) {
  16664. $h = $maxHeight;
  16665. $w = abs($h * $info['w'] / $info['h']);
  16666. }
  16667. $objattr['type'] = 'image';
  16668. $objattr['itype'] = $info['type'];
  16669. $objattr['orig_h'] = $info['h'];
  16670. $objattr['orig_w'] = $info['w'];
  16671. /* -- IMAGES-WMF -- */
  16672. if ($info['type'] == 'wmf') {
  16673. $objattr['wmf_x'] = $info['x'];
  16674. $objattr['wmf_y'] = $info['y'];
  16675. } else
  16676. /* -- END IMAGES-WMF -- */
  16677. if ($info['type'] == 'svg') {
  16678. $objattr['wmf_x'] = $info['x'];
  16679. $objattr['wmf_y'] = $info['y'];
  16680. }
  16681. $objattr['height'] = $h;
  16682. $objattr['width'] = $w;
  16683. $objattr['image_height'] = $h;
  16684. $objattr['image_width'] = $w;
  16685. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  16686. $objattr['listmarker'] = true;
  16687. $objattr['listmarkerposition'] = $listitemposition;
  16688. $e = "\xbb\xa4\xactype=image,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
  16689. $this->_saveTextBuffer($e);
  16690. if ($listitemposition == 'inside') {
  16691. $e = $spacer;
  16692. $this->_saveTextBuffer($e);
  16693. }
  16694. }
  16695. // SYMBOL (needs new font)
  16696. elseif ($listitemtype == 'disc' || $listitemtype == 'circle' || $listitemtype == 'square') {
  16697. $objattr = array();
  16698. $objattr['type'] = 'listmarker';
  16699. $objattr['listmarkerposition'] = $listitemposition;
  16700. $objattr['width'] = 0;
  16701. $size = $this->ConvertSize($this->list_symbol_size, $this->FontSize);
  16702. $objattr['size'] = $size;
  16703. $objattr['offset'] = $this->ConvertSize($this->list_marker_offset, $this->FontSize);
  16704. if ($listitemposition == 'inside') {
  16705. $objattr['width'] = $size + $objattr['offset'];
  16706. }
  16707. $objattr['height'] = $this->FontSize;
  16708. $objattr['vertical-align'] = 'T';
  16709. $objattr['text'] = '';
  16710. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  16711. $objattr['bullet'] = $listitemtype;
  16712. $objattr['colorarray'] = $this->colorarray;
  16713. $objattr['fontfamily'] = $this->FontFamily;
  16714. $objattr['fontsize'] = $this->FontSize;
  16715. $objattr['fontsizept'] = $this->FontSizePt;
  16716. $objattr['fontstyle'] = $this->FontStyle;
  16717. $e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
  16718. $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
  16719. // if ($listitemposition == 'inside') {
  16720. // $e = $spacer;
  16721. // $this->_saveTextBuffer($e);
  16722. // }
  16723. }
  16724. // SYMBOL 2 (needs new font)
  16725. elseif (preg_match('/U\+([a-fA-F0-9]+)/i', $listitemtype, $m)) {
  16726. if ($this->_charDefined($this->CurrentFont['cw'], hexdec($m[1]))) {
  16727. $list_item_marker = codeHex2utf($m[1]);
  16728. } else {
  16729. $list_item_marker = '-';
  16730. }
  16731. if (preg_match('/rgb\(.*?\)/', $listitemtype, $m)) {
  16732. $list_item_color = $this->ConvertColor($m[0]);
  16733. } else {
  16734. $list_item_color = '';
  16735. }
  16736. // SAVE then SET COLR
  16737. $save_colorarray = $this->colorarray;
  16738. if ($list_item_color) {
  16739. $this->colorarray = $list_item_color;
  16740. }
  16741. if ($listitemposition == 'inside') {
  16742. $e = $list_item_marker . $spacer;
  16743. $this->_saveTextBuffer($e);
  16744. } else {
  16745. $objattr = array();
  16746. $objattr['type'] = 'listmarker';
  16747. $objattr['width'] = 0;
  16748. $objattr['height'] = $this->FontSize;
  16749. $objattr['vertical-align'] = 'T';
  16750. $objattr['text'] = $list_item_marker;
  16751. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  16752. $objattr['colorarray'] = $this->colorarray;
  16753. $objattr['fontfamily'] = $this->FontFamily;
  16754. $objattr['fontsize'] = $this->FontSize;
  16755. $objattr['fontsizept'] = $this->FontSizePt;
  16756. $objattr['fontstyle'] = $this->FontStyle;
  16757. $e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
  16758. $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
  16759. }
  16760. // RESET COLOR
  16761. $this->colorarray = $save_colorarray;
  16762. }
  16763. // TEXT
  16764. else {
  16765. $counter = $this->listcounter[$this->listlvl];
  16766. if ($listitemtype == 'none') {
  16767. return;
  16768. }
  16769. $num = $this->_getStyledNumber($counter, $listitemtype, true);
  16770. if ($listitemposition == 'inside') {
  16771. $e = $num . $this->list_number_suffix . $spacer;
  16772. $this->_saveTextBuffer($e);
  16773. } else {
  16774. if (isset($this->blk[$this->blklvl]['direction']) && $this->blk[$this->blklvl]['direction'] == 'rtl') {
  16775. // REPLACE MIRRORED RTL $this->list_number_suffix e.g. ) -> ( (NB could use UCDN::$mirror_pairs)
  16776. $m = strtr($this->list_number_suffix, ")]}", "([{") . $num;
  16777. } else {
  16778. $m = $num . $this->list_number_suffix;
  16779. }
  16780. $objattr = array();
  16781. $objattr['type'] = 'listmarker';
  16782. $objattr['width'] = 0;
  16783. $objattr['height'] = $this->FontSize;
  16784. $objattr['vertical-align'] = 'T';
  16785. $objattr['text'] = $m;
  16786. $objattr['dir'] = (isset($this->blk[$this->blklvl]['direction']) ? $this->blk[$this->blklvl]['direction'] : 'ltr');
  16787. $objattr['colorarray'] = $this->colorarray;
  16788. $objattr['fontfamily'] = $this->FontFamily;
  16789. $objattr['fontsize'] = $this->FontSize;
  16790. $objattr['fontsizept'] = $this->FontSizePt;
  16791. $objattr['fontstyle'] = $this->FontStyle;
  16792. $e = "\xbb\xa4\xactype=listmarker,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
  16793. $this->listitem = $this->_saveTextBuffer($e, '', '', true); // true returns array
  16794. }
  16795. }
  16796. }
  16797. // mPDF Lists
  16798. function _getListMarkerWidth(&$currblk, &$a, &$i)
  16799. {
  16800. $blt_width = 0;
  16801. $markeroffset = $this->ConvertSize($this->list_marker_offset, $this->FontSize);
  16802. // Get Maximum number in the list
  16803. $maxnum = $this->listcounter[$this->listlvl];
  16804. if ($currblk['list_style_type'] != 'disc' && $currblk['list_style_type'] != 'circle' && $currblk['list_style_type'] != 'square') {
  16805. $lvl = 1;
  16806. for ($j = $i + 2; $j < count($a); $j+=2) {
  16807. $e = $a[$j];
  16808. if (!$e) {
  16809. continue;
  16810. }
  16811. if ($e[0] == '/') { // end tag
  16812. $e = strtoupper(substr($e, 1));
  16813. if ($e == 'OL' || $e == 'UL') {
  16814. if ($lvl == 1) {
  16815. break;
  16816. }
  16817. $lvl--;
  16818. }
  16819. } else { // opening tag
  16820. if (strpos($e, ' ')) {
  16821. $e = substr($e, 0, strpos($e, ' '));
  16822. }
  16823. $e = strtoupper($e);
  16824. if ($e == 'LI') {
  16825. if ($lvl == 1) {
  16826. $maxnum++;
  16827. }
  16828. } elseif ($e == 'OL' || $e == 'UL') {
  16829. $lvl++;
  16830. }
  16831. }
  16832. }
  16833. }
  16834. switch ($currblk['list_style_type']) {
  16835. case 'decimal':
  16836. case '1':
  16837. $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
  16838. break;
  16839. case 'none':
  16840. $blt_width = 0;
  16841. break;
  16842. case 'upper-alpha':
  16843. case 'upper-latin':
  16844. case 'A':
  16845. $maxnumA = $this->dec2alpha($maxnum, true);
  16846. if ($maxnum < 13) {
  16847. $blt_width = $this->GetStringWidth('D' . $this->list_number_suffix);
  16848. } else {
  16849. $blt_width = $this->GetStringWidth(str_repeat('W', strlen($maxnumA)) . $this->list_number_suffix);
  16850. }
  16851. break;
  16852. case 'lower-alpha':
  16853. case 'lower-latin':
  16854. case 'a':
  16855. $maxnuma = $this->dec2alpha($maxnum, false);
  16856. if ($maxnum < 13) {
  16857. $blt_width = $this->GetStringWidth('b' . $this->list_number_suffix);
  16858. } else {
  16859. $blt_width = $this->GetStringWidth(str_repeat('m', strlen($maxnuma)) . $this->list_number_suffix);
  16860. }
  16861. break;
  16862. case 'upper-roman':
  16863. case 'I':
  16864. if ($maxnum > 87) {
  16865. $bbit = 87;
  16866. } elseif ($maxnum > 86) {
  16867. $bbit = 86;
  16868. } elseif ($maxnum > 37) {
  16869. $bbit = 38;
  16870. } elseif ($maxnum > 36) {
  16871. $bbit = 37;
  16872. } elseif ($maxnum > 27) {
  16873. $bbit = 28;
  16874. } elseif ($maxnum > 26) {
  16875. $bbit = 27;
  16876. } elseif ($maxnum > 17) {
  16877. $bbit = 18;
  16878. } elseif ($maxnum > 16) {
  16879. $bbit = 17;
  16880. } elseif ($maxnum > 7) {
  16881. $bbit = 8;
  16882. } elseif ($maxnum > 6) {
  16883. $bbit = 7;
  16884. } elseif ($maxnum > 3) {
  16885. $bbit = 4;
  16886. } else {
  16887. $bbit = $maxnum;
  16888. }
  16889. $maxlnum = $this->dec2roman($bbit, true);
  16890. $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
  16891. break;
  16892. case 'lower-roman':
  16893. case 'i':
  16894. if ($maxnum > 87) {
  16895. $bbit = 87;
  16896. } elseif ($maxnum > 86) {
  16897. $bbit = 86;
  16898. } elseif ($maxnum > 37) {
  16899. $bbit = 38;
  16900. } elseif ($maxnum > 36) {
  16901. $bbit = 37;
  16902. } elseif ($maxnum > 27) {
  16903. $bbit = 28;
  16904. } elseif ($maxnum > 26) {
  16905. $bbit = 27;
  16906. } elseif ($maxnum > 17) {
  16907. $bbit = 18;
  16908. } elseif ($maxnum > 16) {
  16909. $bbit = 17;
  16910. } elseif ($maxnum > 7) {
  16911. $bbit = 8;
  16912. } elseif ($maxnum > 6) {
  16913. $bbit = 7;
  16914. } elseif ($maxnum > 3) {
  16915. $bbit = 4;
  16916. } else {
  16917. $bbit = $maxnum;
  16918. }
  16919. $maxlnum = $this->dec2roman($bbit, false);
  16920. $blt_width = $this->GetStringWidth($maxlnum . $this->list_number_suffix);
  16921. break;
  16922. case 'disc':
  16923. case 'circle':
  16924. case 'square':
  16925. $size = $this->ConvertSize($this->list_symbol_size, $this->FontSize);
  16926. $offset = $this->ConvertSize($this->list_marker_offset, $this->FontSize);
  16927. $blt_width = $size + $offset;
  16928. break;
  16929. case 'arabic-indic':
  16930. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0660), strlen($maxnum)) . $this->list_number_suffix);
  16931. break;
  16932. case 'persian':
  16933. case 'urdu':
  16934. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x06F0), strlen($maxnum)) . $this->list_number_suffix);
  16935. break;
  16936. case 'bengali':
  16937. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x09E6), strlen($maxnum)) . $this->list_number_suffix);
  16938. break;
  16939. case 'devanagari':
  16940. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0966), strlen($maxnum)) . $this->list_number_suffix);
  16941. break;
  16942. case 'gujarati':
  16943. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0AE6), strlen($maxnum)) . $this->list_number_suffix);
  16944. break;
  16945. case 'gurmukhi':
  16946. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0A66), strlen($maxnum)) . $this->list_number_suffix);
  16947. break;
  16948. case 'kannada':
  16949. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0CE6), strlen($maxnum)) . $this->list_number_suffix);
  16950. break;
  16951. case 'malayalam':
  16952. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(6, 0x0D66), strlen($maxnum)) . $this->list_number_suffix);
  16953. break;
  16954. case 'oriya':
  16955. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0B66), strlen($maxnum)) . $this->list_number_suffix);
  16956. break;
  16957. case 'telugu':
  16958. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(3, 0x0C66), strlen($maxnum)) . $this->list_number_suffix);
  16959. break;
  16960. case 'tamil':
  16961. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(9, 0x0BE6), strlen($maxnum)) . $this->list_number_suffix);
  16962. break;
  16963. case 'thai':
  16964. $blt_width = $this->GetStringWidth(str_repeat($this->dec2other(5, 0x0E50), strlen($maxnum)) . $this->list_number_suffix);
  16965. break;
  16966. default:
  16967. $blt_width = $this->GetStringWidth(str_repeat('5', strlen($maxnum)) . $this->list_number_suffix);
  16968. break;
  16969. }
  16970. return ($blt_width + $markeroffset);
  16971. }
  16972. /* -- TABLES -- */
  16973. // This function determines the shrink factor when resizing tables
  16974. // val is the table_height / page_height_available
  16975. // returns a scaling factor used as $shrin_k to resize the table
  16976. // Overcompensating will be quicker but may unnecessarily shrink table too much
  16977. // Undercompensating means it will reiterate more times (taking more processing time)
  16978. function tbsqrt($val, $iteration = 3)
  16979. {
  16980. $k = 4; // Alters number of iterations until it returns $val itself - Must be > 2
  16981. // Probably best guess and most accurate
  16982. if ($iteration == 1)
  16983. return sqrt($val);
  16984. // Faster than using sqrt (because it won't undercompensate), and gives reasonable results
  16985. //return 1+(($val-1)/2);
  16986. $x = 2 - (($iteration - 2) / ($k - 2));
  16987. if ($x == 0) {
  16988. $ret = $val + 0.00001;
  16989. } elseif ($x < 0) {
  16990. $ret = 1 + ( pow(2, ($iteration - 2 - $k)) / 1000 );
  16991. } else {
  16992. $ret = 1 + (($val - 1) / $x);
  16993. }
  16994. return $ret;
  16995. }
  16996. /* -- END TABLES -- */
  16997. function _saveTextBuffer($t, $link = '', $intlink = '', $return = false)
  16998. { // mPDF 6 Lists
  16999. $arr = array();
  17000. $arr[0] = $t;
  17001. if (isset($link) && $link)
  17002. $arr[1] = $link;
  17003. $arr[2] = $this->currentfontstyle;
  17004. if (isset($this->colorarray) && $this->colorarray)
  17005. $arr[3] = $this->colorarray;
  17006. $arr[4] = $this->currentfontfamily;
  17007. $arr[5] = $this->currentLang; // mPDF 6
  17008. if (isset($intlink) && $intlink)
  17009. $arr[7] = $intlink;
  17010. // mPDF 6
  17011. // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
  17012. // set for kerning via kern table
  17013. // e.g. Latin script when useOTL set as 0x80
  17014. if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
  17015. $this->textvar = ($this->textvar | FC_KERNING);
  17016. }
  17017. $arr[8] = $this->textvar; // mPDF 5.7.1
  17018. if (isset($this->textparam) && $this->textparam)
  17019. $arr[9] = $this->textparam;
  17020. if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray)
  17021. $arr[10] = $this->spanbgcolorarray;
  17022. $arr[11] = $this->currentfontsize;
  17023. if (isset($this->ReqFontStyle) && $this->ReqFontStyle)
  17024. $arr[12] = $this->ReqFontStyle;
  17025. if (isset($this->lSpacingCSS) && $this->lSpacingCSS)
  17026. $arr[14] = $this->lSpacingCSS;
  17027. if (isset($this->wSpacingCSS) && $this->wSpacingCSS)
  17028. $arr[15] = $this->wSpacingCSS;
  17029. if (isset($this->spanborddet) && $this->spanborddet)
  17030. $arr[16] = $this->spanborddet;
  17031. if (isset($this->textshadow) && $this->textshadow)
  17032. $arr[17] = $this->textshadow;
  17033. if (isset($this->OTLdata) && $this->OTLdata) {
  17034. $arr[18] = $this->OTLdata;
  17035. $this->OTLdata = array();
  17036. } // mPDF 5.7.1
  17037. else {
  17038. $arr[18] = NULL;
  17039. }
  17040. // mPDF 6 Lists
  17041. if ($return) {
  17042. return ($arr);
  17043. }
  17044. if ($this->listitem) {
  17045. $this->textbuffer[] = $this->listitem;
  17046. $this->listitem = array();
  17047. }
  17048. $this->textbuffer[] = $arr;
  17049. }
  17050. function _saveCellTextBuffer($t, $link = '', $intlink = '')
  17051. {
  17052. $arr = array();
  17053. $arr[0] = $t;
  17054. if (isset($link) && $link)
  17055. $arr[1] = $link;
  17056. $arr[2] = $this->currentfontstyle;
  17057. if (isset($this->colorarray) && $this->colorarray)
  17058. $arr[3] = $this->colorarray;
  17059. $arr[4] = $this->currentfontfamily;
  17060. if (isset($intlink) && $intlink)
  17061. $arr[7] = $intlink;
  17062. // mPDF 6
  17063. // If Kerning set for OTL, and useOTL has positive value, but has not set for this particular script,
  17064. // set for kerning via kern table
  17065. // e.g. Latin script when useOTL set as 0x80
  17066. if (isset($this->OTLtags['Plus']) && strpos($this->OTLtags['Plus'], 'kern') !== false && empty($this->OTLdata['GPOSinfo'])) {
  17067. $this->textvar = ($this->textvar | FC_KERNING);
  17068. }
  17069. $arr[8] = $this->textvar; // mPDF 5.7.1
  17070. if (isset($this->textparam) && $this->textparam)
  17071. $arr[9] = $this->textparam;
  17072. if (isset($this->spanbgcolorarray) && $this->spanbgcolorarray)
  17073. $arr[10] = $this->spanbgcolorarray;
  17074. $arr[11] = $this->currentfontsize;
  17075. if (isset($this->ReqFontStyle) && $this->ReqFontStyle)
  17076. $arr[12] = $this->ReqFontStyle;
  17077. if (isset($this->lSpacingCSS) && $this->lSpacingCSS)
  17078. $arr[14] = $this->lSpacingCSS;
  17079. if (isset($this->wSpacingCSS) && $this->wSpacingCSS)
  17080. $arr[15] = $this->wSpacingCSS;
  17081. if (isset($this->spanborddet) && $this->spanborddet)
  17082. $arr[16] = $this->spanborddet;
  17083. if (isset($this->textshadow) && $this->textshadow)
  17084. $arr[17] = $this->textshadow;
  17085. if (isset($this->OTLdata) && $this->OTLdata) {
  17086. $arr[18] = $this->OTLdata;
  17087. $this->OTLdata = array();
  17088. } // mPDF 5.7.1
  17089. else {
  17090. $arr[18] = NULL;
  17091. }
  17092. $this->cell[$this->row][$this->col]['textbuffer'][] = $arr;
  17093. }
  17094. function printbuffer($arrayaux, $blockstate = 0, $is_table = false, $table_draft = false, $cell_dir = '')
  17095. {
  17096. // $blockstate = 0; // NO margins/padding
  17097. // $blockstate = 1; // Top margins/padding only
  17098. // $blockstate = 2; // Bottom margins/padding only
  17099. // $blockstate = 3; // Top & bottom margins/padding
  17100. $this->spanbgcolorarray = '';
  17101. $this->spanbgcolor = false;
  17102. $this->spanborder = false;
  17103. $this->spanborddet = array();
  17104. $paint_ht_corr = 0;
  17105. /* -- CSS-FLOAT -- */
  17106. if (count($this->floatDivs)) {
  17107. list($l_exists, $r_exists, $l_max, $r_max, $l_width, $r_width) = $this->GetFloatDivInfo($this->blklvl);
  17108. if (($this->blk[$this->blklvl]['inner_width'] - $l_width - $r_width) < (2 * $this->GetCharWidth('W', false))) {
  17109. // Too narrow to fit - try to move down past L or R float
  17110. if ($l_max < $r_max && ($this->blk[$this->blklvl]['inner_width'] - $r_width) > (2 * $this->GetCharWidth('W', false))) {
  17111. $this->ClearFloats('LEFT', $this->blklvl);
  17112. } elseif ($r_max < $l_max && ($this->blk[$this->blklvl]['inner_width'] - $l_width) > (2 * $this->GetCharWidth('W', false))) {
  17113. $this->ClearFloats('RIGHT', $this->blklvl);
  17114. } else {
  17115. $this->ClearFloats('BOTH', $this->blklvl);
  17116. }
  17117. }
  17118. }
  17119. /* -- END CSS-FLOAT -- */
  17120. $bak_y = $this->y;
  17121. $bak_x = $this->x;
  17122. $align = '';
  17123. if (!$is_table) {
  17124. if (isset($this->blk[$this->blklvl]['align']) && $this->blk[$this->blklvl]['align']) {
  17125. $align = $this->blk[$this->blklvl]['align'];
  17126. }
  17127. // Block-align is set by e.g. <.. align="center"> Takes priority for this block but not inherited
  17128. if (isset($this->blk[$this->blklvl]['block-align']) && $this->blk[$this->blklvl]['block-align']) {
  17129. $align = $this->blk[$this->blklvl]['block-align'];
  17130. }
  17131. if (isset($this->blk[$this->blklvl]['direction']))
  17132. $blockdir = $this->blk[$this->blklvl]['direction'];
  17133. else
  17134. $blockdir = "";
  17135. $this->divwidth = $this->blk[$this->blklvl]['width'];
  17136. }
  17137. else {
  17138. $align = $this->cellTextAlign;
  17139. $blockdir = $cell_dir;
  17140. }
  17141. $oldpage = $this->page;
  17142. // ADDED for Out of Block now done as Flowing Block
  17143. if ($this->divwidth == 0) {
  17144. $this->divwidth = $this->pgwidth;
  17145. }
  17146. if (!$is_table) {
  17147. $this->SetLineHeight($this->FontSizePt, $this->blk[$this->blklvl]['line_height']);
  17148. }
  17149. $this->divheight = $this->lineheight;
  17150. $old_height = $this->divheight;
  17151. // As a failsafe - if font has been set but not output to page
  17152. if (!$table_draft)
  17153. $this->SetFont($this->default_font, '', $this->default_font_size, true, true); // force output to page
  17154. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, true, $blockdir, $table_draft);
  17155. $array_size = count($arrayaux);
  17156. // Added - Otherwise <div><div><p> did not output top margins/padding for 1st/2nd div
  17157. if ($array_size == 0) {
  17158. $this->finishFlowingBlock(true);
  17159. } // true = END of flowing block
  17160. // mPDF 6
  17161. // ALL the chunks of textbuffer need to have at least basic OTLdata set
  17162. // First make sure each element/chunk has the OTLdata for Bidi set.
  17163. for ($i = 0; $i < $array_size; $i++) {
  17164. if (empty($arrayaux[$i][18])) {
  17165. if (substr($arrayaux[$i][0], 0, 3) == "\xbb\xa4\xac") { // object identifier has been identified!
  17166. $unicode = array(0xFFFC); // Object replacement character
  17167. } else {
  17168. $unicode = $this->UTF8StringToArray($arrayaux[$i][0], false);
  17169. }
  17170. $is_strong = false;
  17171. $this->getBasicOTLdata($arrayaux[$i][18], $unicode, $is_strong);
  17172. }
  17173. // Gets messed up if try and use core fonts inside a paragraph of text which needs to be BiDi re-ordered or OTLdata set
  17174. if (($blockdir == 'rtl' || $this->biDirectional) && isset($arrayaux[$i][4]) && in_array($arrayaux[$i][4], array('ccourier', 'ctimes', 'chelvetica', 'csymbol', 'czapfdingbats'))) {
  17175. throw new MpdfException("You cannot use core fonts in a document which contains RTL text.");
  17176. }
  17177. }
  17178. // mPDF 6
  17179. // Process bidirectional text ready for bidi-re-ordering (which is done after line-breaks are established in WriteFlowingBlock etc.)
  17180. if (($blockdir == 'rtl' || $this->biDirectional) && !$table_draft) {
  17181. if (!class_exists('otl', false)) {
  17182. include(_MPDF_PATH . 'classes/otl.php');
  17183. }
  17184. if (empty($this->otl)) {
  17185. $this->otl = new otl($this);
  17186. }
  17187. $this->otl->_bidiPrepare($arrayaux, $blockdir);
  17188. $array_size = count($arrayaux);
  17189. }
  17190. // Remove empty items // mPDF 6
  17191. for ($i = $array_size - 1; $i > 0; $i--) {
  17192. if (empty($arrayaux[$i][0]) && (isset($arrayaux[$i][16]) && $arrayaux[$i][16] !== '0') && empty($arrayaux[$i][7])) {
  17193. unset($arrayaux[$i]);
  17194. }
  17195. }
  17196. // Correct adjoining borders for inline elements
  17197. if (isset($arrayaux[0][16])) {
  17198. $lastspanborder = $arrayaux[0][16];
  17199. } else {
  17200. $lastspanborder = false;
  17201. }
  17202. for ($i = 1; $i < $array_size; $i++) {
  17203. if (isset($arrayaux[$i][16]) && $arrayaux[$i][16] == $lastspanborder &&
  17204. ((!isset($arrayaux[$i][9]['bord-decoration']) && !isset($arrayaux[$i - 1][9]['bord-decoration'])) ||
  17205. (isset($arrayaux[$i][9]['bord-decoration']) && isset($arrayaux[$i - 1][9]['bord-decoration']) && $arrayaux[$i][9]['bord-decoration'] == $arrayaux[$i - 1][9]['bord-decoration'])
  17206. )
  17207. ) {
  17208. if (isset($arrayaux[$i][16]['R'])) {
  17209. $lastspanborder = $arrayaux[$i][16];
  17210. } else {
  17211. $lastspanborder = false;
  17212. }
  17213. $arrayaux[$i][16]['L']['s'] = 0;
  17214. $arrayaux[$i][16]['L']['w'] = 0;
  17215. $arrayaux[$i - 1][16]['R']['s'] = 0;
  17216. $arrayaux[$i - 1][16]['R']['w'] = 0;
  17217. } else {
  17218. if (isset($arrayaux[$i][16]['R'])) {
  17219. $lastspanborder = $arrayaux[$i][16];
  17220. } else {
  17221. $lastspanborder = false;
  17222. }
  17223. }
  17224. }
  17225. for ($i = 0; $i < $array_size; $i++) {
  17226. // COLS
  17227. $oldcolumn = $this->CurrCol;
  17228. $vetor = isset($arrayaux[$i]) ? $arrayaux[$i] : NULL;
  17229. if ($i == 0 && $vetor[0] != "\n" && ! $this->ispre) {
  17230. $vetor[0] = ltrim($vetor[0]);
  17231. if (!empty($vetor[18])) {
  17232. $this->otl->trimOTLdata($vetor[18], true, false);
  17233. } // *OTL*
  17234. }
  17235. // FIXED TO ALLOW IT TO SHOW '0'
  17236. if (empty($vetor[0]) && !($vetor[0] === '0') && empty($vetor[7])) { //Ignore empty text and not carrying an internal link
  17237. //Check if it is the last element. If so then finish printing the block
  17238. if ($i == ($array_size - 1)) {
  17239. $this->finishFlowingBlock(true);
  17240. } // true = END of flowing block
  17241. continue;
  17242. }
  17243. //Activating buffer properties
  17244. if (isset($vetor[11]) && $vetor[11] != '') { // Font Size
  17245. if ($is_table && $this->shrin_k) {
  17246. $this->SetFontSize($vetor[11] / $this->shrin_k, false);
  17247. } else {
  17248. $this->SetFontSize($vetor[11], false);
  17249. }
  17250. }
  17251. if (isset($vetor[17]) && !empty($vetor[17])) { //TextShadow
  17252. $this->textshadow = $vetor[17];
  17253. }
  17254. if (isset($vetor[16]) && !empty($vetor[16])) { //Border
  17255. $this->spanborddet = $vetor[16];
  17256. $this->spanborder = true;
  17257. }
  17258. if (isset($vetor[15])) { // Word spacing
  17259. $this->wSpacingCSS = $vetor[15];
  17260. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  17261. $this->minwSpacing = $this->ConvertSize($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  17262. }
  17263. }
  17264. if (isset($vetor[14])) { // Letter spacing
  17265. $this->lSpacingCSS = $vetor[14];
  17266. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  17267. $this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  17268. }
  17269. }
  17270. if (isset($vetor[10]) and ! empty($vetor[10])) { //Background color
  17271. $this->spanbgcolorarray = $vetor[10];
  17272. $this->spanbgcolor = true;
  17273. }
  17274. if (isset($vetor[9]) and ! empty($vetor[9])) { // Text parameters - Outline + hyphens
  17275. $this->textparam = $vetor[9];
  17276. $this->SetTextOutline($this->textparam);
  17277. // mPDF 5.7.3 inline text-decoration parameters
  17278. if ($is_table && $this->shrin_k) {
  17279. if (isset($this->textparam['text-baseline'])) {
  17280. $this->textparam['text-baseline'] /= $this->shrin_k;
  17281. }
  17282. if (isset($this->textparam['decoration-baseline'])) {
  17283. $this->textparam['decoration-baseline'] /= $this->shrin_k;
  17284. }
  17285. if (isset($this->textparam['decoration-fontsize'])) {
  17286. $this->textparam['decoration-fontsize'] /= $this->shrin_k;
  17287. }
  17288. }
  17289. }
  17290. if (isset($vetor[8])) { // mPDF 5.7.1
  17291. $this->textvar = $vetor[8];
  17292. }
  17293. if (isset($vetor[7]) and $vetor[7] != '') { // internal target: <a name="anyvalue">
  17294. $ily = $this->y;
  17295. if ($this->table_rotate) {
  17296. $this->internallink[$vetor[7]] = array("Y" => $ily, "PAGE" => $this->page, "tbrot" => true);
  17297. } elseif ($this->kwt) {
  17298. $this->internallink[$vetor[7]] = array("Y" => $ily, "PAGE" => $this->page, "kwt" => true);
  17299. } elseif ($this->ColActive) {
  17300. $this->internallink[$vetor[7]] = array("Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol);
  17301. } elseif (!$this->keep_block_together) {
  17302. $this->internallink[$vetor[7]] = array("Y" => $ily, "PAGE" => $this->page);
  17303. }
  17304. if (empty($vetor[0])) { //Ignore empty text
  17305. //Check if it is the last element. If so then finish printing the block
  17306. if ($i == ($array_size - 1)) {
  17307. $this->finishFlowingBlock(true);
  17308. } // true = END of flowing block
  17309. continue;
  17310. }
  17311. }
  17312. if (isset($vetor[5]) and $vetor[5] != '') { // Language // mPDF 6
  17313. $this->currentLang = $vetor[5];
  17314. }
  17315. if (isset($vetor[4]) and $vetor[4] != '') { // Font Family
  17316. $font = $this->SetFont($vetor[4], $this->FontStyle, 0, false);
  17317. }
  17318. if (!empty($vetor[3])) { //Font Color
  17319. $cor = $vetor[3];
  17320. $this->SetTColor($cor);
  17321. }
  17322. if (isset($vetor[2]) and $vetor[2] != '') { //Bold,Italic styles
  17323. $this->SetStyles($vetor[2]);
  17324. }
  17325. if (isset($vetor[12]) and $vetor[12] != '') { //Requested Bold,Italic
  17326. $this->ReqFontStyle = $vetor[12];
  17327. }
  17328. if (isset($vetor[1]) and $vetor[1] != '') { //LINK
  17329. if (strpos($vetor[1], ".") === false && strpos($vetor[1], "@") !== 0) { //assuming every external link has a dot indicating extension (e.g: .html .txt .zip www.somewhere.com etc.)
  17330. //Repeated reference to same anchor?
  17331. while (array_key_exists($vetor[1], $this->internallink))
  17332. $vetor[1] = "#" . $vetor[1];
  17333. $this->internallink[$vetor[1]] = $this->AddLink();
  17334. $vetor[1] = $this->internallink[$vetor[1]];
  17335. }
  17336. $this->HREF = $vetor[1]; // HREF link style set here ******
  17337. }
  17338. // SPECIAL CONTENT - IMAGES & FORM OBJECTS
  17339. //Print-out special content
  17340. if (substr($vetor[0], 0, 3) == "\xbb\xa4\xac") { //identifier has been identified!
  17341. $objattr = $this->_getObjAttr($vetor[0]);
  17342. /* -- TABLES -- */
  17343. if ($objattr['type'] == 'nestedtable') {
  17344. if ($objattr['nestedcontent']) {
  17345. $level = $objattr['level'];
  17346. $table = &$this->table[$level][$objattr['table']];
  17347. if ($table_draft) {
  17348. $this->y += $this->table[($level + 1)][$objattr['nestedcontent']]['h']; // nested table height
  17349. $this->finishFlowingBlock(false, 'nestedtable');
  17350. } else {
  17351. $cell = &$table['cells'][$objattr['row']][$objattr['col']];
  17352. $this->finishFlowingBlock(false, 'nestedtable');
  17353. $save_dw = $this->divwidth;
  17354. $save_buffer = $this->cellBorderBuffer;
  17355. $this->cellBorderBuffer = array();
  17356. $ncx = $this->x;
  17357. list($dummyx, $w) = $this->_tableGetWidth($table, $objattr['row'], $objattr['col']);
  17358. $ntw = $this->table[($level + 1)][$objattr['nestedcontent']]['w']; // nested table width
  17359. if (!$this->simpleTables) {
  17360. if ($this->packTableData) {
  17361. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cell['borderbin']);
  17362. } else {
  17363. $br = $cell['border_details']['R']['w'];
  17364. $bl = $cell['border_details']['L']['w'];
  17365. }
  17366. if ($table['borders_separate']) {
  17367. $innerw = $w - $bl - $br - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
  17368. } else {
  17369. $innerw = $w - $bl / 2 - $br / 2 - $cell['padding']['L'] - $cell['padding']['R'];
  17370. }
  17371. } elseif ($this->simpleTables) {
  17372. if ($table['borders_separate']) {
  17373. $innerw = $w - $table['simple']['border_details']['L']['w'] - $table['simple']['border_details']['R']['w'] - $cell['padding']['L'] - $cell['padding']['R'] - $table['border_spacing_H'];
  17374. } else {
  17375. $innerw = $w - $table['simple']['border_details']['L']['w'] / 2 - $table['simple']['border_details']['R']['w'] / 2 - $cell['padding']['L'] - $cell['padding']['R'];
  17376. }
  17377. }
  17378. if ($cell['a'] == 'C' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'C') {
  17379. $ncx += ($innerw - $ntw) / 2;
  17380. } elseif ($cell['a'] == 'R' || $this->table[($level + 1)][$objattr['nestedcontent']]['a'] == 'R') {
  17381. $ncx += $innerw - $ntw;
  17382. }
  17383. $this->x = $ncx;
  17384. $this->_tableWrite($this->table[($level + 1)][$objattr['nestedcontent']]);
  17385. $this->cellBorderBuffer = $save_buffer;
  17386. $this->x = $bak_x;
  17387. $this->divwidth = $save_dw;
  17388. }
  17389. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
  17390. }
  17391. } else {
  17392. /* -- END TABLES -- */
  17393. if ($is_table) { // *TABLES*
  17394. $maxWidth = $this->divwidth; // *TABLES*
  17395. } // *TABLES*
  17396. else { // *TABLES*
  17397. $maxWidth = $this->divwidth - ($this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_right'] + $this->blk[$this->blklvl]['border_right']['w']);
  17398. } // *TABLES*
  17399. /* -- CSS-IMAGE-FLOAT -- */
  17400. // If float (already) exists at this level
  17401. if (isset($this->floatmargins['R']) && $this->y <= $this->floatmargins['R']['y1'] && $this->y >= $this->floatmargins['R']['y0']) {
  17402. $maxWidth -= $this->floatmargins['R']['w'];
  17403. }
  17404. if (isset($this->floatmargins['L']) && $this->y <= $this->floatmargins['L']['y1'] && $this->y >= $this->floatmargins['L']['y0']) {
  17405. $maxWidth -= $this->floatmargins['L']['w'];
  17406. }
  17407. /* -- END CSS-IMAGE-FLOAT -- */
  17408. list($skipln) = $this->inlineObject($objattr['type'], '', $this->y, $objattr, $this->lMargin, ($this->flowingBlockAttr['contentWidth'] / _MPDFK), $maxWidth, $this->flowingBlockAttr['height'], false, $is_table);
  17409. // 1 -> New line needed because of width
  17410. // -1 -> Will fit width on line but NEW PAGE REQUIRED because of height
  17411. // -2 -> Will not fit on line therefore needs new line but thus NEW PAGE REQUIRED
  17412. $iby = $this->y;
  17413. $oldpage = $this->page;
  17414. $oldcol = $this->CurrCol;
  17415. if (($skipln == 1 || $skipln == -2) && !isset($objattr['float'])) {
  17416. $this->finishFlowingBlock(false, $objattr['type']);
  17417. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
  17418. }
  17419. if (!$table_draft) {
  17420. $thispage = $this->page;
  17421. if ($this->CurrCol != $oldcol) {
  17422. $changedcol = true;
  17423. } else {
  17424. $changedcol = false;
  17425. }
  17426. // the previous lines can already have triggered page break or column change
  17427. if (!$changedcol && $skipln < 0 && $this->AcceptPageBreak() && $thispage == $oldpage) {
  17428. $this->AddPage($this->CurOrientation);
  17429. // Added to correct Images already set on line before page advanced
  17430. // i.e. if second inline image on line is higher than first and forces new page
  17431. if (count($this->objectbuffer)) {
  17432. $yadj = $iby - $this->y;
  17433. foreach ($this->objectbuffer AS $ib => $val) {
  17434. if ($this->objectbuffer[$ib]['OUTER-Y'])
  17435. $this->objectbuffer[$ib]['OUTER-Y'] -= $yadj;
  17436. if ($this->objectbuffer[$ib]['BORDER-Y'])
  17437. $this->objectbuffer[$ib]['BORDER-Y'] -= $yadj;
  17438. if ($this->objectbuffer[$ib]['INNER-Y'])
  17439. $this->objectbuffer[$ib]['INNER-Y'] -= $yadj;
  17440. }
  17441. }
  17442. }
  17443. // Added to correct for OddEven Margins
  17444. if ($this->page != $oldpage) {
  17445. if (($this->page - $oldpage) % 2 == 1) {
  17446. $bak_x += $this->MarginCorrection;
  17447. }
  17448. $oldpage = $this->page;
  17449. $y = $this->tMargin - $paint_ht_corr;
  17450. $this->oldy = $this->tMargin - $paint_ht_corr;
  17451. $old_height = 0;
  17452. }
  17453. $this->x = $bak_x;
  17454. /* -- COLUMNS -- */
  17455. // COLS
  17456. // OR COLUMN CHANGE
  17457. if ($this->CurrCol != $oldcolumn) {
  17458. if ($this->directionality == 'rtl') { // *OTL*
  17459. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  17460. } // *OTL*
  17461. else { // *OTL*
  17462. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  17463. } // *OTL*
  17464. $this->x = $bak_x;
  17465. $oldcolumn = $this->CurrCol;
  17466. $y = $this->y0 - $paint_ht_corr;
  17467. $this->oldy = $this->y0 - $paint_ht_corr;
  17468. $old_height = 0;
  17469. }
  17470. /* -- END COLUMNS -- */
  17471. }
  17472. /* -- CSS-IMAGE-FLOAT -- */
  17473. if ($objattr['type'] == 'image' && isset($objattr['float'])) {
  17474. $fy = $this->y;
  17475. // DIV TOP MARGIN/BORDER/PADDING
  17476. if ($this->flowingBlockAttr['newblock'] && ($this->flowingBlockAttr['blockstate'] == 1 || $this->flowingBlockAttr['blockstate'] == 3) && $this->flowingBlockAttr['lineCount'] == 0) {
  17477. $fy += $this->blk[$this->blklvl]['margin_top'] + $this->blk[$this->blklvl]['padding_top'] + $this->blk[$this->blklvl]['border_top']['w'];
  17478. }
  17479. if ($objattr['float'] == 'R') {
  17480. $fx = $this->w - $this->rMargin - $objattr['width'] - ($this->blk[$this->blklvl]['outer_right_margin'] + $this->blk[$this->blklvl]['border_right']['w'] + $this->blk[$this->blklvl]['padding_right']);
  17481. } elseif ($objattr['float'] == 'L') {
  17482. $fx = $this->lMargin + ($this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left']);
  17483. }
  17484. $w = $objattr['width'];
  17485. $h = abs($objattr['height']);
  17486. $widthLeft = $maxWidth - ($this->flowingBlockAttr['contentWidth'] / _MPDFK);
  17487. $maxHeight = $this->h - ($this->tMargin + $this->margin_header + $this->bMargin + 10);
  17488. // For Images
  17489. $extraWidth = ($objattr['border_left']['w'] + $objattr['border_right']['w'] + $objattr['margin_left'] + $objattr['margin_right']);
  17490. $extraHeight = ($objattr['border_top']['w'] + $objattr['border_bottom']['w'] + $objattr['margin_top'] + $objattr['margin_bottom']);
  17491. if ($objattr['itype'] == 'wmf' || $objattr['itype'] == 'svg') {
  17492. $file = $objattr['file'];
  17493. $info = $this->formobjects[$file];
  17494. } else {
  17495. $file = $objattr['file'];
  17496. $info = $this->images[$file];
  17497. }
  17498. $img_w = $w - $extraWidth;
  17499. $img_h = $h - $extraHeight;
  17500. if ($objattr['border_left']['w']) {
  17501. $objattr['BORDER-WIDTH'] = $img_w + (($objattr['border_left']['w'] + $objattr['border_right']['w']) / 2);
  17502. $objattr['BORDER-HEIGHT'] = $img_h + (($objattr['border_top']['w'] + $objattr['border_bottom']['w']) / 2);
  17503. $objattr['BORDER-X'] = $fx + $objattr['margin_left'] + (($objattr['border_left']['w']) / 2);
  17504. $objattr['BORDER-Y'] = $fy + $objattr['margin_top'] + (($objattr['border_top']['w']) / 2);
  17505. }
  17506. $objattr['INNER-WIDTH'] = $img_w;
  17507. $objattr['INNER-HEIGHT'] = $img_h;
  17508. $objattr['INNER-X'] = $fx + $objattr['margin_left'] + ($objattr['border_left']['w']);
  17509. $objattr['INNER-Y'] = $fy + $objattr['margin_top'] + ($objattr['border_top']['w']);
  17510. $objattr['ID'] = $info['i'];
  17511. $objattr['OUTER-WIDTH'] = $w;
  17512. $objattr['OUTER-HEIGHT'] = $h;
  17513. $objattr['OUTER-X'] = $fx;
  17514. $objattr['OUTER-Y'] = $fy;
  17515. if ($objattr['float'] == 'R') {
  17516. // If R float already exists at this level
  17517. $this->floatmargins['R']['skipline'] = false;
  17518. if (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
  17519. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  17520. }
  17521. // If L float already exists at this level
  17522. elseif (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
  17523. // Final check distance between floats is not now too narrow to fit text
  17524. $mw = 2 * $this->GetCharWidth('W', false);
  17525. if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['L']['w']) < $mw) {
  17526. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  17527. } else {
  17528. $this->floatmargins['R']['x'] = $fx;
  17529. $this->floatmargins['R']['w'] = $w;
  17530. $this->floatmargins['R']['y0'] = $fy;
  17531. $this->floatmargins['R']['y1'] = $fy + $h;
  17532. if ($skipln == 1) {
  17533. $this->floatmargins['R']['skipline'] = true;
  17534. $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
  17535. $objattr['skipline'] = true;
  17536. }
  17537. $this->floatbuffer[] = $objattr;
  17538. }
  17539. } else {
  17540. $this->floatmargins['R']['x'] = $fx;
  17541. $this->floatmargins['R']['w'] = $w;
  17542. $this->floatmargins['R']['y0'] = $fy;
  17543. $this->floatmargins['R']['y1'] = $fy + $h;
  17544. if ($skipln == 1) {
  17545. $this->floatmargins['R']['skipline'] = true;
  17546. $this->floatmargins['R']['id'] = count($this->floatbuffer) + 0;
  17547. $objattr['skipline'] = true;
  17548. }
  17549. $this->floatbuffer[] = $objattr;
  17550. }
  17551. } elseif ($objattr['float'] == 'L') {
  17552. // If L float already exists at this level
  17553. $this->floatmargins['L']['skipline'] = false;
  17554. if (isset($this->floatmargins['L']['y1']) && $this->floatmargins['L']['y1'] > 0 && $fy < $this->floatmargins['L']['y1']) {
  17555. $this->floatmargins['L']['skipline'] = false;
  17556. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  17557. }
  17558. // If R float already exists at this level
  17559. elseif (isset($this->floatmargins['R']['y1']) && $this->floatmargins['R']['y1'] > 0 && $fy < $this->floatmargins['R']['y1']) {
  17560. // Final check distance between floats is not now too narrow to fit text
  17561. $mw = 2 * $this->GetCharWidth('W', false);
  17562. if (($this->blk[$this->blklvl]['inner_width'] - $w - $this->floatmargins['R']['w']) < $mw) {
  17563. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  17564. } else {
  17565. $this->floatmargins['L']['x'] = $fx + $w;
  17566. $this->floatmargins['L']['w'] = $w;
  17567. $this->floatmargins['L']['y0'] = $fy;
  17568. $this->floatmargins['L']['y1'] = $fy + $h;
  17569. if ($skipln == 1) {
  17570. $this->floatmargins['L']['skipline'] = true;
  17571. $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
  17572. $objattr['skipline'] = true;
  17573. }
  17574. $this->floatbuffer[] = $objattr;
  17575. }
  17576. } else {
  17577. $this->floatmargins['L']['x'] = $fx + $w;
  17578. $this->floatmargins['L']['w'] = $w;
  17579. $this->floatmargins['L']['y0'] = $fy;
  17580. $this->floatmargins['L']['y1'] = $fy + $h;
  17581. if ($skipln == 1) {
  17582. $this->floatmargins['L']['skipline'] = true;
  17583. $this->floatmargins['L']['id'] = count($this->floatbuffer) + 0;
  17584. $objattr['skipline'] = true;
  17585. }
  17586. $this->floatbuffer[] = $objattr;
  17587. }
  17588. }
  17589. } else {
  17590. /* -- END CSS-IMAGE-FLOAT -- */
  17591. $this->WriteFlowingBlock($vetor[0], (isset($vetor[18]) ? $vetor[18] : NULL)); // mPDF 5.7.1
  17592. /* -- CSS-IMAGE-FLOAT -- */
  17593. }
  17594. /* -- END CSS-IMAGE-FLOAT -- */
  17595. } // *TABLES*
  17596. } // END If special content
  17597. else { //THE text
  17598. if ($this->tableLevel) {
  17599. $paint_ht_corr = 0;
  17600. } // To move the y up when new column/page started if div border needed
  17601. else {
  17602. $paint_ht_corr = $this->blk[$this->blklvl]['border_top']['w'];
  17603. }
  17604. if ($vetor[0] == "\n") { //We are reading a <BR> now turned into newline ("\n")
  17605. if ($this->flowingBlockAttr['content']) {
  17606. $this->finishFlowingBlock(false, 'br');
  17607. } elseif ($is_table) {
  17608. $this->y+= $this->_computeLineheight($this->cellLineHeight);
  17609. } elseif (!$is_table) {
  17610. $this->DivLn($this->lineheight);
  17611. if ($this->ColActive) {
  17612. $this->breakpoints[$this->CurrCol][] = $this->y;
  17613. } // *COLUMNS*
  17614. }
  17615. // Added to correct for OddEven Margins
  17616. if ($this->page != $oldpage) {
  17617. if (($this->page - $oldpage) % 2 == 1) {
  17618. $bak_x += $this->MarginCorrection;
  17619. }
  17620. $oldpage = $this->page;
  17621. $y = $this->tMargin - $paint_ht_corr;
  17622. $this->oldy = $this->tMargin - $paint_ht_corr;
  17623. $old_height = 0;
  17624. }
  17625. $this->x = $bak_x;
  17626. /* -- COLUMNS -- */
  17627. // COLS
  17628. // OR COLUMN CHANGE
  17629. if ($this->CurrCol != $oldcolumn) {
  17630. if ($this->directionality == 'rtl') { // *OTL*
  17631. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  17632. } // *OTL*
  17633. else { // *OTL*
  17634. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  17635. } // *OTL*
  17636. $this->x = $bak_x;
  17637. $oldcolumn = $this->CurrCol;
  17638. $y = $this->y0 - $paint_ht_corr;
  17639. $this->oldy = $this->y0 - $paint_ht_corr;
  17640. $old_height = 0;
  17641. }
  17642. /* -- END COLUMNS -- */
  17643. $this->newFlowingBlock($this->divwidth, $this->divheight, $align, $is_table, $blockstate, false, $blockdir, $table_draft);
  17644. } else {
  17645. $this->WriteFlowingBlock($vetor[0], $vetor[18]); // mPDF 5.7.1
  17646. // Added to correct for OddEven Margins
  17647. if ($this->page != $oldpage) {
  17648. if (($this->page - $oldpage) % 2 == 1) {
  17649. $bak_x += $this->MarginCorrection;
  17650. $this->x = $bak_x;
  17651. }
  17652. $oldpage = $this->page;
  17653. $y = $this->tMargin - $paint_ht_corr;
  17654. $this->oldy = $this->tMargin - $paint_ht_corr;
  17655. $old_height = 0;
  17656. }
  17657. /* -- COLUMNS -- */
  17658. // COLS
  17659. // OR COLUMN CHANGE
  17660. if ($this->CurrCol != $oldcolumn) {
  17661. if ($this->directionality == 'rtl') { // *OTL*
  17662. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  17663. } // *OTL*
  17664. else { // *OTL*
  17665. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  17666. } // *OTL*
  17667. $this->x = $bak_x;
  17668. $oldcolumn = $this->CurrCol;
  17669. $y = $this->y0 - $paint_ht_corr;
  17670. $this->oldy = $this->y0 - $paint_ht_corr;
  17671. $old_height = 0;
  17672. }
  17673. /* -- END COLUMNS -- */
  17674. }
  17675. }
  17676. //Check if it is the last element. If so then finish printing the block
  17677. if ($i == ($array_size - 1)) {
  17678. $this->finishFlowingBlock(true); // true = END of flowing block
  17679. // Added to correct for OddEven Margins
  17680. if ($this->page != $oldpage) {
  17681. if (($this->page - $oldpage) % 2 == 1) {
  17682. $bak_x += $this->MarginCorrection;
  17683. $this->x = $bak_x;
  17684. }
  17685. $oldpage = $this->page;
  17686. $y = $this->tMargin - $paint_ht_corr;
  17687. $this->oldy = $this->tMargin - $paint_ht_corr;
  17688. $old_height = 0;
  17689. }
  17690. /* -- COLUMNS -- */
  17691. // COLS
  17692. // OR COLUMN CHANGE
  17693. if ($this->CurrCol != $oldcolumn) {
  17694. if ($this->directionality == 'rtl') { // *OTL*
  17695. $bak_x -= ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap); // *OTL*
  17696. } // *OTL*
  17697. else { // *OTL*
  17698. $bak_x += ($this->CurrCol - $oldcolumn) * ($this->ColWidth + $this->ColGap);
  17699. } // *OTL*
  17700. $this->x = $bak_x;
  17701. $oldcolumn = $this->CurrCol;
  17702. $y = $this->y0 - $paint_ht_corr;
  17703. $this->oldy = $this->y0 - $paint_ht_corr;
  17704. $old_height = 0;
  17705. }
  17706. /* -- END COLUMNS -- */
  17707. }
  17708. // RESETTING VALUES
  17709. $this->SetTColor($this->ConvertColor(0));
  17710. $this->SetDColor($this->ConvertColor(0));
  17711. $this->SetFColor($this->ConvertColor(255));
  17712. $this->colorarray = '';
  17713. $this->spanbgcolorarray = '';
  17714. $this->spanbgcolor = false;
  17715. $this->spanborder = false;
  17716. $this->spanborddet = array();
  17717. $this->HREF = '';
  17718. $this->textparam = array();
  17719. $this->SetTextOutline();
  17720. $this->textvar = 0x00; // mPDF 5.7.1
  17721. $this->OTLtags = array();
  17722. $this->textshadow = '';
  17723. $this->currentfontfamily = '';
  17724. $this->currentfontsize = '';
  17725. $this->currentfontstyle = '';
  17726. $this->currentLang = $this->default_lang; // mPDF 6
  17727. $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
  17728. /* -- TABLES -- */
  17729. if ($this->tableLevel) {
  17730. $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*
  17731. } else
  17732. /* -- END TABLES -- */
  17733. if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
  17734. $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
  17735. }
  17736. $this->ResetStyles();
  17737. $this->lSpacingCSS = '';
  17738. $this->wSpacingCSS = '';
  17739. $this->fixedlSpacing = false;
  17740. $this->minwSpacing = 0;
  17741. $this->SetDash();
  17742. $this->dash_on = false;
  17743. $this->dotted_on = false;
  17744. }//end of for(i=0;i<arraysize;i++)
  17745. $this->Reset(); // mPDF 6
  17746. // PAINT DIV BORDER // DISABLED IN COLUMNS AS DOESN'T WORK WHEN BROKEN ACROSS COLS??
  17747. if ((isset($this->blk[$this->blklvl]['border']) || isset($this->blk[$this->blklvl]['bgcolor']) || isset($this->blk[$this->blklvl]['box_shadow'])) && $blockstate && ($this->y != $this->oldy)) {
  17748. $bottom_y = $this->y; // Does not include Bottom Margin
  17749. if (isset($this->blk[$this->blklvl]['startpage']) && $this->blk[$this->blklvl]['startpage'] != $this->page && $blockstate != 1) {
  17750. $this->PaintDivBB('pagetop', $blockstate);
  17751. } elseif ($blockstate != 1) {
  17752. $this->PaintDivBB('', $blockstate);
  17753. }
  17754. $this->y = $bottom_y;
  17755. $this->x = $bak_x;
  17756. }
  17757. // Reset Font
  17758. $this->SetFontSize($this->default_font_size, false);
  17759. if ($table_draft) {
  17760. $ch = $this->y - $bak_y;
  17761. $this->y = $bak_y;
  17762. $this->x = $bak_x;
  17763. return $ch;
  17764. }
  17765. }
  17766. function _setDashBorder($style, $div, $cp, $side)
  17767. {
  17768. if ($style == 'dashed' && (($side == 'L' || $side == 'R') || ($side == 'T' && $div != 'pagetop' && !$cp) || ($side == 'B' && $div != 'pagebottom') )) {
  17769. $dashsize = 2; // final dash will be this + 1*linewidth
  17770. $dashsizek = 1.5; // ratio of Dash/Blank
  17771. $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
  17772. } elseif ($style == 'dotted' || ($side == 'T' && ($div == 'pagetop' || $cp)) || ($side == 'B' && $div == 'pagebottom')) {
  17773. //Round join and cap
  17774. $this->SetLineJoin(1);
  17775. $this->SetLineCap(1);
  17776. $this->SetDash(0.001, ($this->LineWidth * 3));
  17777. }
  17778. }
  17779. function _setBorderLine($b, $k = 1)
  17780. {
  17781. $this->SetLineWidth($b['w'] / $k);
  17782. $this->SetDColor($b['c']);
  17783. if ($b['c'][0] == 5) { // RGBa
  17784. $this->SetAlpha(ord($b['c'][4]) / 100, 'Normal', false, 'S') . "\n"; // mPDF 5.7.2
  17785. } elseif ($b['c'][0] == 6) { // CMYKa
  17786. $this->SetAlpha(ord($b['c'][5]) / 100, 'Normal', false, 'S') . "\n"; // mPDF 5.7.2
  17787. }
  17788. }
  17789. function PaintDivBB($divider = '', $blockstate = 0, $blvl = 0)
  17790. {
  17791. // Borders & backgrounds are done elsewhere for columns - messes up the repositioning in printcolumnbuffer
  17792. if ($this->ColActive) {
  17793. return;
  17794. } // *COLUMNS*
  17795. if ($this->keep_block_together) {
  17796. return;
  17797. } // mPDF 6
  17798. $save_y = $this->y;
  17799. if (!$blvl) {
  17800. $blvl = $this->blklvl;
  17801. }
  17802. $x0 = $x1 = $y0 = $y1 = 0;
  17803. // Added mPDF 3.0 Float DIV
  17804. if (isset($this->blk[$blvl]['bb_painted'][$this->page]) && $this->blk[$blvl]['bb_painted'][$this->page]) {
  17805. return;
  17806. } // *CSS-FLOAT*
  17807. if (isset($this->blk[$blvl]['x0'])) {
  17808. $x0 = $this->blk[$blvl]['x0'];
  17809. } // left
  17810. if (isset($this->blk[$blvl]['y1'])) {
  17811. $y1 = $this->blk[$blvl]['y1'];
  17812. } // bottom
  17813. // Added mPDF 3.0 Float DIV - ensures backgrounds/borders are drawn to bottom of page
  17814. if ($y1 == 0) {
  17815. if ($divider == 'pagebottom') {
  17816. $y1 = $this->h - $this->bMargin;
  17817. } else {
  17818. $y1 = $this->y;
  17819. }
  17820. }
  17821. if (isset($this->blk[$blvl]['startpage']) && $this->blk[$blvl]['startpage'] != $this->page) {
  17822. $continuingpage = true;
  17823. } else {
  17824. $continuingpage = false;
  17825. }
  17826. if (isset($this->blk[$blvl]['y0'])) {
  17827. $y0 = $this->blk[$blvl]['y0'];
  17828. }
  17829. $h = $y1 - $y0;
  17830. $w = $this->blk[$blvl]['width'];
  17831. $x1 = $x0 + $w;
  17832. // Set border-widths as used here
  17833. $border_top = $this->blk[$blvl]['border_top']['w'];
  17834. $border_bottom = $this->blk[$blvl]['border_bottom']['w'];
  17835. $border_left = $this->blk[$blvl]['border_left']['w'];
  17836. $border_right = $this->blk[$blvl]['border_right']['w'];
  17837. if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
  17838. $border_top = 0;
  17839. }
  17840. if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
  17841. $border_bottom = 0;
  17842. }
  17843. $brTL_H = 0;
  17844. $brTL_V = 0;
  17845. $brTR_H = 0;
  17846. $brTR_V = 0;
  17847. $brBL_H = 0;
  17848. $brBL_V = 0;
  17849. $brBR_H = 0;
  17850. $brBR_V = 0;
  17851. $brset = false;
  17852. /* -- BORDER-RADIUS -- */
  17853. if (isset($this->blk[$blvl]['border_radius_TL_H'])) {
  17854. $brTL_H = $this->blk[$blvl]['border_radius_TL_H'];
  17855. $brset = true;
  17856. }
  17857. if (isset($this->blk[$blvl]['border_radius_TL_V'])) {
  17858. $brTL_V = $this->blk[$blvl]['border_radius_TL_V'];
  17859. $brset = true;
  17860. }
  17861. if (isset($this->blk[$blvl]['border_radius_TR_H'])) {
  17862. $brTR_H = $this->blk[$blvl]['border_radius_TR_H'];
  17863. $brset = true;
  17864. }
  17865. if (isset($this->blk[$blvl]['border_radius_TR_V'])) {
  17866. $brTR_V = $this->blk[$blvl]['border_radius_TR_V'];
  17867. $brset = true;
  17868. }
  17869. if (isset($this->blk[$blvl]['border_radius_BR_H'])) {
  17870. $brBR_H = $this->blk[$blvl]['border_radius_BR_H'];
  17871. $brset = true;
  17872. }
  17873. if (isset($this->blk[$blvl]['border_radius_BR_V'])) {
  17874. $brBR_V = $this->blk[$blvl]['border_radius_BR_V'];
  17875. $brset = true;
  17876. }
  17877. if (isset($this->blk[$blvl]['border_radius_BL_H'])) {
  17878. $brBL_H = $this->blk[$blvl]['border_radius_BL_H'];
  17879. $brset = true;
  17880. }
  17881. if (isset($this->blk[$blvl]['border_radius_BL_V'])) {
  17882. $brBL_V = $this->blk[$blvl]['border_radius_BL_V'];
  17883. $brset = true;
  17884. }
  17885. if (!$this->blk[$blvl]['border_top'] || $divider == 'pagetop' || $continuingpage) {
  17886. $brTL_H = 0;
  17887. $brTL_V = 0;
  17888. $brTR_H = 0;
  17889. $brTR_V = 0;
  17890. }
  17891. if (!$this->blk[$blvl]['border_bottom'] || $blockstate == 1 || $divider == 'pagebottom') {
  17892. $brBL_H = 0;
  17893. $brBL_V = 0;
  17894. $brBR_H = 0;
  17895. $brBR_V = 0;
  17896. }
  17897. // Disallow border-radius if it is smaller than the border width.
  17898. if ($brTL_H < min($border_left, $border_top)) {
  17899. $brTL_H = $brTL_V = 0;
  17900. }
  17901. if ($brTL_V < min($border_left, $border_top)) {
  17902. $brTL_V = $brTL_H = 0;
  17903. }
  17904. if ($brTR_H < min($border_right, $border_top)) {
  17905. $brTR_H = $brTR_V = 0;
  17906. }
  17907. if ($brTR_V < min($border_right, $border_top)) {
  17908. $brTR_V = $brTR_H = 0;
  17909. }
  17910. if ($brBL_H < min($border_left, $border_bottom)) {
  17911. $brBL_H = $brBL_V = 0;
  17912. }
  17913. if ($brBL_V < min($border_left, $border_bottom)) {
  17914. $brBL_V = $brBL_H = 0;
  17915. }
  17916. if ($brBR_H < min($border_right, $border_bottom)) {
  17917. $brBR_H = $brBR_V = 0;
  17918. }
  17919. if ($brBR_V < min($border_right, $border_bottom)) {
  17920. $brBR_V = $brBR_H = 0;
  17921. }
  17922. // CHECK FOR radii that sum to > width or height of div ********
  17923. $f = min($h / ($brTL_V + $brBL_V + 0.001), $h / ($brTR_V + $brBR_V + 0.001), $w / ($brTL_H + $brTR_H + 0.001), $w / ($brBL_H + $brBR_H + 0.001));
  17924. if ($f < 1) {
  17925. $brTL_H *= $f;
  17926. $brTL_V *= $f;
  17927. $brTR_H *= $f;
  17928. $brTR_V *= $f;
  17929. $brBL_H *= $f;
  17930. $brBL_V *= $f;
  17931. $brBR_H *= $f;
  17932. $brBR_V *= $f;
  17933. }
  17934. /* -- END BORDER-RADIUS -- */
  17935. $tbcol = $this->ConvertColor(255);
  17936. for ($l = 0; $l <= $blvl; $l++) {
  17937. if ($this->blk[$l]['bgcolor']) {
  17938. $tbcol = $this->blk[$l]['bgcolorarray'];
  17939. }
  17940. }
  17941. // BORDERS
  17942. if (isset($this->blk[$blvl]['y0']) && $this->blk[$blvl]['y0']) {
  17943. $y0 = $this->blk[$blvl]['y0'];
  17944. }
  17945. $h = $y1 - $y0;
  17946. $w = $this->blk[$blvl]['width'];
  17947. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  17948. $tbd = $this->blk[$blvl]['border_top'];
  17949. $legend = '';
  17950. $legbreakL = 0;
  17951. $legbreakR = 0;
  17952. // BORDER LEGEND
  17953. if (isset($this->blk[$blvl]['border_legend']) && $this->blk[$blvl]['border_legend']) {
  17954. $legend = $this->blk[$blvl]['border_legend']; // Same structure array as textbuffer
  17955. $txt = $legend[0] = ltrim($legend[0]);
  17956. if (!empty($legend[18])) {
  17957. $this->otl->trimOTLdata($legend[18], true, false);
  17958. } // *OTL*
  17959. //Set font, size, style, color
  17960. $this->SetFont($legend[4], $legend[2], $legend[11]);
  17961. if (isset($legend[3]) && $legend[3]) {
  17962. $cor = $legend[3];
  17963. $this->SetTColor($cor);
  17964. }
  17965. $stringWidth = $this->GetStringWidth($txt, true, $legend[18], $legend[8]);
  17966. $save_x = $this->x;
  17967. $save_y = $this->y;
  17968. $save_currentfontfamily = $this->FontFamily;
  17969. $save_currentfontsize = $this->FontSizePt;
  17970. $save_currentfontstyle = $this->FontStyle;
  17971. $this->y = $y0 - $this->FontSize / 2 + $this->blk[$blvl]['border_top']['w'] / 2;
  17972. $this->x = $x0 + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_left']['w'];
  17973. // Set the distance from the border line to the text ? make configurable variable
  17974. $gap = 0.2 * $this->FontSize;
  17975. $legbreakL = $this->x - $gap;
  17976. $legbreakR = $this->x + $stringWidth + $gap;
  17977. $this->magic_reverse_dir($txt, $this->blk[$blvl]['direction'], $legend[18]);
  17978. $fill = '';
  17979. $this->Cell($stringWidth, $this->FontSize, $txt, '', 0, 'C', $fill, '', 0, 0, 0, 'M', $fill, false, $legend[18], $legend[8]);
  17980. // Reset
  17981. $this->x = $save_x;
  17982. $this->y = $save_y;
  17983. $this->SetFont($save_currentfontfamily, $save_currentfontstyle, $save_currentfontsize);
  17984. $this->SetTColor($this->ConvertColor(0));
  17985. }
  17986. if (isset($tbd['s']) && $tbd['s']) {
  17987. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  17988. $this->_out('q');
  17989. $this->SetLineWidth(0);
  17990. $this->_out(sprintf('%.3F %.3F m ', ($x0) * _MPDFK, ($this->h - ($y0)) * _MPDFK));
  17991. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * _MPDFK, ($this->h - ($y0 + $border_top)) * _MPDFK));
  17992. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * _MPDFK, ($this->h - ($y0 + $border_top)) * _MPDFK));
  17993. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w) * _MPDFK, ($this->h - ($y0)) * _MPDFK));
  17994. $this->_out(' h W n '); // Ends path no-op & Sets the clipping path
  17995. }
  17996. $this->_setBorderLine($tbd);
  17997. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  17998. $legbreakL -= $border_top / 2; // because line cap different
  17999. $legbreakR += $border_top / 2;
  18000. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'T');
  18001. }
  18002. /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brTR_V && $brTR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18003. $this->SetLineJoin(0);
  18004. $this->SetLineCap(0);
  18005. }
  18006. $s = '';
  18007. if ($brTR_H && $brTR_V) {
  18008. $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_top / 2, $brTR_V - $border_top / 2, 1, 2, true)) . "\n";
  18009. } else
  18010. /* -- END BORDER-RADIUS -- */
  18011. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18012. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18013. } else {
  18014. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_top / 2)) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18015. }
  18016. /* -- BORDER-RADIUS -- */
  18017. if ($brTL_H && $brTL_V) {
  18018. if ($legend) {
  18019. if ($legbreakR < ($x0 + $w - $brTR_H)) {
  18020. $s .= (sprintf('%.3F %.3F l ', $legbreakR * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18021. }
  18022. if ($legbreakL > ($x0 + $brTL_H )) {
  18023. $s .= (sprintf('%.3F %.3F m ', $legbreakL * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18024. $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK) . "\n");
  18025. } else {
  18026. $s .= (sprintf('%.3F %.3F m ', ($x0 + $brTL_H ) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18027. }
  18028. } else {
  18029. $s .= (sprintf('%.3F %.3F l ', ($x0 + $brTL_H ) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18030. }
  18031. $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_top / 2, $brTL_V - $border_top / 2, 2, 1)) . "\n";
  18032. } else {
  18033. /* -- END BORDER-RADIUS -- */
  18034. if ($legend) {
  18035. if ($legbreakR < ($x0 + $w)) {
  18036. $s .= (sprintf('%.3F %.3F l ', $legbreakR * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18037. }
  18038. if ($legbreakL > ($x0)) {
  18039. $s .= (sprintf('%.3F %.3F m ', $legbreakL * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18040. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18041. $s .= (sprintf('%.3F %.3F l ', ($x0) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18042. } else {
  18043. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18044. }
  18045. } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18046. $s .= (sprintf('%.3F %.3F m ', ($x0) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18047. } else {
  18048. $s .= (sprintf('%.3F %.3F m ', ($x0 + $border_top / 2) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18049. }
  18050. } elseif ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18051. $s .= (sprintf('%.3F %.3F l ', ($x0) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18052. } else {
  18053. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_top / 2)) * _MPDFK, ($this->h - ($y0 + ($border_top / 2))) * _MPDFK)) . "\n";
  18054. }
  18055. /* -- BORDER-RADIUS -- */
  18056. }
  18057. /* -- END BORDER-RADIUS -- */
  18058. $s .= 'S' . "\n";
  18059. $this->_out($s);
  18060. if ($tbd['style'] == 'double') {
  18061. $this->SetLineWidth($tbd['w'] / 3);
  18062. $this->SetDColor($tbcol);
  18063. $this->_out($s);
  18064. }
  18065. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18066. $this->_out('Q');
  18067. }
  18068. // Reset Corners and Dash off
  18069. $this->SetLineWidth(0.1);
  18070. $this->SetDColor($this->ConvertColor(0));
  18071. $this->SetLineJoin(2);
  18072. $this->SetLineCap(2);
  18073. $this->SetDash();
  18074. }
  18075. }
  18076. // Reinstate line above for dotted line divider when block border crosses a page
  18077. //elseif ($divider == 'pagetop' || $continuingpage) {
  18078. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  18079. $tbd = $this->blk[$blvl]['border_bottom'];
  18080. if (isset($tbd['s']) && $tbd['s']) {
  18081. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18082. $this->_out('q');
  18083. $this->SetLineWidth(0);
  18084. $this->_out(sprintf('%.3F %.3F m ', ($x0) * _MPDFK, ($this->h - ($y0 + $h)) * _MPDFK));
  18085. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * _MPDFK, ($this->h - ($y0 + $h - $border_bottom)) * _MPDFK));
  18086. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * _MPDFK, ($this->h - ($y0 + $h - $border_bottom)) * _MPDFK));
  18087. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w) * _MPDFK, ($this->h - ($y0 + $h)) * _MPDFK));
  18088. $this->_out(' h W n '); // Ends path no-op & Sets the clipping path
  18089. }
  18090. $this->_setBorderLine($tbd);
  18091. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18092. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'B');
  18093. }
  18094. /* -- BORDER-RADIUS -- */ elseif (($brBL_V && $brBL_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18095. $this->SetLineJoin(0);
  18096. $this->SetLineCap(0);
  18097. }
  18098. $s = '';
  18099. if ($brBL_H && $brBL_V) {
  18100. $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_bottom / 2, $brBL_V - $border_bottom / 2, 3, 2, true)) . "\n";
  18101. } else
  18102. /* -- END BORDER-RADIUS -- */
  18103. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18104. $s .= (sprintf('%.3F %.3F m ', ($x0) * _MPDFK, ($this->h - ($y0 + $h - ($border_bottom / 2))) * _MPDFK)) . "\n";
  18105. } else {
  18106. $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_bottom / 2)) * _MPDFK, ($this->h - ($y0 + $h - ($border_bottom / 2))) * _MPDFK)) . "\n";
  18107. }
  18108. /* -- BORDER-RADIUS -- */
  18109. if ($brBR_H && $brBR_V) {
  18110. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2) - $brBR_H ) * _MPDFK, ($this->h - ($y0 + $h - ($border_bottom / 2))) * _MPDFK)) . "\n";
  18111. $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_bottom / 2, $brBR_V - $border_bottom / 2, 4, 1)) . "\n";
  18112. } else
  18113. /* -- END BORDER-RADIUS -- */
  18114. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18115. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w) * _MPDFK, ($this->h - ($y0 + $h - ($border_bottom / 2))) * _MPDFK)) . "\n";
  18116. } else {
  18117. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_bottom / 2)) * _MPDFK, ($this->h - ($y0 + $h - ($border_bottom / 2))) * _MPDFK)) . "\n";
  18118. }
  18119. $s .= 'S' . "\n";
  18120. $this->_out($s);
  18121. if ($tbd['style'] == 'double') {
  18122. $this->SetLineWidth($tbd['w'] / 3);
  18123. $this->SetDColor($tbcol);
  18124. $this->_out($s);
  18125. }
  18126. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18127. $this->_out('Q');
  18128. }
  18129. // Reset Corners and Dash off
  18130. $this->SetLineWidth(0.1);
  18131. $this->SetDColor($this->ConvertColor(0));
  18132. $this->SetLineJoin(2);
  18133. $this->SetLineCap(2);
  18134. $this->SetDash();
  18135. }
  18136. }
  18137. // Reinstate line below for dotted line divider when block border crosses a page
  18138. //elseif ($blockstate == 1 || $divider == 'pagebottom') {
  18139. if ($this->blk[$blvl]['border_left']) {
  18140. $tbd = $this->blk[$blvl]['border_left'];
  18141. if (isset($tbd['s']) && $tbd['s']) {
  18142. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18143. $this->_out('q');
  18144. $this->SetLineWidth(0);
  18145. $this->_out(sprintf('%.3F %.3F m ', ($x0) * _MPDFK, ($this->h - ($y0)) * _MPDFK));
  18146. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * _MPDFK, ($this->h - ($y0 + $border_top)) * _MPDFK));
  18147. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $border_left) * _MPDFK, ($this->h - ($y0 + $h - $border_bottom)) * _MPDFK));
  18148. $this->_out(sprintf('%.3F %.3F l ', ($x0) * _MPDFK, ($this->h - ($y0 + $h)) * _MPDFK));
  18149. $this->_out(' h W n '); // Ends path no-op & Sets the clipping path
  18150. }
  18151. $this->_setBorderLine($tbd);
  18152. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18153. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'L');
  18154. }
  18155. /* -- BORDER-RADIUS -- */ elseif (($brTL_V && $brTL_H) || ($brBL_V && $brBL_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18156. $this->SetLineJoin(0);
  18157. $this->SetLineCap(0);
  18158. }
  18159. $s = '';
  18160. if ($brTL_V && $brTL_H) {
  18161. $s .= ($this->_EllipseArc($x0 + $brTL_H, $y0 + $brTL_V, $brTL_H - $border_left / 2, $brTL_V - $border_left / 2, 2, 2, true)) . "\n";
  18162. } else
  18163. /* -- END BORDER-RADIUS -- */
  18164. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18165. $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * _MPDFK, ($this->h - ($y0)) * _MPDFK)) . "\n";
  18166. } else {
  18167. $s .= (sprintf('%.3F %.3F m ', ($x0 + ($border_left / 2)) * _MPDFK, ($this->h - ($y0 + ($border_left / 2))) * _MPDFK)) . "\n";
  18168. }
  18169. /* -- BORDER-RADIUS -- */
  18170. if ($brBL_V && $brBL_H) {
  18171. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * _MPDFK, ($this->h - ($y0 + $h - ($border_left / 2) - $brBL_V) ) * _MPDFK)) . "\n";
  18172. $s .= ($this->_EllipseArc($x0 + $brBL_H, $y0 + $h - $brBL_V, $brBL_H - $border_left / 2, $brBL_V - $border_left / 2, 3, 1)) . "\n";
  18173. } else
  18174. /* -- END BORDER-RADIUS -- */
  18175. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18176. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * _MPDFK, ($this->h - ($y0 + $h) ) * _MPDFK)) . "\n";
  18177. } else {
  18178. $s .= (sprintf('%.3F %.3F l ', ($x0 + ($border_left / 2)) * _MPDFK, ($this->h - ($y0 + $h - ($border_left / 2)) ) * _MPDFK)) . "\n";
  18179. }
  18180. $s .= 'S' . "\n";
  18181. $this->_out($s);
  18182. if ($tbd['style'] == 'double') {
  18183. $this->SetLineWidth($tbd['w'] / 3);
  18184. $this->SetDColor($tbcol);
  18185. $this->_out($s);
  18186. }
  18187. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18188. $this->_out('Q');
  18189. }
  18190. // Reset Corners and Dash off
  18191. $this->SetLineWidth(0.1);
  18192. $this->SetDColor($this->ConvertColor(0));
  18193. $this->SetLineJoin(2);
  18194. $this->SetLineCap(2);
  18195. $this->SetDash();
  18196. }
  18197. }
  18198. if ($this->blk[$blvl]['border_right']) {
  18199. $tbd = $this->blk[$blvl]['border_right'];
  18200. if (isset($tbd['s']) && $tbd['s']) {
  18201. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18202. $this->_out('q');
  18203. $this->SetLineWidth(0);
  18204. $this->_out(sprintf('%.3F %.3F m ', ($x0 + $w) * _MPDFK, ($this->h - ($y0)) * _MPDFK));
  18205. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * _MPDFK, ($this->h - ($y0 + $border_top)) * _MPDFK));
  18206. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w - $border_right) * _MPDFK, ($this->h - ($y0 + $h - $border_bottom)) * _MPDFK));
  18207. $this->_out(sprintf('%.3F %.3F l ', ($x0 + $w) * _MPDFK, ($this->h - ($y0 + $h)) * _MPDFK));
  18208. $this->_out(' h W n '); // Ends path no-op & Sets the clipping path
  18209. }
  18210. $this->_setBorderLine($tbd);
  18211. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18212. $this->_setDashBorder($tbd['style'], $divider, $continuingpage, 'R');
  18213. }
  18214. /* -- BORDER-RADIUS -- */ elseif (($brTR_V && $brTR_H) || ($brBR_V && $brBR_H) || $tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18215. $this->SetLineJoin(0);
  18216. $this->SetLineCap(0);
  18217. }
  18218. $s = '';
  18219. if ($brBR_V && $brBR_H) {
  18220. $s .= ($this->_EllipseArc($x0 + $w - $brBR_H, $y0 + $h - $brBR_V, $brBR_H - $border_right / 2, $brBR_V - $border_right / 2, 4, 2, true)) . "\n";
  18221. } else
  18222. /* -- END BORDER-RADIUS -- */
  18223. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18224. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * _MPDFK, ($this->h - ($y0 + $h)) * _MPDFK)) . "\n";
  18225. } else {
  18226. $s .= (sprintf('%.3F %.3F m ', ($x0 + $w - ($border_right / 2)) * _MPDFK, ($this->h - ($y0 + $h - ($border_right / 2))) * _MPDFK)) . "\n";
  18227. }
  18228. /* -- BORDER-RADIUS -- */
  18229. if ($brTR_V && $brTR_H) {
  18230. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * _MPDFK, ($this->h - ($y0 + ($border_right / 2) + $brTR_V) ) * _MPDFK)) . "\n";
  18231. $s .= ($this->_EllipseArc($x0 + $w - $brTR_H, $y0 + $brTR_V, $brTR_H - $border_right / 2, $brTR_V - $border_right / 2, 1, 1)) . "\n";
  18232. } else
  18233. /* -- END BORDER-RADIUS -- */
  18234. if ($tbd['style'] == 'solid' || $tbd['style'] == 'double') {
  18235. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * _MPDFK, ($this->h - ($y0) ) * _MPDFK)) . "\n";
  18236. } else {
  18237. $s .= (sprintf('%.3F %.3F l ', ($x0 + $w - ($border_right / 2)) * _MPDFK, ($this->h - ($y0 + ($border_right / 2)) ) * _MPDFK)) . "\n";
  18238. }
  18239. $s .= 'S' . "\n";
  18240. $this->_out($s);
  18241. if ($tbd['style'] == 'double') {
  18242. $this->SetLineWidth($tbd['w'] / 3);
  18243. $this->SetDColor($tbcol);
  18244. $this->_out($s);
  18245. }
  18246. if (!$brset && $tbd['style'] != 'dotted' && $tbd['style'] != 'dashed') {
  18247. $this->_out('Q');
  18248. }
  18249. // Reset Corners and Dash off
  18250. $this->SetLineWidth(0.1);
  18251. $this->SetDColor($this->ConvertColor(0));
  18252. $this->SetLineJoin(2);
  18253. $this->SetLineCap(2);
  18254. $this->SetDash();
  18255. }
  18256. }
  18257. $this->SetDash();
  18258. $this->y = $save_y;
  18259. // BACKGROUNDS are disabled in columns/kbt/headers - messes up the repositioning in printcolumnbuffer
  18260. if ($this->ColActive || $this->kwt || $this->keep_block_together) {
  18261. return;
  18262. }
  18263. $bgx0 = $x0;
  18264. $bgx1 = $x1;
  18265. $bgy0 = $y0;
  18266. $bgy1 = $y1;
  18267. // Defined br values represent the radius of the outer curve - need to take border-width/2 from each radius for drawing the borders
  18268. if (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'padding-box') {
  18269. $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w']);
  18270. $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w']);
  18271. $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w']);
  18272. $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w']);
  18273. $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w']);
  18274. $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w']);
  18275. $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w']);
  18276. $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w']);
  18277. $bgx0 += $this->blk[$blvl]['border_left']['w'];
  18278. $bgx1 -= $this->blk[$blvl]['border_right']['w'];
  18279. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  18280. $bgy0 += $this->blk[$blvl]['border_top']['w'];
  18281. }
  18282. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  18283. $bgy1 -= $this->blk[$blvl]['border_bottom']['w'];
  18284. }
  18285. } elseif (isset($this->blk[$blvl]['background_clip']) && $this->blk[$blvl]['background_clip'] == 'content-box') {
  18286. $brbgTL_H = max(0, $brTL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
  18287. $brbgTL_V = max(0, $brTL_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
  18288. $brbgTR_H = max(0, $brTR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
  18289. $brbgTR_V = max(0, $brTR_V - $this->blk[$blvl]['border_top']['w'] - $this->blk[$blvl]['padding_top']);
  18290. $brbgBL_H = max(0, $brBL_H - $this->blk[$blvl]['border_left']['w'] - $this->blk[$blvl]['padding_left']);
  18291. $brbgBL_V = max(0, $brBL_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
  18292. $brbgBR_H = max(0, $brBR_H - $this->blk[$blvl]['border_right']['w'] - $this->blk[$blvl]['padding_right']);
  18293. $brbgBR_V = max(0, $brBR_V - $this->blk[$blvl]['border_bottom']['w'] - $this->blk[$blvl]['padding_bottom']);
  18294. $bgx0 += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
  18295. $bgx1 -= $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right'];
  18296. if (($this->blk[$blvl]['border_top']['w'] || $this->blk[$blvl]['padding_top']) && $divider != 'pagetop' && !$continuingpage) {
  18297. $bgy0 += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
  18298. }
  18299. if (($this->blk[$blvl]['border_bottom']['w'] || $this->blk[$blvl]['padding_bottom']) && $blockstate != 1 && $divider != 'pagebottom') {
  18300. $bgy1 -= $this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom'];
  18301. }
  18302. } else {
  18303. $brbgTL_H = $brTL_H;
  18304. $brbgTL_V = $brTL_V;
  18305. $brbgTR_H = $brTR_H;
  18306. $brbgTR_V = $brTR_V;
  18307. $brbgBL_H = $brBL_H;
  18308. $brbgBL_V = $brBL_V;
  18309. $brbgBR_H = $brBR_H;
  18310. $brbgBR_V = $brBR_V;
  18311. }
  18312. // Set clipping path
  18313. $s = ' q 0 w '; // Line width=0
  18314. $s .= sprintf('%.3F %.3F m ', ($bgx0 + $brbgTL_H ) * _MPDFK, ($this->h - $bgy0) * _MPDFK); // start point TL before the arc
  18315. /* -- BORDER-RADIUS -- */
  18316. if ($brbgTL_H || $brbgTL_V) {
  18317. $s .= $this->_EllipseArc($bgx0 + $brbgTL_H, $bgy0 + $brbgTL_V, $brbgTL_H, $brbgTL_V, 2); // segment 2 TL
  18318. }
  18319. /* -- END BORDER-RADIUS -- */
  18320. $s .= sprintf('%.3F %.3F l ', ($bgx0) * _MPDFK, ($this->h - ($bgy1 - $brbgBL_V )) * _MPDFK); // line to BL
  18321. /* -- BORDER-RADIUS -- */
  18322. if ($brbgBL_H || $brbgBL_V) {
  18323. $s .= $this->_EllipseArc($bgx0 + $brbgBL_H, $bgy1 - $brbgBL_V, $brbgBL_H, $brbgBL_V, 3); // segment 3 BL
  18324. }
  18325. /* -- END BORDER-RADIUS -- */
  18326. $s .= sprintf('%.3F %.3F l ', ($bgx1 - $brbgBR_H ) * _MPDFK, ($this->h - ($bgy1)) * _MPDFK); // line to BR
  18327. /* -- BORDER-RADIUS -- */
  18328. if ($brbgBR_H || $brbgBR_V) {
  18329. $s .= $this->_EllipseArc($bgx1 - $brbgBR_H, $bgy1 - $brbgBR_V, $brbgBR_H, $brbgBR_V, 4); // segment 4 BR
  18330. }
  18331. /* -- END BORDER-RADIUS -- */
  18332. $s .= sprintf('%.3F %.3F l ', ($bgx1) * _MPDFK, ($this->h - ($bgy0 + $brbgTR_V)) * _MPDFK); // line to TR
  18333. /* -- BORDER-RADIUS -- */
  18334. if ($brbgTR_H || $brbgTR_V) {
  18335. $s .= $this->_EllipseArc($bgx1 - $brbgTR_H, $bgy0 + $brbgTR_V, $brbgTR_H, $brbgTR_V, 1); // segment 1 TR
  18336. }
  18337. /* -- END BORDER-RADIUS -- */
  18338. $s .= sprintf('%.3F %.3F l ', ($bgx0 + $brbgTL_H ) * _MPDFK, ($this->h - $bgy0) * _MPDFK); // line to TL
  18339. // Box Shadow
  18340. $shadow = '';
  18341. if (isset($this->blk[$blvl]['box_shadow']) && $this->blk[$blvl]['box_shadow'] && $h > 0) {
  18342. foreach ($this->blk[$blvl]['box_shadow'] AS $sh) {
  18343. // Colors
  18344. if ($sh['col']{0} == 1) {
  18345. $colspace = 'Gray';
  18346. if ($sh['col']{2} == 1) {
  18347. $col1 = '1' . $sh['col'][1] . '1' . $sh['col'][3];
  18348. } else {
  18349. $col1 = '1' . $sh['col'][1] . '1' . chr(100);
  18350. }
  18351. $col2 = '1' . $sh['col'][1] . '1' . chr(0);
  18352. } elseif ($sh['col']{0} == 4) { // CMYK
  18353. $colspace = 'CMYK';
  18354. $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(100);
  18355. $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
  18356. } elseif ($sh['col']{0} == 5) { // RGBa
  18357. $colspace = 'RGB';
  18358. $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4];
  18359. $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
  18360. } elseif ($sh['col']{0} == 6) { // CMYKa
  18361. $colspace = 'CMYK';
  18362. $col1 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . $sh['col'][5];
  18363. $col2 = '6' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . $sh['col'][4] . chr(0);
  18364. } else {
  18365. $colspace = 'RGB';
  18366. $col1 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(100);
  18367. $col2 = '5' . $sh['col'][1] . $sh['col'][2] . $sh['col'][3] . chr(0);
  18368. }
  18369. // Use clipping path as set above (and rectangle around page) to clip area outside box
  18370. $shadow .= $s; // Use the clipping path with W*
  18371. $shadow .= sprintf('0 %.3F m %.3F %.3F l ', $this->h * _MPDFK, $this->w * _MPDFK, $this->h * _MPDFK);
  18372. $shadow .= sprintf('%.3F 0 l 0 0 l 0 %.3F l ', $this->w * _MPDFK, $this->h * _MPDFK);
  18373. $shadow .= 'W n' . "\n";
  18374. $sh['blur'] = abs($sh['blur']); // cannot have negative blur value
  18375. // Ensure spread/blur do not make effective shadow width/height < 0
  18376. // Could do more complex things but this just adjusts spread value
  18377. if (-$sh['spread'] + $sh['blur'] / 2 > min($w / 2, $h / 2)) {
  18378. $sh['spread'] = $sh['blur'] / 2 - min($w / 2, $h / 2) + 0.01;
  18379. }
  18380. // Shadow Offset
  18381. if ($sh['x'] || $sh['y'])
  18382. $shadow .= sprintf(' q 1 0 0 1 %.4F %.4F cm', $sh['x'] * _MPDFK, -$sh['y'] * _MPDFK) . "\n";
  18383. // Set path for INNER shadow
  18384. $shadow .= ' q 0 w ';
  18385. $shadow .= $this->SetFColor($col1, true) . "\n";
  18386. if ($col1{0} == 5 && ord($col1{4}) < 100) { // RGBa
  18387. $shadow .= $this->SetAlpha(ord($col1{4}) / 100, 'Normal', true, 'F') . "\n";
  18388. } elseif ($col1{0} == 6 && ord($col1{5}) < 100) { // CMYKa
  18389. $shadow .= $this->SetAlpha(ord($col1{5}) / 100, 'Normal', true, 'F') . "\n";
  18390. } elseif ($col1{0} == 1 && $col1{2} == 1 && ord($col1{3}) < 100) { // Gray
  18391. $shadow .= $this->SetAlpha(ord($col1{3}) / 100, 'Normal', true, 'F') . "\n";
  18392. }
  18393. // Blur edges
  18394. $mag = 0.551784; // Bezier Control magic number for 4-part spline for circle/ellipse
  18395. $mag2 = 0.551784; // Bezier Control magic number to fill in edge of blurred rectangle
  18396. $d1 = $sh['spread'] + $sh['blur'] / 2;
  18397. $d2 = $sh['spread'] - $sh['blur'] / 2;
  18398. $bl = $sh['blur'];
  18399. $x00 = $x0 - $d1;
  18400. $y00 = $y0 - $d1;
  18401. $w00 = $w + $d1 * 2;
  18402. $h00 = $h + $d1 * 2;
  18403. // If any border-radius is greater width-negative spread(inner edge), ignore radii for shadow or screws up
  18404. $flatten = false;
  18405. if (max($brbgTR_H, $brbgTL_H, $brbgBR_H, $brbgBL_H) >= $w + $d2) {
  18406. $flatten = true;
  18407. }
  18408. if (max($brbgTR_V, $brbgTL_V, $brbgBR_V, $brbgBL_V) >= $h + $d2) {
  18409. $flatten = true;
  18410. }
  18411. // TOP RIGHT corner
  18412. $p1x = $x00 + $w00 - $d1 - $brbgTR_H;
  18413. $p1c2x = $p1x + ($d2 + $brbgTR_H) * $mag;
  18414. $p1y = $y00 + $bl;
  18415. $p2x = $x00 + $w00 - $d1 - $brbgTR_H;
  18416. $p2c2x = $p2x + ($d1 + $brbgTR_H) * $mag;
  18417. $p2y = $y00;
  18418. $p2c1y = $p2y + $bl / 2;
  18419. $p3x = $x00 + $w00;
  18420. $p3c2x = $p3x - $bl / 2;
  18421. $p3y = $y00 + $d1 + $brbgTR_V;
  18422. $p3c1y = $p3y - ($d1 + $brbgTR_V) * $mag;
  18423. $p4x = $x00 + $w00 - $bl;
  18424. $p4y = $y00 + $d1 + $brbgTR_V;
  18425. $p4c2y = $p4y - ($d2 + $brbgTR_V) * $mag;
  18426. if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
  18427. $p1x = $x00 + $w00 - $bl;
  18428. $p1c2x = $p1x;
  18429. $p2x = $x00 + $w00 - $bl;
  18430. $p2c2x = $p2x + $bl * $mag2;
  18431. $p3y = $y00 + $bl;
  18432. $p3c1y = $p3y - $bl * $mag2;
  18433. $p4y = $y00 + $bl;
  18434. $p4c2y = $p4y;
  18435. }
  18436. $shadow .= sprintf('%.3F %.3F m ', ($p1x ) * _MPDFK, ($this->h - ($p1y )) * _MPDFK);
  18437. $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * _MPDFK, ($this->h - ($p1y)) * _MPDFK, ($p4x) * _MPDFK, ($this->h - ($p4c2y)) * _MPDFK, ($p4x) * _MPDFK, ($this->h - ($p4y)) * _MPDFK);
  18438. $patch_array[0]['f'] = 0;
  18439. $patch_array[0]['points'] = array($p1x, $p1y, $p1x, $p1y,
  18440. $p2x, $p2c1y, $p2x, $p2y, $p2c2x, $p2y,
  18441. $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
  18442. $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
  18443. $p1c2x, $p1y);
  18444. $patch_array[0]['colors'] = array($col1, $col2, $col2, $col1);
  18445. // RIGHT
  18446. $p1x = $x00 + $w00; // control point only matches p3 preceding
  18447. $p1y = $y00 + $d1 + $brbgTR_V;
  18448. $p2x = $x00 + $w00 - $bl; // control point only matches p4 preceding
  18449. $p2y = $y00 + $d1 + $brbgTR_V;
  18450. $p3x = $x00 + $w00 - $bl;
  18451. $p3y = $y00 + $h00 - $d1 - $brbgBR_V;
  18452. $p4x = $x00 + $w00;
  18453. $p4c1x = $p4x - $bl / 2;
  18454. $p4y = $y00 + $h00 - $d1 - $brbgBR_V;
  18455. if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
  18456. $p1y = $y00 + $bl;
  18457. $p2y = $y00 + $bl;
  18458. }
  18459. if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
  18460. $p3y = $y00 + $h00 - $bl;
  18461. $p4y = $y00 + $h00 - $bl;
  18462. }
  18463. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * _MPDFK, ($this->h - ($p3y )) * _MPDFK);
  18464. $patch_array[1]['f'] = 2;
  18465. $patch_array[1]['points'] = array($p2x, $p2y,
  18466. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  18467. $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
  18468. $p1x, $p1y);
  18469. $patch_array[1]['colors'] = array($col1, $col2);
  18470. // BOTTOM RIGHT corner
  18471. $p1x = $x00 + $w00 - $bl; // control points only matches p3 preceding
  18472. $p1y = $y00 + $h00 - $d1 - $brbgBR_V;
  18473. $p1c2y = $p1y + ($d2 + $brbgBR_V) * $mag;
  18474. $p2x = $x00 + $w00; // control point only matches p4 preceding
  18475. $p2y = $y00 + $h00 - $d1 - $brbgBR_V;
  18476. $p2c2y = $p2y + ($d1 + $brbgBR_V) * $mag;
  18477. $p3x = $x00 + $w00 - $d1 - $brbgBR_H;
  18478. $p3c1x = $p3x + ($d1 + $brbgBR_H) * $mag;
  18479. $p3y = $y00 + $h00;
  18480. $p3c2y = $p3y - $bl / 2;
  18481. $p4x = $x00 + $w00 - $d1 - $brbgBR_H;
  18482. $p4c2x = $p4x + ($d2 + $brbgBR_H) * $mag;
  18483. $p4y = $y00 + $h00 - $bl;
  18484. if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
  18485. $p1y = $y00 + $h00 - $bl;
  18486. $p1c2y = $p1y;
  18487. $p2y = $y00 + $h00 - $bl;
  18488. $p2c2y = $p2y + $bl * $mag2;
  18489. $p3x = $x00 + $w00 - $bl;
  18490. $p3c1x = $p3x + $bl * $mag2;
  18491. $p4x = $x00 + $w00 - $bl;
  18492. $p4c2x = $p4x;
  18493. }
  18494. $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * _MPDFK, ($this->h - ($p1c2y)) * _MPDFK, ($p4c2x) * _MPDFK, ($this->h - ($p4y)) * _MPDFK, ($p4x) * _MPDFK, ($this->h - ($p4y)) * _MPDFK);
  18495. $patch_array[2]['f'] = 2;
  18496. $patch_array[2]['points'] = array($p2x, $p2c2y,
  18497. $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
  18498. $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
  18499. $p1x, $p1c2y);
  18500. $patch_array[2]['colors'] = array($col2, $col1);
  18501. // BOTTOM
  18502. $p1x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p3 preceding
  18503. $p1y = $y00 + $h00;
  18504. $p2x = $x00 + $w00 - $d1 - $brbgBR_H; // control point only matches p4 preceding
  18505. $p2y = $y00 + $h00 - $bl;
  18506. $p3x = $x00 + $d1 + $brbgBL_H;
  18507. $p3y = $y00 + $h00 - $bl;
  18508. $p4x = $x00 + $d1 + $brbgBL_H;
  18509. $p4y = $y00 + $h00;
  18510. $p4c1y = $p4y - $bl / 2;
  18511. if (-$d2 > min($brbgBR_H, $brbgBR_V) || $flatten) {
  18512. $p1x = $x00 + $w00 - $bl;
  18513. $p2x = $x00 + $w00 - $bl;
  18514. }
  18515. if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
  18516. $p3x = $x00 + $bl;
  18517. $p4x = $x00 + $bl;
  18518. }
  18519. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * _MPDFK, ($this->h - ($p3y )) * _MPDFK);
  18520. $patch_array[3]['f'] = 2;
  18521. $patch_array[3]['points'] = array($p2x, $p2y,
  18522. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  18523. $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
  18524. $p1x, $p1y);
  18525. $patch_array[3]['colors'] = array($col1, $col2);
  18526. // BOTTOM LEFT corner
  18527. $p1x = $x00 + $d1 + $brbgBL_H;
  18528. $p1c2x = $p1x - ($d2 + $brbgBL_H) * $mag; // control points only matches p3 preceding
  18529. $p1y = $y00 + $h00 - $bl;
  18530. $p2x = $x00 + $d1 + $brbgBL_H;
  18531. $p2c2x = $p2x - ($d1 + $brbgBL_H) * $mag; // control point only matches p4 preceding
  18532. $p2y = $y00 + $h00;
  18533. $p3x = $x00;
  18534. $p3c2x = $p3x + $bl / 2;
  18535. $p3y = $y00 + $h00 - $d1 - $brbgBL_V;
  18536. $p3c1y = $p3y + ($d1 + $brbgBL_V) * $mag;
  18537. $p4x = $x00 + $bl;
  18538. $p4y = $y00 + $h00 - $d1 - $brbgBL_V;
  18539. $p4c2y = $p4y + ($d2 + $brbgBL_V) * $mag;
  18540. if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
  18541. $p1x = $x00 + $bl;
  18542. $p1c2x = $p1x;
  18543. $p2x = $x00 + $bl;
  18544. $p2c2x = $p2x - $bl * $mag2;
  18545. $p3y = $y00 + $h00 - $bl;
  18546. $p3c1y = $p3y + $bl * $mag2;
  18547. $p4y = $y00 + $h00 - $bl;
  18548. $p4c2y = $p4y;
  18549. }
  18550. $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1c2x) * _MPDFK, ($this->h - ($p1y)) * _MPDFK, ($p4x) * _MPDFK, ($this->h - ($p4c2y)) * _MPDFK, ($p4x) * _MPDFK, ($this->h - ($p4y)) * _MPDFK);
  18551. $patch_array[4]['f'] = 2;
  18552. $patch_array[4]['points'] = array($p2c2x, $p2y,
  18553. $p3x, $p3c1y, $p3x, $p3y, $p3c2x, $p3y,
  18554. $p4x, $p4y, $p4x, $p4y, $p4x, $p4c2y,
  18555. $p1c2x, $p1y);
  18556. $patch_array[4]['colors'] = array($col2, $col1);
  18557. // LEFT - joins on the right (C3-C4 of previous): f = 2
  18558. $p1x = $x00; // control point only matches p3 preceding
  18559. $p1y = $y00 + $h00 - $d1 - $brbgBL_V;
  18560. $p2x = $x00 + $bl; // control point only matches p4 preceding
  18561. $p2y = $y00 + $h00 - $d1 - $brbgBL_V;
  18562. $p3x = $x00 + $bl;
  18563. $p3y = $y00 + $d1 + $brbgTL_V;
  18564. $p4x = $x00;
  18565. $p4c1x = $p4x + $bl / 2;
  18566. $p4y = $y00 + $d1 + $brbgTL_V;
  18567. if (-$d2 > min($brbgBL_H, $brbgBL_V) || $flatten) {
  18568. $p1y = $y00 + $h00 - $bl;
  18569. $p2y = $y00 + $h00 - $bl;
  18570. }
  18571. if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
  18572. $p3y = $y00 + $bl;
  18573. $p4y = $y00 + $bl;
  18574. }
  18575. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * _MPDFK, ($this->h - ($p3y )) * _MPDFK);
  18576. $patch_array[5]['f'] = 2;
  18577. $patch_array[5]['points'] = array($p2x, $p2y,
  18578. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  18579. $p4c1x, $p4y, $p4x, $p4y, $p4x, $p4y,
  18580. $p1x, $p1y);
  18581. $patch_array[5]['colors'] = array($col1, $col2);
  18582. // TOP LEFT corner
  18583. $p1x = $x00 + $bl; // control points only matches p3 preceding
  18584. $p1y = $y00 + $d1 + $brbgTL_V;
  18585. $p1c2y = $p1y - ($d2 + $brbgTL_V) * $mag;
  18586. $p2x = $x00; // control point only matches p4 preceding
  18587. $p2y = $y00 + $d1 + $brbgTL_V;
  18588. $p2c2y = $p2y - ($d1 + $brbgTL_V) * $mag;
  18589. $p3x = $x00 + $d1 + $brbgTL_H;
  18590. $p3c1x = $p3x - ($d1 + $brbgTL_H) * $mag;
  18591. $p3y = $y00;
  18592. $p3c2y = $p3y + $bl / 2;
  18593. $p4x = $x00 + $d1 + $brbgTL_H;
  18594. $p4c2x = $p4x - ($d2 + $brbgTL_H) * $mag;
  18595. $p4y = $y00 + $bl;
  18596. if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
  18597. $p1y = $y00 + $bl;
  18598. $p1c2y = $p1y;
  18599. $p2y = $y00 + $bl;
  18600. $p2c2y = $p2y - $bl * $mag2;
  18601. $p3x = $x00 + $bl;
  18602. $p3c1x = $p3x - $bl * $mag2;
  18603. $p4x = $x00 + $bl;
  18604. $p4c2x = $p4x;
  18605. }
  18606. $shadow .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($p1x) * _MPDFK, ($this->h - ($p1c2y)) * _MPDFK, ($p4c2x) * _MPDFK, ($this->h - ($p4y)) * _MPDFK, ($p4x) * _MPDFK, ($this->h - ($p4y)) * _MPDFK);
  18607. $patch_array[6]['f'] = 2;
  18608. $patch_array[6]['points'] = array($p2x, $p2c2y,
  18609. $p3c1x, $p3y, $p3x, $p3y, $p3x, $p3c2y,
  18610. $p4x, $p4y, $p4x, $p4y, $p4c2x, $p4y,
  18611. $p1x, $p1c2y);
  18612. $patch_array[6]['colors'] = array($col2, $col1);
  18613. // TOP - joins on the right (C3-C4 of previous): f = 2
  18614. $p1x = $x00 + $d1 + $brbgTL_H; // control point only matches p3 preceding
  18615. $p1y = $y00;
  18616. $p2x = $x00 + $d1 + $brbgTL_H; // control point only matches p4 preceding
  18617. $p2y = $y00 + $bl;
  18618. $p3x = $x00 + $w00 - $d1 - $brbgTR_H;
  18619. $p3y = $y00 + $bl;
  18620. $p4x = $x00 + $w00 - $d1 - $brbgTR_H;
  18621. $p4y = $y00;
  18622. $p4c1y = $p4y + $bl / 2;
  18623. if (-$d2 > min($brbgTL_H, $brbgTL_V) || $flatten) {
  18624. $p1x = $x00 + $bl;
  18625. $p2x = $x00 + $bl;
  18626. }
  18627. if (-$d2 > min($brbgTR_H, $brbgTR_V) || $flatten) {
  18628. $p3x = $x00 + $w00 - $bl;
  18629. $p4x = $x00 + $w00 - $bl;
  18630. }
  18631. $shadow .= sprintf('%.3F %.3F l ', ($p3x ) * _MPDFK, ($this->h - ($p3y )) * _MPDFK);
  18632. $patch_array[7]['f'] = 2;
  18633. $patch_array[7]['points'] = array($p2x, $p2y,
  18634. $p3x, $p3y, $p3x, $p3y, $p3x, $p3y,
  18635. $p4x, $p4c1y, $p4x, $p4y, $p4x, $p4y,
  18636. $p1x, $p1y);
  18637. $patch_array[7]['colors'] = array($col1, $col2);
  18638. $shadow .= ' h f Q ' . "\n"; // Close path and Fill the inner solid shadow
  18639. if ($bl)
  18640. $shadow .= $this->grad->CoonsPatchMesh($x00, $y00, $w00, $h00, $patch_array, $x00, $x00 + $w00, $y00, $y00 + $h00, $colspace, true);
  18641. if ($sh['x'] || $sh['y'])
  18642. $shadow .= ' Q' . "\n"; // Shadow Offset
  18643. $shadow .= ' Q' . "\n"; // Ends path no-op & Sets the clipping path
  18644. }
  18645. }
  18646. $s .= ' W n '; // Ends path no-op & Sets the clipping path
  18647. if ($this->blk[$blvl]['bgcolor']) {
  18648. $this->pageBackgrounds[$blvl][] = array('x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h, 'col' => $this->blk[$blvl]['bgcolorarray'], 'clippath' => $s, 'visibility' => $this->visibility, 'shadow' => $shadow, 'z-index' => $this->current_layer);
  18649. } elseif ($shadow) {
  18650. $this->pageBackgrounds[$blvl][] = array('shadowonly' => true, 'col' => '', 'clippath' => '', 'visibility' => $this->visibility, 'shadow' => $shadow, 'z-index' => $this->current_layer);
  18651. }
  18652. /* -- BACKGROUNDS -- */
  18653. if (isset($this->blk[$blvl]['gradient'])) {
  18654. $g = $this->grad->parseBackgroundGradient($this->blk[$blvl]['gradient']);
  18655. if ($g) {
  18656. $gx = $x0;
  18657. $gy = $y0;
  18658. $this->pageBackgrounds[$blvl][] = array('gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $w, 'h' => $h, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s, 'visibility' => $this->visibility, 'z-index' => $this->current_layer);
  18659. }
  18660. }
  18661. if (isset($this->blk[$blvl]['background-image'])) {
  18662. if (isset($this->blk[$blvl]['background-image']['gradient']) && $this->blk[$blvl]['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $this->blk[$blvl]['background-image']['gradient'])) {
  18663. $g = $this->grad->parseMozGradient($this->blk[$blvl]['background-image']['gradient']);
  18664. if ($g) {
  18665. $gx = $x0;
  18666. $gy = $y0;
  18667. // origin specifies the background-positioning-area (bpa)
  18668. if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
  18669. $gx += $this->blk[$blvl]['border_left']['w'];
  18670. $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
  18671. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  18672. $gy += $this->blk[$blvl]['border_top']['w'];
  18673. }
  18674. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  18675. $gy1 = $y1 - $this->blk[$blvl]['border_bottom']['w'];
  18676. } else {
  18677. $gy1 = $y1;
  18678. }
  18679. $h = $gy1 - $gy;
  18680. } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
  18681. $gx += $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
  18682. $w -= ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);
  18683. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  18684. $gy += $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
  18685. }
  18686. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  18687. $gy1 = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
  18688. } else {
  18689. $gy1 = $y1 - $this->blk[$blvl]['padding_bottom'];
  18690. }
  18691. $h = $gy1 - $gy;
  18692. }
  18693. if (isset($this->blk[$blvl]['background-image']['size']['w']) && $this->blk[$blvl]['background-image']['size']['w']) {
  18694. $size = $this->blk[$blvl]['background-image']['size'];
  18695. if ($size['w'] != 'contain' && $size['w'] != 'cover') {
  18696. if (stristr($size['w'], '%')) {
  18697. $size['w'] += 0;
  18698. $size['w'] /= 100;
  18699. $w *= $size['w'];
  18700. } elseif ($size['w'] != 'auto') {
  18701. $w = $size['w'];
  18702. }
  18703. if (stristr($size['h'], '%')) {
  18704. $size['h'] += 0;
  18705. $size['h'] /= 100;
  18706. $h *= $size['h'];
  18707. } elseif ($size['h'] != 'auto') {
  18708. $h = $size['h'];
  18709. }
  18710. }
  18711. }
  18712. $this->pageBackgrounds[$blvl][] = array('gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $w, 'h' => $h, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s, 'visibility' => $this->visibility, 'z-index' => $this->current_layer);
  18713. }
  18714. } else {
  18715. $image_id = $this->blk[$blvl]['background-image']['image_id'];
  18716. $orig_w = $this->blk[$blvl]['background-image']['orig_w'];
  18717. $orig_h = $this->blk[$blvl]['background-image']['orig_h'];
  18718. $x_pos = $this->blk[$blvl]['background-image']['x_pos'];
  18719. $y_pos = $this->blk[$blvl]['background-image']['y_pos'];
  18720. $x_repeat = $this->blk[$blvl]['background-image']['x_repeat'];
  18721. $y_repeat = $this->blk[$blvl]['background-image']['y_repeat'];
  18722. $resize = $this->blk[$blvl]['background-image']['resize'];
  18723. $opacity = $this->blk[$blvl]['background-image']['opacity'];
  18724. $itype = $this->blk[$blvl]['background-image']['itype'];
  18725. $size = $this->blk[$blvl]['background-image']['size'];
  18726. // origin specifies the background-positioning-area (bpa)
  18727. $bpa = array('x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h);
  18728. if ($this->blk[$blvl]['background-image']['origin'] == 'padding-box') {
  18729. $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'];
  18730. $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['border_right']['w']);
  18731. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  18732. $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'];
  18733. } else {
  18734. $bpa['y'] = $y0;
  18735. }
  18736. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  18737. $bpay = $y1 - $this->blk[$blvl]['border_bottom']['w'];
  18738. } else {
  18739. $bpay = $y1;
  18740. }
  18741. $bpa['h'] = $bpay - $bpa['y'];
  18742. } elseif ($this->blk[$blvl]['background-image']['origin'] == 'content-box') {
  18743. $bpa['x'] = $x0 + $this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'];
  18744. $bpa['w'] = $w - ($this->blk[$blvl]['border_left']['w'] + $this->blk[$blvl]['padding_left'] + $this->blk[$blvl]['border_right']['w'] + $this->blk[$blvl]['padding_right']);
  18745. if ($this->blk[$blvl]['border_top'] && $divider != 'pagetop' && !$continuingpage) {
  18746. $bpa['y'] = $y0 + $this->blk[$blvl]['border_top']['w'] + $this->blk[$blvl]['padding_top'];
  18747. } else {
  18748. $bpa['y'] = $y0 + $this->blk[$blvl]['padding_top'];
  18749. }
  18750. if ($this->blk[$blvl]['border_bottom'] && $blockstate != 1 && $divider != 'pagebottom') {
  18751. $bpay = $y1 - ($this->blk[$blvl]['border_bottom']['w'] + $this->blk[$blvl]['padding_bottom']);
  18752. } else {
  18753. $bpay = $y1 - $this->blk[$blvl]['padding_bottom'];
  18754. }
  18755. $bpa['h'] = $bpay - $bpa['y'];
  18756. }
  18757. $this->pageBackgrounds[$blvl][] = array('x' => $x0, 'y' => $y0, 'w' => $w, 'h' => $h, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype, 'visibility' => $this->visibility, 'z-index' => $this->current_layer, 'size' => $size, 'bpa' => $bpa);
  18758. }
  18759. }
  18760. /* -- END BACKGROUNDS -- */
  18761. // Float DIV
  18762. $this->blk[$blvl]['bb_painted'][$this->page] = true;
  18763. }
  18764. /* -- BORDER-RADIUS -- */
  18765. function _EllipseArc($x0, $y0, $rx, $ry, $seg = 1, $part = false, $start = false)
  18766. { // Anticlockwise segment 1-4 TR-TL-BL-BR (part=1 or 2)
  18767. $s = '';
  18768. if ($rx < 0) {
  18769. $rx = 0;
  18770. }
  18771. if ($ry < 0) {
  18772. $ry = 0;
  18773. }
  18774. $rx *= _MPDFK;
  18775. $ry *= _MPDFK;
  18776. $astart = 0;
  18777. if ($seg == 1) { // Top Right
  18778. $afinish = 90;
  18779. $nSeg = 4;
  18780. } elseif ($seg == 2) { // Top Left
  18781. $afinish = 180;
  18782. $nSeg = 8;
  18783. } elseif ($seg == 3) { // Bottom Left
  18784. $afinish = 270;
  18785. $nSeg = 12;
  18786. } else { // Bottom Right
  18787. $afinish = 360;
  18788. $nSeg = 16;
  18789. }
  18790. $astart = deg2rad((float) $astart);
  18791. $afinish = deg2rad((float) $afinish);
  18792. $totalAngle = $afinish - $astart;
  18793. $dt = $totalAngle / $nSeg; // segment angle
  18794. $dtm = $dt / 3;
  18795. $x0 *= _MPDFK;
  18796. $y0 = ($this->h - $y0) * _MPDFK;
  18797. $t1 = $astart;
  18798. $a0 = $x0 + ($rx * cos($t1));
  18799. $b0 = $y0 + ($ry * sin($t1));
  18800. $c0 = -$rx * sin($t1);
  18801. $d0 = $ry * cos($t1);
  18802. $op = false;
  18803. for ($i = 1; $i <= $nSeg; $i++) {
  18804. // Draw this bit of the total curve
  18805. $t1 = ($i * $dt) + $astart;
  18806. $a1 = $x0 + ($rx * cos($t1));
  18807. $b1 = $y0 + ($ry * sin($t1));
  18808. $c1 = -$rx * sin($t1);
  18809. $d1 = $ry * cos($t1);
  18810. if ($i > ($nSeg - 4) && (!$part || ($part == 1 && $i <= $nSeg - 2) || ($part == 2 && $i > $nSeg - 2))) {
  18811. if ($start && !$op) {
  18812. $s .= sprintf('%.3F %.3F m ', $a0, $b0);
  18813. }
  18814. $s .= sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c ', ($a0 + ($c0 * $dtm)), ($b0 + ($d0 * $dtm)), ($a1 - ($c1 * $dtm)), ($b1 - ($d1 * $dtm)), $a1, $b1);
  18815. $op = true;
  18816. }
  18817. $a0 = $a1;
  18818. $b0 = $b1;
  18819. $c0 = $c1;
  18820. $d0 = $d1;
  18821. }
  18822. return $s;
  18823. }
  18824. /* -- END BORDER-RADIUS -- */
  18825. function PaintDivLnBorder($state = 0, $blvl = 0, $h)
  18826. {
  18827. // $state = 0 normal; 1 top; 2 bottom; 3 top and bottom
  18828. $this->ColDetails[$this->CurrCol]['bottom_margin'] = $this->y + $h;
  18829. $save_y = $this->y;
  18830. $w = $this->blk[$blvl]['width'];
  18831. $x0 = $this->x; // left
  18832. $y0 = $this->y; // top
  18833. $x1 = $this->x + $w; // bottom
  18834. $y1 = $this->y + $h; // bottom
  18835. if ($this->blk[$blvl]['border_top'] && ($state == 1 || $state == 3)) {
  18836. $tbd = $this->blk[$blvl]['border_top'];
  18837. if (isset($tbd['s']) && $tbd['s']) {
  18838. $this->_setBorderLine($tbd);
  18839. $this->y = $y0 + ($tbd['w'] / 2);
  18840. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18841. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'T');
  18842. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
  18843. } else {
  18844. $this->SetLineJoin(0);
  18845. $this->SetLineCap(0);
  18846. $this->Line($x0, $this->y, $x0 + $w, $this->y);
  18847. }
  18848. $this->y += $tbd['w'];
  18849. // Reset Corners and Dash off
  18850. $this->SetLineJoin(2);
  18851. $this->SetLineCap(2);
  18852. $this->SetDash();
  18853. }
  18854. }
  18855. if ($this->blk[$blvl]['border_left']) {
  18856. $tbd = $this->blk[$blvl]['border_left'];
  18857. if (isset($tbd['s']) && $tbd['s']) {
  18858. $this->_setBorderLine($tbd);
  18859. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18860. $this->y = $y0 + ($tbd['w'] / 2);
  18861. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'L');
  18862. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
  18863. } else {
  18864. $this->y = $y0;
  18865. $this->SetLineJoin(0);
  18866. $this->SetLineCap(0);
  18867. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + ($tbd['w'] / 2), $y0 + $h);
  18868. }
  18869. $this->y += $tbd['w'];
  18870. // Reset Corners and Dash off
  18871. $this->SetLineJoin(2);
  18872. $this->SetLineCap(2);
  18873. $this->SetDash();
  18874. }
  18875. }
  18876. if ($this->blk[$blvl]['border_right']) {
  18877. $tbd = $this->blk[$blvl]['border_right'];
  18878. if (isset($tbd['s']) && $tbd['s']) {
  18879. $this->_setBorderLine($tbd);
  18880. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18881. $this->y = $y0 + ($tbd['w'] / 2);
  18882. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'R');
  18883. $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h - ($tbd['w'] / 2));
  18884. } else {
  18885. $this->y = $y0;
  18886. $this->SetLineJoin(0);
  18887. $this->SetLineCap(0);
  18888. $this->Line($x0 + $w - ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $y0 + $h);
  18889. }
  18890. $this->y += $tbd['w'];
  18891. // Reset Corners and Dash off
  18892. $this->SetLineJoin(2);
  18893. $this->SetLineCap(2);
  18894. $this->SetDash();
  18895. }
  18896. }
  18897. if ($this->blk[$blvl]['border_bottom'] && $state > 1) {
  18898. $tbd = $this->blk[$blvl]['border_bottom'];
  18899. if (isset($tbd['s']) && $tbd['s']) {
  18900. $this->_setBorderLine($tbd);
  18901. $this->y = $y0 + $h - ($tbd['w'] / 2);
  18902. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18903. $this->_setDashBorder($tbd['style'], '', $continuingpage, 'B');
  18904. $this->Line($x0 + ($tbd['w'] / 2), $this->y, $x0 + $w - ($tbd['w'] / 2), $this->y);
  18905. } else {
  18906. $this->SetLineJoin(0);
  18907. $this->SetLineCap(0);
  18908. $this->Line($x0, $this->y, $x0 + $w, $this->y);
  18909. }
  18910. $this->y += $tbd['w'];
  18911. // Reset Corners and Dash off
  18912. $this->SetLineJoin(2);
  18913. $this->SetLineCap(2);
  18914. $this->SetDash();
  18915. }
  18916. }
  18917. $this->SetDash();
  18918. $this->y = $save_y;
  18919. }
  18920. function PaintImgBorder($objattr, $is_table)
  18921. {
  18922. // Borders are disabled in columns - messes up the repositioning in printcolumnbuffer
  18923. if ($this->ColActive) {
  18924. return;
  18925. } // *COLUMNS*
  18926. if ($is_table) {
  18927. $k = $this->shrin_k;
  18928. } else {
  18929. $k = 1;
  18930. }
  18931. $h = (isset($objattr['BORDER-HEIGHT']) ? $objattr['BORDER-HEIGHT'] : 0);
  18932. $w = (isset($objattr['BORDER-WIDTH']) ? $objattr['BORDER-WIDTH'] : 0);
  18933. $x0 = (isset($objattr['BORDER-X']) ? $objattr['BORDER-X'] : 0);
  18934. $y0 = (isset($objattr['BORDER-Y']) ? $objattr['BORDER-Y'] : 0);
  18935. // BORDERS
  18936. if ($objattr['border_top']) {
  18937. $tbd = $objattr['border_top'];
  18938. if (!empty($tbd['s'])) {
  18939. $this->_setBorderLine($tbd, $k);
  18940. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18941. $this->_setDashBorder($tbd['style'], '', '', 'T');
  18942. }
  18943. $this->Line($x0, $y0, $x0 + $w, $y0);
  18944. // Reset Corners and Dash off
  18945. $this->SetLineJoin(2);
  18946. $this->SetLineCap(2);
  18947. $this->SetDash();
  18948. }
  18949. }
  18950. if ($objattr['border_left']) {
  18951. $tbd = $objattr['border_left'];
  18952. if (!empty($tbd['s'])) {
  18953. $this->_setBorderLine($tbd, $k);
  18954. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18955. $this->_setDashBorder($tbd['style'], '', '', 'L');
  18956. }
  18957. $this->Line($x0, $y0, $x0, $y0 + $h);
  18958. // Reset Corners and Dash off
  18959. $this->SetLineJoin(2);
  18960. $this->SetLineCap(2);
  18961. $this->SetDash();
  18962. }
  18963. }
  18964. if ($objattr['border_right']) {
  18965. $tbd = $objattr['border_right'];
  18966. if (!empty($tbd['s'])) {
  18967. $this->_setBorderLine($tbd, $k);
  18968. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18969. $this->_setDashBorder($tbd['style'], '', '', 'R');
  18970. }
  18971. $this->Line($x0 + $w, $y0, $x0 + $w, $y0 + $h);
  18972. // Reset Corners and Dash off
  18973. $this->SetLineJoin(2);
  18974. $this->SetLineCap(2);
  18975. $this->SetDash();
  18976. }
  18977. }
  18978. if ($objattr['border_bottom']) {
  18979. $tbd = $objattr['border_bottom'];
  18980. if (!empty($tbd['s'])) {
  18981. $this->_setBorderLine($tbd, $k);
  18982. if ($tbd['style'] == 'dotted' || $tbd['style'] == 'dashed') {
  18983. $this->_setDashBorder($tbd['style'], '', '', 'B');
  18984. }
  18985. $this->Line($x0, $y0 + $h, $x0 + $w, $y0 + $h);
  18986. // Reset Corners and Dash off
  18987. $this->SetLineJoin(2);
  18988. $this->SetLineCap(2);
  18989. $this->SetDash();
  18990. }
  18991. }
  18992. $this->SetDash();
  18993. $this->SetAlpha(1);
  18994. }
  18995. /* -- END HTML-CSS -- */
  18996. function Reset()
  18997. {
  18998. $this->SetTColor($this->ConvertColor(0));
  18999. $this->SetDColor($this->ConvertColor(0));
  19000. $this->SetFColor($this->ConvertColor(255));
  19001. $this->SetAlpha(1);
  19002. $this->colorarray = '';
  19003. $this->spanbgcolorarray = '';
  19004. $this->spanbgcolor = false;
  19005. $this->spanborder = false;
  19006. $this->spanborddet = array();
  19007. $this->ResetStyles();
  19008. $this->HREF = '';
  19009. $this->textparam = array();
  19010. $this->SetTextOutline();
  19011. $this->textvar = 0x00; // mPDF 5.7.1
  19012. $this->OTLtags = array();
  19013. $this->textshadow = '';
  19014. $this->currentLang = $this->default_lang; // mPDF 6
  19015. $this->RestrictUnicodeFonts($this->default_available_fonts); // mPDF 6
  19016. $this->SetFont($this->default_font, '', 0, false);
  19017. $this->SetFontSize($this->default_font_size, false);
  19018. $this->currentfontfamily = '';
  19019. $this->currentfontsize = '';
  19020. $this->currentfontstyle = '';
  19021. /* -- TABLES -- */
  19022. if ($this->tableLevel && isset($this->table[1][1]['cellLineHeight'])) {
  19023. $this->SetLineHeight('', $this->table[1][1]['cellLineHeight']); // *TABLES*
  19024. } else
  19025. /* -- END TABLES -- */
  19026. if (isset($this->blk[$this->blklvl]['line_height']) && $this->blk[$this->blklvl]['line_height']) {
  19027. $this->SetLineHeight('', $this->blk[$this->blklvl]['line_height']); // sets default line height
  19028. }
  19029. $this->lSpacingCSS = '';
  19030. $this->wSpacingCSS = '';
  19031. $this->fixedlSpacing = false;
  19032. $this->minwSpacing = 0;
  19033. $this->SetDash(); //restore to no dash
  19034. $this->dash_on = false;
  19035. $this->dotted_on = false;
  19036. $this->divwidth = 0;
  19037. $this->divheight = 0;
  19038. $this->cellTextAlign = '';
  19039. $this->cellLineHeight = '';
  19040. $this->cellLineStackingStrategy = '';
  19041. $this->cellLineStackingShift = '';
  19042. $this->oldy = -1;
  19043. $bodystyle = array();
  19044. if (isset($this->cssmgr->CSS['BODY']['FONT-STYLE'])) {
  19045. $bodystyle['FONT-STYLE'] = $this->cssmgr->CSS['BODY']['FONT-STYLE'];
  19046. }
  19047. if (isset($this->cssmgr->CSS['BODY']['FONT-WEIGHT'])) {
  19048. $bodystyle['FONT-WEIGHT'] = $this->cssmgr->CSS['BODY']['FONT-WEIGHT'];
  19049. }
  19050. if (isset($this->cssmgr->CSS['BODY']['COLOR'])) {
  19051. $bodystyle['COLOR'] = $this->cssmgr->CSS['BODY']['COLOR'];
  19052. }
  19053. if (isset($bodystyle)) {
  19054. $this->setCSS($bodystyle, 'BLOCK', 'BODY');
  19055. }
  19056. }
  19057. /* -- HTML-CSS -- */
  19058. function ReadMetaTags($html)
  19059. {
  19060. // changes anykey=anyvalue to anykey="anyvalue" (only do this when this happens inside tags)
  19061. $regexp = '/ (\\w+?)=([^\\s>"]+)/si';
  19062. $html = preg_replace($regexp, " \$1=\"\$2\"", $html);
  19063. if (preg_match('/<title>(.*?)<\/title>/si', $html, $m)) {
  19064. $this->SetTitle($m[1]);
  19065. }
  19066. preg_match_all('/<meta [^>]*?(name|content)="([^>]*?)" [^>]*?(name|content)="([^>]*?)".*?>/si', $html, $aux);
  19067. $firstattr = $aux[1];
  19068. $secondattr = $aux[3];
  19069. for ($i = 0; $i < count($aux[0]); $i++) {
  19070. $name = ( strtoupper($firstattr[$i]) == "NAME" ) ? strtoupper($aux[2][$i]) : strtoupper($aux[4][$i]);
  19071. $content = ( strtoupper($firstattr[$i]) == "CONTENT" ) ? $aux[2][$i] : $aux[4][$i];
  19072. switch ($name) {
  19073. case "KEYWORDS": $this->SetKeywords($content);
  19074. break;
  19075. case "AUTHOR": $this->SetAuthor($content);
  19076. break;
  19077. case "DESCRIPTION": $this->SetSubject($content);
  19078. break;
  19079. }
  19080. }
  19081. }
  19082. function ReadCharset($html)
  19083. {
  19084. // Charset conversion
  19085. if ($this->allow_charset_conversion) {
  19086. if (preg_match('/<head.*charset=([^\'\"\s]*).*<\/head>/si', $html, $m)) {
  19087. if (strtoupper($m[1]) != 'UTF-8') {
  19088. $this->charset_in = strtoupper($m[1]);
  19089. }
  19090. }
  19091. }
  19092. }
  19093. function setCSS($arrayaux, $type = '', $tag = '')
  19094. { // type= INLINE | BLOCK | TABLECELL // tag= BODY
  19095. if (!is_array($arrayaux))
  19096. return; //Removes PHP Warning
  19097. // mPDF 5.7.3 inline text-decoration parameters
  19098. $preceeding_fontkey = $this->FontFamily . $this->FontStyle;
  19099. $preceeding_fontsize = $this->FontSize;
  19100. $spanbordset = false;
  19101. $spanbgset = false;
  19102. // mPDF 6
  19103. $prevlevel = (($this->blklvl == 0) ? 0 : $this->blklvl - 1);
  19104. // Set font size first so that e.g. MARGIN 0.83em works on font size for this element
  19105. if (isset($arrayaux['FONT-SIZE'])) {
  19106. $v = $arrayaux['FONT-SIZE'];
  19107. if (is_numeric($v[0])) {
  19108. if ($type == 'BLOCK' && $this->blklvl > 0 && isset($this->blk[$this->blklvl - 1]['InlineProperties']) && isset($this->blk[$this->blklvl - 1]['InlineProperties']['size'])) {
  19109. $mmsize = $this->ConvertSize($v, $this->blk[$this->blklvl - 1]['InlineProperties']['size']);
  19110. } elseif ($type == 'TABLECELL') {
  19111. $mmsize = $this->ConvertSize($v, $this->default_font_size / _MPDFK);
  19112. } else {
  19113. $mmsize = $this->ConvertSize($v, $this->FontSize);
  19114. }
  19115. $this->SetFontSize($mmsize * (_MPDFK), false); //Get size in points (pt)
  19116. } else {
  19117. $v = strtoupper($v);
  19118. if (isset($this->fontsizes[$v])) {
  19119. $this->SetFontSize($this->fontsizes[$v] * $this->default_font_size, false);
  19120. }
  19121. }
  19122. if ($tag == 'BODY') {
  19123. $this->SetDefaultFontSize($this->FontSizePt);
  19124. }
  19125. }
  19126. // mPDF 6
  19127. if (isset($arrayaux['LANG']) && $arrayaux['LANG']) {
  19128. if ($this->autoLangToFont && !$this->usingCoreFont) {
  19129. if ($arrayaux['LANG'] != $this->default_lang && $arrayaux['LANG'] != 'UTF-8') {
  19130. list ($coreSuitable, $mpdf_pdf_unifont) = GetLangOpts($arrayaux['LANG'], $this->useAdobeCJK, $this->fontdata);
  19131. if ($mpdf_pdf_unifont) {
  19132. $arrayaux['FONT-FAMILY'] = $mpdf_pdf_unifont;
  19133. }
  19134. if ($tag == 'BODY') {
  19135. $this->default_lang = $arrayaux['LANG'];
  19136. }
  19137. }
  19138. }
  19139. $this->currentLang = $arrayaux['LANG'];
  19140. }
  19141. // FOR INLINE and BLOCK OR 'BODY'
  19142. if (isset($arrayaux['FONT-FAMILY'])) {
  19143. $v = $arrayaux['FONT-FAMILY'];
  19144. //If it is a font list, get all font types
  19145. $aux_fontlist = explode(",", $v);
  19146. $found = 0;
  19147. foreach ($aux_fontlist AS $f) {
  19148. $fonttype = trim($f);
  19149. $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
  19150. $fonttype = preg_replace('/ /', '', $fonttype);
  19151. $v = strtolower(trim($fonttype));
  19152. if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
  19153. $v = $this->fonttrans[$v];
  19154. }
  19155. if ((!$this->onlyCoreFonts && in_array($v, $this->available_unifonts)) ||
  19156. in_array($v, array('ccourier', 'ctimes', 'chelvetica')) ||
  19157. ($this->onlyCoreFonts && in_array($v, array('courier', 'times', 'helvetica', 'arial'))) ||
  19158. in_array($v, array('sjis', 'uhc', 'big5', 'gb'))) {
  19159. $fonttype = $v;
  19160. $found = 1;
  19161. break;
  19162. }
  19163. }
  19164. if (!$found) {
  19165. foreach ($aux_fontlist AS $f) {
  19166. $fonttype = trim($f);
  19167. $fonttype = preg_replace('/["\']*(.*?)["\']*/', '\\1', $fonttype);
  19168. $fonttype = preg_replace('/ /', '', $fonttype);
  19169. $v = strtolower(trim($fonttype));
  19170. if (isset($this->fonttrans[$v]) && $this->fonttrans[$v]) {
  19171. $v = $this->fonttrans[$v];
  19172. }
  19173. if (in_array($v, $this->sans_fonts) || in_array($v, $this->serif_fonts) || in_array($v, $this->mono_fonts)) {
  19174. $fonttype = $v;
  19175. break;
  19176. }
  19177. }
  19178. }
  19179. if ($tag == 'BODY') {
  19180. $this->SetDefaultFont($fonttype);
  19181. }
  19182. $this->SetFont($fonttype, $this->currentfontstyle, 0, false);
  19183. } else {
  19184. $this->SetFont($this->currentfontfamily, $this->currentfontstyle, 0, false);
  19185. }
  19186. foreach ($arrayaux as $k => $v) {
  19187. if ($type != 'INLINE' && $tag != 'BODY' && $type != 'TABLECELL') {
  19188. switch ($k) {
  19189. // BORDERS
  19190. case 'BORDER-TOP':
  19191. $this->blk[$this->blklvl]['border_top'] = $this->border_details($v);
  19192. if ($this->blk[$this->blklvl]['border_top']['s']) {
  19193. $this->blk[$this->blklvl]['border'] = 1;
  19194. }
  19195. break;
  19196. case 'BORDER-BOTTOM':
  19197. $this->blk[$this->blklvl]['border_bottom'] = $this->border_details($v);
  19198. if ($this->blk[$this->blklvl]['border_bottom']['s']) {
  19199. $this->blk[$this->blklvl]['border'] = 1;
  19200. }
  19201. break;
  19202. case 'BORDER-LEFT':
  19203. $this->blk[$this->blklvl]['border_left'] = $this->border_details($v);
  19204. if ($this->blk[$this->blklvl]['border_left']['s']) {
  19205. $this->blk[$this->blklvl]['border'] = 1;
  19206. }
  19207. break;
  19208. case 'BORDER-RIGHT':
  19209. $this->blk[$this->blklvl]['border_right'] = $this->border_details($v);
  19210. if ($this->blk[$this->blklvl]['border_right']['s']) {
  19211. $this->blk[$this->blklvl]['border'] = 1;
  19212. }
  19213. break;
  19214. // PADDING
  19215. case 'PADDING-TOP':
  19216. $this->blk[$this->blklvl]['padding_top'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19217. break;
  19218. case 'PADDING-BOTTOM':
  19219. $this->blk[$this->blklvl]['padding_bottom'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19220. break;
  19221. case 'PADDING-LEFT':
  19222. if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
  19223. $this->blk[$this->blklvl]['padding_left'] = 'auto';
  19224. break;
  19225. }
  19226. $this->blk[$this->blklvl]['padding_left'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19227. break;
  19228. case 'PADDING-RIGHT':
  19229. if (($tag == 'UL' || $tag == 'OL') && $v == 'auto') {
  19230. $this->blk[$this->blklvl]['padding_right'] = 'auto';
  19231. break;
  19232. }
  19233. $this->blk[$this->blklvl]['padding_right'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19234. break;
  19235. // MARGINS
  19236. case 'MARGIN-TOP':
  19237. $tmp = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19238. if (isset($this->blk[$this->blklvl]['lastbottommargin'])) {
  19239. if ($tmp > $this->blk[$this->blklvl]['lastbottommargin']) {
  19240. $tmp -= $this->blk[$this->blklvl]['lastbottommargin'];
  19241. } else {
  19242. $tmp = 0;
  19243. }
  19244. }
  19245. $this->blk[$this->blklvl]['margin_top'] = $tmp;
  19246. break;
  19247. case 'MARGIN-BOTTOM':
  19248. $this->blk[$this->blklvl]['margin_bottom'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19249. break;
  19250. case 'MARGIN-LEFT':
  19251. $this->blk[$this->blklvl]['margin_left'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19252. break;
  19253. case 'MARGIN-RIGHT':
  19254. $this->blk[$this->blklvl]['margin_right'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19255. break;
  19256. /* -- BORDER-RADIUS -- */
  19257. case 'BORDER-TOP-LEFT-RADIUS-H':
  19258. $this->blk[$this->blklvl]['border_radius_TL_H'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19259. break;
  19260. case 'BORDER-TOP-LEFT-RADIUS-V':
  19261. $this->blk[$this->blklvl]['border_radius_TL_V'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19262. break;
  19263. case 'BORDER-TOP-RIGHT-RADIUS-H':
  19264. $this->blk[$this->blklvl]['border_radius_TR_H'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19265. break;
  19266. case 'BORDER-TOP-RIGHT-RADIUS-V':
  19267. $this->blk[$this->blklvl]['border_radius_TR_V'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19268. break;
  19269. case 'BORDER-BOTTOM-LEFT-RADIUS-H':
  19270. $this->blk[$this->blklvl]['border_radius_BL_H'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19271. break;
  19272. case 'BORDER-BOTTOM-LEFT-RADIUS-V':
  19273. $this->blk[$this->blklvl]['border_radius_BL_V'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19274. break;
  19275. case 'BORDER-BOTTOM-RIGHT-RADIUS-H':
  19276. $this->blk[$this->blklvl]['border_radius_BR_H'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19277. break;
  19278. case 'BORDER-BOTTOM-RIGHT-RADIUS-V':
  19279. $this->blk[$this->blklvl]['border_radius_BR_V'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19280. break;
  19281. /* -- END BORDER-RADIUS -- */
  19282. case 'BOX-SHADOW':
  19283. $bs = $this->cssmgr->setCSSboxshadow($v);
  19284. if ($bs) {
  19285. $this->blk[$this->blklvl]['box_shadow'] = $bs;
  19286. }
  19287. break;
  19288. case 'BACKGROUND-CLIP':
  19289. if (strtoupper($v) == 'PADDING-BOX') {
  19290. $this->blk[$this->blklvl]['background_clip'] = 'padding-box';
  19291. } elseif (strtoupper($v) == 'CONTENT-BOX') {
  19292. $this->blk[$this->blklvl]['background_clip'] = 'content-box';
  19293. }
  19294. break;
  19295. case 'PAGE-BREAK-AFTER':
  19296. if (strtoupper($v) == 'AVOID') {
  19297. $this->blk[$this->blklvl]['page_break_after_avoid'] = true;
  19298. } elseif (strtoupper($v) == 'ALWAYS' || strtoupper($v) == 'LEFT' || strtoupper($v) == 'RIGHT') {
  19299. $this->blk[$this->blklvl]['page_break_after'] = strtoupper($v);
  19300. }
  19301. break;
  19302. // mPDF 6 pagebreaktype
  19303. case 'BOX-DECORATION-BREAK':
  19304. if (strtoupper($v) == 'CLONE') {
  19305. $this->blk[$this->blklvl]['box_decoration_break'] = 'clone';
  19306. } elseif (strtoupper($v) == 'SLICE') {
  19307. $this->blk[$this->blklvl]['box_decoration_break'] = 'slice';
  19308. }
  19309. break;
  19310. case 'WIDTH':
  19311. if (strtoupper($v) != 'AUTO') {
  19312. $this->blk[$this->blklvl]['css_set_width'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false);
  19313. }
  19314. break;
  19315. // mPDF 6 Lists
  19316. // LISTS
  19317. case 'LIST-STYLE-TYPE':
  19318. $this->blk[$this->blklvl]['list_style_type'] = strtolower($v);
  19319. break;
  19320. case 'LIST-STYLE-IMAGE':
  19321. $this->blk[$this->blklvl]['list_style_image'] = strtolower($v);
  19322. break;
  19323. case 'LIST-STYLE-POSITION':
  19324. $this->blk[$this->blklvl]['list_style_position'] = strtolower($v);
  19325. break;
  19326. }//end of switch($k)
  19327. }
  19328. if ($type != 'INLINE' && $type != 'TABLECELL') { // All block-level, including BODY tag
  19329. switch ($k) {
  19330. case 'TEXT-INDENT':
  19331. // Computed value - to inherit
  19332. $this->blk[$this->blklvl]['text_indent'] = $this->ConvertSize($v, $this->blk[$prevlevel]['inner_width'], $this->FontSize, false) . 'mm';
  19333. break;
  19334. case 'MARGIN-COLLAPSE': // Custom tag to collapse margins at top and bottom of page
  19335. if (strtoupper($v) == 'COLLAPSE') {
  19336. $this->blk[$this->blklvl]['margin_collapse'] = true;
  19337. }
  19338. break;
  19339. case 'LINE-HEIGHT':
  19340. $this->blk[$this->blklvl]['line_height'] = $this->fixLineheight($v);
  19341. if (!$this->blk[$this->blklvl]['line_height']) {
  19342. $this->blk[$this->blklvl]['line_height'] = 'N';
  19343. } // mPDF 6
  19344. break;
  19345. // mPDF 6
  19346. case 'LINE-STACKING-STRATEGY':
  19347. $this->blk[$this->blklvl]['line_stacking_strategy'] = strtolower($v);
  19348. break;
  19349. case 'LINE-STACKING-SHIFT':
  19350. $this->blk[$this->blklvl]['line_stacking_shift'] = strtolower($v);
  19351. break;
  19352. case 'TEXT-ALIGN': //left right center justify
  19353. switch (strtoupper($v)) {
  19354. case 'LEFT':
  19355. $this->blk[$this->blklvl]['align'] = "L";
  19356. break;
  19357. case 'CENTER':
  19358. $this->blk[$this->blklvl]['align'] = "C";
  19359. break;
  19360. case 'RIGHT':
  19361. $this->blk[$this->blklvl]['align'] = "R";
  19362. break;
  19363. case 'JUSTIFY':
  19364. $this->blk[$this->blklvl]['align'] = "J";
  19365. break;
  19366. }
  19367. break;
  19368. /* -- BACKGROUNDS -- */
  19369. case 'BACKGROUND-GRADIENT':
  19370. if ($type == 'BLOCK') {
  19371. $this->blk[$this->blklvl]['gradient'] = $v;
  19372. }
  19373. break;
  19374. /* -- END BACKGROUNDS -- */
  19375. case 'DIRECTION':
  19376. if ($v) {
  19377. $this->blk[$this->blklvl]['direction'] = strtolower($v);
  19378. }
  19379. break;
  19380. }//end of switch($k)
  19381. }
  19382. // FOR INLINE ONLY
  19383. if ($type == 'INLINE') {
  19384. switch ($k) {
  19385. case 'DISPLAY':
  19386. if (strtoupper($v) == 'NONE') {
  19387. $this->inlineDisplayOff = true;
  19388. }
  19389. break;
  19390. case 'DIRECTION':
  19391. break;
  19392. }//end of switch($k)
  19393. }
  19394. // FOR INLINE ONLY
  19395. if ($type == 'INLINE') {
  19396. switch ($k) {
  19397. // BORDERS
  19398. case 'BORDER-TOP':
  19399. $this->spanborddet['T'] = $this->border_details($v);
  19400. $this->spanborder = true;
  19401. $spanbordset = true;
  19402. break;
  19403. case 'BORDER-BOTTOM':
  19404. $this->spanborddet['B'] = $this->border_details($v);
  19405. $this->spanborder = true;
  19406. $spanbordset = true;
  19407. break;
  19408. case 'BORDER-LEFT':
  19409. $this->spanborddet['L'] = $this->border_details($v);
  19410. $this->spanborder = true;
  19411. $spanbordset = true;
  19412. break;
  19413. case 'BORDER-RIGHT':
  19414. $this->spanborddet['R'] = $this->border_details($v);
  19415. $this->spanborder = true;
  19416. $spanbordset = true;
  19417. break;
  19418. case 'VISIBILITY': // block is set in OpenTag
  19419. $v = strtolower($v);
  19420. if ($v == 'visible' || $v == 'hidden' || $v == 'printonly' || $v == 'screenonly') {
  19421. $this->textparam['visibility'] = $v;
  19422. }
  19423. break;
  19424. }//end of switch($k)
  19425. }
  19426. if ($type != 'TABLECELL') {
  19427. // FOR INLINE and BLOCK
  19428. switch ($k) {
  19429. case 'TEXT-ALIGN': //left right center justify
  19430. if (strtoupper($v) == 'NOJUSTIFY' && $this->blk[$this->blklvl]['align'] == "J") {
  19431. $this->blk[$this->blklvl]['align'] = "";
  19432. }
  19433. break;
  19434. // bgcolor only - to stay consistent with original html2fpdf
  19435. case 'BACKGROUND':
  19436. case 'BACKGROUND-COLOR':
  19437. $cor = $this->ConvertColor($v);
  19438. if ($cor) {
  19439. if ($tag == 'BODY') {
  19440. $this->bodyBackgroundColor = $cor;
  19441. } elseif ($type == 'INLINE') {
  19442. $this->spanbgcolorarray = $cor;
  19443. $this->spanbgcolor = true;
  19444. $spanbgset = true;
  19445. } else {
  19446. $this->blk[$this->blklvl]['bgcolorarray'] = $cor;
  19447. $this->blk[$this->blklvl]['bgcolor'] = true;
  19448. }
  19449. } elseif ($type != 'INLINE') {
  19450. if ($this->ColActive) {
  19451. $this->blk[$this->blklvl]['bgcolorarray'] = $this->blk[$prevlevel]['bgcolorarray'];
  19452. $this->blk[$this->blklvl]['bgcolor'] = $this->blk[$prevlevel]['bgcolor'];
  19453. }
  19454. }
  19455. break;
  19456. case 'VERTICAL-ALIGN': //super and sub only dealt with here e.g. <SUB> and <SUP>
  19457. switch (strtoupper($v)) {
  19458. case 'SUPER':
  19459. $this->textvar = ($this->textvar | FA_SUPERSCRIPT); // mPDF 5.7.1
  19460. $this->textvar = ($this->textvar & ~FA_SUBSCRIPT);
  19461. // mPDF 5.7.3 inline text-decoration parameters
  19462. if (isset($this->textparam['text-baseline'])) {
  19463. $this->textparam['text-baseline'] += ($this->baselineSup) * $preceeding_fontsize;
  19464. } else {
  19465. $this->textparam['text-baseline'] = ($this->baselineSup) * $preceeding_fontsize;
  19466. }
  19467. break;
  19468. case 'SUB':
  19469. $this->textvar = ($this->textvar | FA_SUBSCRIPT);
  19470. $this->textvar = ($this->textvar & ~FA_SUPERSCRIPT);
  19471. // mPDF 5.7.3 inline text-decoration parameters
  19472. if (isset($this->textparam['text-baseline'])) {
  19473. $this->textparam['text-baseline'] += ($this->baselineSub) * $preceeding_fontsize;
  19474. } else {
  19475. $this->textparam['text-baseline'] = ($this->baselineSub) * $preceeding_fontsize;
  19476. }
  19477. break;
  19478. case 'BASELINE':
  19479. $this->textvar = ($this->textvar & ~FA_SUBSCRIPT);
  19480. $this->textvar = ($this->textvar & ~FA_SUPERSCRIPT);
  19481. // mPDF 5.7.3 inline text-decoration parameters
  19482. if (isset($this->textparam['text-baseline'])) {
  19483. unset($this->textparam['text-baseline']);
  19484. }
  19485. break;
  19486. // mPDF 5.7.3 inline text-decoration parameters
  19487. default:
  19488. $lh = $this->_computeLineheight($this->blk[$this->blklvl]['line_height']);
  19489. $sz = $this->ConvertSize($v, $lh, $this->FontSize, false);
  19490. $this->textvar = ($this->textvar & ~FA_SUBSCRIPT);
  19491. $this->textvar = ($this->textvar & ~FA_SUPERSCRIPT);
  19492. if ($sz) {
  19493. if ($sz > 0) {
  19494. $this->textvar = ($this->textvar | FA_SUPERSCRIPT);
  19495. } else {
  19496. $this->textvar = ($this->textvar | FA_SUBSCRIPT);
  19497. }
  19498. if (isset($this->textparam['text-baseline'])) {
  19499. $this->textparam['text-baseline'] += $sz;
  19500. } else {
  19501. $this->textparam['text-baseline'] = $sz;
  19502. }
  19503. }
  19504. }
  19505. break;
  19506. }//end of switch($k)
  19507. }
  19508. // FOR ALL
  19509. switch ($k) {
  19510. case 'LETTER-SPACING':
  19511. $this->lSpacingCSS = $v;
  19512. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  19513. $this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS, $this->FontSize);
  19514. }
  19515. break;
  19516. case 'WORD-SPACING':
  19517. $this->wSpacingCSS = $v;
  19518. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  19519. $this->minwSpacing = $this->ConvertSize($this->wSpacingCSS, $this->FontSize);
  19520. }
  19521. break;
  19522. case 'FONT-STYLE': // italic normal oblique
  19523. switch (strtoupper($v)) {
  19524. case 'ITALIC':
  19525. case 'OBLIQUE':
  19526. $this->SetStyle('I', true);
  19527. break;
  19528. case 'NORMAL':
  19529. $this->SetStyle('I', false);
  19530. break;
  19531. }
  19532. break;
  19533. case 'FONT-WEIGHT': // normal bold //Does not support: bolder, lighter, 100..900(step value=100)
  19534. switch (strtoupper($v)) {
  19535. case 'BOLD':
  19536. $this->SetStyle('B', true);
  19537. break;
  19538. case 'NORMAL':
  19539. $this->SetStyle('B', false);
  19540. break;
  19541. }
  19542. break;
  19543. case 'FONT-KERNING':
  19544. if (strtoupper($v) == 'NORMAL' || (strtoupper($v) == 'AUTO' && $this->useKerning)) {
  19545. /* -- OTL -- */
  19546. if ($this->CurrentFont['haskernGPOS']) {
  19547. if (isset($this->OTLtags['Plus'])) {
  19548. $this->OTLtags['Plus'] .= ' kern';
  19549. } else {
  19550. $this->OTLtags['Plus'] = ' kern';
  19551. }
  19552. }
  19553. /* -- END OTL -- */ else { // *OTL*
  19554. $this->textvar = ($this->textvar | FC_KERNING);
  19555. } // *OTL*
  19556. } elseif (strtoupper($v) == 'NONE' || (strtoupper($v) == 'AUTO' && !$this->useKerning)) {
  19557. if (isset($this->OTLtags['Plus']))
  19558. $this->OTLtags['Plus'] = str_replace('kern', '', $this->OTLtags['Plus']); // *OTL*
  19559. if (isset($this->OTLtags['FFPlus']))
  19560. $this->OTLtags['FFPlus'] = preg_replace('/kern[\d]*/', '', $this->OTLtags['FFPlus']);
  19561. $this->textvar = ($this->textvar & ~FC_KERNING);
  19562. }
  19563. break;
  19564. /* -- OTL -- */
  19565. case 'FONT-LANGUAGE-OVERRIDE':
  19566. $v = strtoupper($v);
  19567. if (strpos($v, 'NORMAL') !== false) {
  19568. $this->fontLanguageOverride = '';
  19569. } else {
  19570. $this->fontLanguageOverride = trim($v);
  19571. }
  19572. break;
  19573. case 'FONT-VARIANT-POSITION':
  19574. if (isset($this->OTLtags['Plus']))
  19575. $this->OTLtags['Plus'] = str_replace(array('sups', 'subs'), '', $this->OTLtags['Plus']);
  19576. switch (strtoupper($v)) {
  19577. case 'SUPER':
  19578. $this->OTLtags['Plus'] .= ' sups';
  19579. break;
  19580. case 'SUB':
  19581. $this->OTLtags['Plus'] .= ' subs';
  19582. break;
  19583. case 'NORMAL':
  19584. break;
  19585. }
  19586. break;
  19587. case 'FONT-VARIANT-CAPS':
  19588. $v = strtoupper($v);
  19589. if (!isset($this->OTLtags['Plus'])) {
  19590. $this->OTLtags['Plus'] = '';
  19591. }
  19592. $this->OTLtags['Plus'] = str_replace(array('c2sc', 'smcp', 'c2pc', 'pcap', 'unic', 'titl'), '', $this->OTLtags['Plus']);
  19593. $this->textvar = ($this->textvar & ~FC_SMALLCAPS); // ?????????????? <small-caps>
  19594. if (strpos($v, 'ALL-SMALL-CAPS') !== false) {
  19595. $this->OTLtags['Plus'] .= ' c2sc smcp';
  19596. } elseif (strpos($v, 'SMALL-CAPS') !== false) {
  19597. if (isset($this->CurrentFont['hassmallcapsGSUB']) && $this->CurrentFont['hassmallcapsGSUB']) {
  19598. $this->OTLtags['Plus'] .= ' smcp';
  19599. } else {
  19600. $this->textvar = ($this->textvar | FC_SMALLCAPS);
  19601. }
  19602. } elseif (strpos($v, 'ALL-PETITE-CAPS') !== false) {
  19603. $this->OTLtags['Plus'] .= ' c2pc pcap';
  19604. } elseif (strpos($v, 'PETITE-CAPS') !== false) {
  19605. $this->OTLtags['Plus'] .= ' pcap';
  19606. } elseif (strpos($v, 'UNICASE') !== false) {
  19607. $this->OTLtags['Plus'] .= ' unic';
  19608. } elseif (strpos($v, 'TITLING-CAPS') !== false) {
  19609. $this->OTLtags['Plus'] .= ' titl';
  19610. }
  19611. break;
  19612. case 'FONT-VARIANT-LIGATURES':
  19613. $v = strtoupper($v);
  19614. if (!isset($this->OTLtags['Plus'])) {
  19615. $this->OTLtags['Plus'] = '';
  19616. }
  19617. if (!isset($this->OTLtags['Minus'])) {
  19618. $this->OTLtags['Minus'] = '';
  19619. }
  19620. if (strpos($v, 'NORMAL') !== false) {
  19621. $this->OTLtags['Minus'] = str_replace(array('liga', 'clig', 'calt'), '', $this->OTLtags['Minus']);
  19622. $this->OTLtags['Plus'] = str_replace(array('dlig', 'hlig'), '', $this->OTLtags['Plus']);
  19623. } elseif (strpos($v, 'NONE') !== false) {
  19624. $this->OTLtags['Minus'] .= ' liga clig calt';
  19625. $this->OTLtags['Plus'] = str_replace(array('dlig', 'hlig'), '', $this->OTLtags['Plus']);
  19626. }
  19627. if (strpos($v, 'NO-COMMON-LIGATURES') !== false) {
  19628. $this->OTLtags['Minus'] .= ' liga clig';
  19629. } elseif (strpos($v, 'COMMON-LIGATURES') !== false) {
  19630. $this->OTLtags['Minus'] = str_replace(array('liga', 'clig'), '', $this->OTLtags['Minus']);
  19631. }
  19632. if (strpos($v, 'NO-CONTEXTUAL') !== false) {
  19633. $this->OTLtags['Minus'] .= ' calt';
  19634. } elseif (strpos($v, 'CONTEXTUAL') !== false) {
  19635. $this->OTLtags['Minus'] = str_replace('calt', '', $this->OTLtags['Minus']);
  19636. }
  19637. if (strpos($v, 'NO-DISCRETIONARY-LIGATURES') !== false) {
  19638. $this->OTLtags['Plus'] = str_replace('dlig', '', $this->OTLtags['Plus']);
  19639. } elseif (strpos($v, 'DISCRETIONARY-LIGATURES') !== false) {
  19640. $this->OTLtags['Plus'] .= ' dlig';
  19641. }
  19642. if (strpos($v, 'NO-HISTORICAL-LIGATURES') !== false) {
  19643. $this->OTLtags['Plus'] = str_replace('hlig', '', $this->OTLtags['Plus']);
  19644. } elseif (strpos($v, 'HISTORICAL-LIGATURES') !== false) {
  19645. $this->OTLtags['Plus'] .= ' hlig';
  19646. }
  19647. break;
  19648. case 'FONT-VARIANT-NUMERIC':
  19649. $v = strtoupper($v);
  19650. if (!isset($this->OTLtags['Plus'])) {
  19651. $this->OTLtags['Plus'] = '';
  19652. }
  19653. if (strpos($v, 'NORMAL') !== false) {
  19654. $this->OTLtags['Plus'] = str_replace(array('ordn', 'zero', 'lnum', 'onum', 'pnum', 'tnum', 'frac', 'afrc'), '', $this->OTLtags['Plus']);
  19655. }
  19656. if (strpos($v, 'ORDINAL') !== false) {
  19657. $this->OTLtags['Plus'] .= ' ordn';
  19658. }
  19659. if (strpos($v, 'SLASHED-ZERO') !== false) {
  19660. $this->OTLtags['Plus'] .= ' zero';
  19661. }
  19662. if (strpos($v, 'LINING-NUMS') !== false) {
  19663. $this->OTLtags['Plus'] .= ' lnum';
  19664. $this->OTLtags['Plus'] = str_replace('onum', '', $this->OTLtags['Plus']);
  19665. } elseif (strpos($v, 'OLDSTYLE-NUMS') !== false) {
  19666. $this->OTLtags['Plus'] .= ' onum';
  19667. $this->OTLtags['Plus'] = str_replace('lnum', '', $this->OTLtags['Plus']);
  19668. }
  19669. if (strpos($v, 'PROPORTIONAL-NUMS') !== false) {
  19670. $this->OTLtags['Plus'] .= ' pnum';
  19671. $this->OTLtags['Plus'] = str_replace('tnum', '', $this->OTLtags['Plus']);
  19672. } elseif (strpos($v, 'TABULAR-NUMS') !== false) {
  19673. $this->OTLtags['Plus'] .= ' tnum';
  19674. $this->OTLtags['Plus'] = str_replace('pnum', '', $this->OTLtags['Plus']);
  19675. }
  19676. if (strpos($v, 'DIAGONAL-FRACTIONS') !== false) {
  19677. $this->OTLtags['Plus'] .= ' frac';
  19678. $this->OTLtags['Plus'] = str_replace('afrc', '', $this->OTLtags['Plus']);
  19679. } elseif (strpos($v, 'STACKED-FRACTIONS') !== false) {
  19680. $this->OTLtags['Plus'] .= ' afrc';
  19681. $this->OTLtags['Plus'] = str_replace('frac', '', $this->OTLtags['Plus']);
  19682. }
  19683. break;
  19684. case 'FONT-VARIANT-ALTERNATES': // Only supports historical-forms
  19685. $v = strtoupper($v);
  19686. if (!isset($this->OTLtags['Plus'])) {
  19687. $this->OTLtags['Plus'] = '';
  19688. }
  19689. if (strpos($v, 'NORMAL') !== false) {
  19690. $this->OTLtags['Plus'] = str_replace('hist', '', $this->OTLtags['Plus']);
  19691. }
  19692. if (strpos($v, 'HISTORICAL-FORMS') !== false) {
  19693. $this->OTLtags['Plus'] .= ' hist';
  19694. }
  19695. break;
  19696. case 'FONT-FEATURE-SETTINGS':
  19697. $v = strtolower($v);
  19698. if (strpos($v, 'normal') !== false) {
  19699. $this->OTLtags['FFMinus'] = '';
  19700. $this->OTLtags['FFPlus'] = '';
  19701. } else {
  19702. if (!isset($this->OTLtags['FFPlus'])) {
  19703. $this->OTLtags['FFPlus'] = '';
  19704. }
  19705. if (!isset($this->OTLtags['FFMinus'])) {
  19706. $this->OTLtags['FFMinus'] = '';
  19707. }
  19708. $tags = preg_split('/[,]/', $v);
  19709. foreach ($tags AS $t) {
  19710. if (preg_match('/[\"\']([a-zA-Z0-9]{4})[\"\']\s*(on|off|\d*){0,1}/', $t, $m)) {
  19711. if ($m[2] == 'off' || $m[2] === '0') {
  19712. if (strpos($this->OTLtags['FFMinus'], $m[1]) === false) {
  19713. $this->OTLtags['FFMinus'] .= ' ' . $m[1];
  19714. }
  19715. $this->OTLtags['FFPlus'] = preg_replace('/' . $m[1] . '[\d]*/', '', $this->OTLtags['FFPlus']);
  19716. } else {
  19717. if ($m[2] == 'on') {
  19718. $m[2] = '1';
  19719. }
  19720. if (strpos($this->OTLtags['FFPlus'], $m[1]) === false) {
  19721. $this->OTLtags['FFPlus'] .= ' ' . $m[1] . $m[2];
  19722. }
  19723. $this->OTLtags['FFMinus'] = str_replace($m[1], '', $this->OTLtags['FFMinus']);
  19724. }
  19725. }
  19726. }
  19727. }
  19728. break;
  19729. /* -- END OTL -- */
  19730. case 'TEXT-TRANSFORM': // none uppercase lowercase //Does support: capitalize
  19731. switch (strtoupper($v)) { //Not working 100%
  19732. case 'CAPITALIZE':
  19733. $this->textvar = ($this->textvar | FT_CAPITALIZE); // mPDF 5.7.1
  19734. $this->textvar = ($this->textvar & ~FT_UPPERCASE); // mPDF 5.7.1
  19735. $this->textvar = ($this->textvar & ~FT_LOWERCASE); // mPDF 5.7.1
  19736. break;
  19737. case 'UPPERCASE':
  19738. $this->textvar = ($this->textvar | FT_UPPERCASE); // mPDF 5.7.1
  19739. $this->textvar = ($this->textvar & ~FT_LOWERCASE); // mPDF 5.7.1
  19740. $this->textvar = ($this->textvar & ~FT_CAPITALIZE); // mPDF 5.7.1
  19741. break;
  19742. case 'LOWERCASE':
  19743. $this->textvar = ($this->textvar | FT_LOWERCASE); // mPDF 5.7.1
  19744. $this->textvar = ($this->textvar & ~FT_UPPERCASE); // mPDF 5.7.1
  19745. $this->textvar = ($this->textvar & ~FT_CAPITALIZE); // mPDF 5.7.1
  19746. break;
  19747. case 'NONE': break;
  19748. $this->textvar = ($this->textvar & ~FT_UPPERCASE); // mPDF 5.7.1
  19749. $this->textvar = ($this->textvar & ~FT_LOWERCASE); // mPDF 5.7.1
  19750. $this->textvar = ($this->textvar & ~FT_CAPITALIZE); // mPDF 5.7.1
  19751. }
  19752. break;
  19753. case 'TEXT-SHADOW':
  19754. $ts = $this->cssmgr->setCSStextshadow($v);
  19755. if ($ts) {
  19756. $this->textshadow = $ts;
  19757. }
  19758. break;
  19759. case 'HYPHENS':
  19760. if (strtoupper($v) == 'NONE') {
  19761. $this->textparam['hyphens'] = 2;
  19762. } elseif (strtoupper($v) == 'AUTO') {
  19763. $this->textparam['hyphens'] = 1;
  19764. } elseif (strtoupper($v) == 'MANUAL') {
  19765. $this->textparam['hyphens'] = 0;
  19766. }
  19767. break;
  19768. case 'TEXT-OUTLINE':
  19769. if (strtoupper($v) == 'NONE') {
  19770. $this->textparam['outline-s'] = false;
  19771. }
  19772. break;
  19773. case 'TEXT-OUTLINE-WIDTH':
  19774. case 'OUTLINE-WIDTH':
  19775. switch (strtoupper($v)) {
  19776. case 'THIN': $v = '0.03em';
  19777. break;
  19778. case 'MEDIUM': $v = '0.05em';
  19779. break;
  19780. case 'THICK': $v = '0.07em';
  19781. break;
  19782. }
  19783. $w = $this->ConvertSize($v, $this->FontSize, $this->FontSize);
  19784. if ($w) {
  19785. $this->textparam['outline-WIDTH'] = $w;
  19786. $this->textparam['outline-s'] = true;
  19787. } else {
  19788. $this->textparam['outline-s'] = false;
  19789. }
  19790. break;
  19791. case 'TEXT-OUTLINE-COLOR':
  19792. case 'OUTLINE-COLOR':
  19793. if (strtoupper($v) == 'INVERT') {
  19794. if ($this->colorarray) {
  19795. $cor = $this->colorarray;
  19796. $this->textparam['outline-COLOR'] = $this->_invertColor($cor);
  19797. } else {
  19798. $this->textparam['outline-COLOR'] = $this->ConvertColor(255);
  19799. }
  19800. } else {
  19801. $cor = $this->ConvertColor($v);
  19802. if ($cor) {
  19803. $this->textparam['outline-COLOR'] = $cor;
  19804. }
  19805. }
  19806. break;
  19807. case 'COLOR': // font color
  19808. $cor = $this->ConvertColor($v);
  19809. if ($cor) {
  19810. $this->colorarray = $cor;
  19811. $this->SetTColor($cor);
  19812. }
  19813. break;
  19814. }//end of switch($k)
  19815. }//end of foreach
  19816. // mPDF 5.7.3 inline text-decoration parameters
  19817. // Needs to be set at the end - after vertical-align = super/sub, so that textparam['text-baseline'] is set
  19818. if (isset($arrayaux['TEXT-DECORATION'])) {
  19819. $v = $arrayaux['TEXT-DECORATION']; // none underline line-through (strikeout) //Does not support: blink
  19820. if (stristr($v, 'LINE-THROUGH')) {
  19821. $this->textvar = ($this->textvar | FD_LINETHROUGH);
  19822. // mPDF 5.7.3 inline text-decoration parameters
  19823. if (isset($this->textparam['text-baseline'])) {
  19824. $this->textparam['s-decoration']['baseline'] = $this->textparam['text-baseline'];
  19825. } else {
  19826. $this->textparam['s-decoration']['baseline'] = 0;
  19827. }
  19828. $this->textparam['s-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  19829. $this->textparam['s-decoration']['fontsize'] = $this->FontSize;
  19830. $this->textparam['s-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  19831. }
  19832. if (stristr($v, 'UNDERLINE')) {
  19833. $this->textvar = ($this->textvar | FD_UNDERLINE);
  19834. // mPDF 5.7.3 inline text-decoration parameters
  19835. if (isset($this->textparam['text-baseline'])) {
  19836. $this->textparam['u-decoration']['baseline'] = $this->textparam['text-baseline'];
  19837. } else {
  19838. $this->textparam['u-decoration']['baseline'] = 0;
  19839. }
  19840. $this->textparam['u-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  19841. $this->textparam['u-decoration']['fontsize'] = $this->FontSize;
  19842. $this->textparam['u-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  19843. }
  19844. if (stristr($v, 'OVERLINE')) {
  19845. $this->textvar = ($this->textvar | FD_OVERLINE);
  19846. // mPDF 5.7.3 inline text-decoration parameters
  19847. if (isset($this->textparam['text-baseline'])) {
  19848. $this->textparam['o-decoration']['baseline'] = $this->textparam['text-baseline'];
  19849. } else {
  19850. $this->textparam['o-decoration']['baseline'] = 0;
  19851. }
  19852. $this->textparam['o-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  19853. $this->textparam['o-decoration']['fontsize'] = $this->FontSize;
  19854. $this->textparam['o-decoration']['color'] = strtoupper($this->TextColor); // change 0 0 0 rg to 0 0 0 RG
  19855. }
  19856. if (stristr($v, 'NONE')) {
  19857. $this->textvar = ($this->textvar & ~FD_UNDERLINE);
  19858. $this->textvar = ($this->textvar & ~FD_LINETHROUGH);
  19859. $this->textvar = ($this->textvar & ~FD_OVERLINE);
  19860. // mPDF 5.7.3 inline text-decoration parameters
  19861. if (isset($this->textparam['u-decoration'])) {
  19862. unset($this->textparam['u-decoration']);
  19863. }
  19864. if (isset($this->textparam['s-decoration'])) {
  19865. unset($this->textparam['s-decoration']);
  19866. }
  19867. if (isset($this->textparam['o-decoration'])) {
  19868. unset($this->textparam['o-decoration']);
  19869. }
  19870. }
  19871. }
  19872. // mPDF 6
  19873. if ($spanbordset) { // BORDER has been set on this INLINE element
  19874. if (isset($this->textparam['text-baseline'])) {
  19875. $this->textparam['bord-decoration']['baseline'] = $this->textparam['text-baseline'];
  19876. } else {
  19877. $this->textparam['bord-decoration']['baseline'] = 0;
  19878. }
  19879. $this->textparam['bord-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  19880. $this->textparam['bord-decoration']['fontsize'] = $this->FontSize;
  19881. }
  19882. if ($spanbgset) { // BACKGROUND[-COLOR] has been set on this INLINE element
  19883. if (isset($this->textparam['text-baseline'])) {
  19884. $this->textparam['bg-decoration']['baseline'] = $this->textparam['text-baseline'];
  19885. } else {
  19886. $this->textparam['bg-decoration']['baseline'] = 0;
  19887. }
  19888. $this->textparam['bg-decoration']['fontkey'] = $this->FontFamily . $this->FontStyle;
  19889. $this->textparam['bg-decoration']['fontsize'] = $this->FontSize;
  19890. }
  19891. }
  19892. /* -- END HTML-CSS -- */
  19893. function SetStyle($tag, $enable)
  19894. {
  19895. $this->$tag = $enable;
  19896. $style = '';
  19897. foreach (array('B', 'I') as $s) {
  19898. if ($this->$s) {
  19899. $style.=$s;
  19900. }
  19901. }
  19902. $this->currentfontstyle = $style;
  19903. $this->SetFont('', $style, 0, false);
  19904. }
  19905. // Set multiple styles at one time
  19906. function SetStylesArray($arr)
  19907. {
  19908. $style = '';
  19909. foreach (array('B', 'I') as $s) {
  19910. if (isset($arr[$s])) {
  19911. if ($arr[$s]) {
  19912. $this->$s = true;
  19913. $style.=$s;
  19914. } else {
  19915. $this->$s = false;
  19916. }
  19917. } elseif ($this->$s) {
  19918. $style.=$s;
  19919. }
  19920. }
  19921. $this->currentfontstyle = $style;
  19922. $this->SetFont('', $style, 0, false);
  19923. }
  19924. // Set multiple styles at one $str e.g. "BI"
  19925. function SetStyles($str)
  19926. {
  19927. $style = '';
  19928. foreach (array('B', 'I') as $s) {
  19929. if (strpos($str, $s) !== false) {
  19930. $this->$s = true;
  19931. $style.=$s;
  19932. } else {
  19933. $this->$s = false;
  19934. }
  19935. }
  19936. $this->currentfontstyle = $style;
  19937. $this->SetFont('', $style, 0, false);
  19938. }
  19939. function ResetStyles()
  19940. {
  19941. foreach (array('B', 'I') as $s) {
  19942. $this->$s = false;
  19943. }
  19944. $this->currentfontstyle = '';
  19945. $this->SetFont('', '', 0, false);
  19946. }
  19947. function DisableTags($str = '')
  19948. {
  19949. if ($str == '') { //enable all tags
  19950. //Insert new supported tags in the long string below.
  19951. $this->enabledtags = "<a><acronym><address><article><aside><b><bdi><bdo><big><blockquote><br><caption><center><cite><code><del><details><dd><div><dl><dt><em><fieldset><figcaption><figure><font><form><h1><h2><h3><h4><h5><h6><hgroup><hr><i><img><input><ins><kbd><legend><li><main><mark><meter><nav><ol><option><p><pre><progress><q><s><samp><section><select><small><span><strike><strong><sub><summary><sup><table><tbody><td><template><textarea><tfoot><th><thead><time><tr><tt><u><ul><var><footer><header><annotation><bookmark><jpgraph><textcircle><barcode><dottab><indexentry><indexinsert><watermarktext><watermarkimage><tts><ttz><tta><column_break><columnbreak><newcolumn><newpage><page_break><pagebreak><formfeed><columns><toc><tocentry><tocpagebreak><pageheader><pagefooter><setpageheader><setpagefooter><sethtmlpageheader><sethtmlpagefooter>";
  19952. } else {
  19953. $str = explode(",", $str);
  19954. foreach ($str as $v)
  19955. $this->enabledtags = str_replace(trim($v), '', $this->enabledtags);
  19956. }
  19957. }
  19958. /* -- TABLES -- */
  19959. function TableCheckMinWidth($maxwidth, $forcewrap = 0, $textbuffer, $checkletter = false)
  19960. { // mPDF 6
  19961. $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
  19962. $acclongest = 0; // mPDF 6 (accumulated length max across > 1 chunk)
  19963. $biggestword = 0;
  19964. $toonarrow = false;
  19965. if ((count($textbuffer) == 0) or ( (count($textbuffer) == 1) && ($textbuffer[0][0] == ''))) {
  19966. return 0;
  19967. }
  19968. foreach ($textbuffer as $chunk) {
  19969. $line = $chunk[0];
  19970. $OTLdata = (isset($chunk[18]) ? $chunk[18] : NULL);
  19971. // mPDF ITERATION
  19972. if ($this->iterationCounter)
  19973. $line = preg_replace('/{iteration ([a-zA-Z0-9_]+)}/', '\\1', $line);
  19974. // IMAGES & FORM ELEMENTS
  19975. if (substr($line, 0, 3) == "\xbb\xa4\xac") { //inline object - FORM element or IMAGE!
  19976. $objattr = $this->_getObjAttr($line);
  19977. if ($objattr['type'] != 'hr' && isset($objattr['width']) && ($objattr['width'] / $this->shrin_k) > ($maxwidth + 0.0001)) {
  19978. if (($objattr['width'] / $this->shrin_k) > $biggestword) {
  19979. $biggestword = ($objattr['width'] / $this->shrin_k);
  19980. }
  19981. $toonarrow = true;
  19982. }
  19983. continue;
  19984. }
  19985. if ($line == "\n") {
  19986. $acclength = 0; // mPDF 6 (accumulated length across > 1 chunk)
  19987. continue;
  19988. }
  19989. $line = trim($line);
  19990. if (!empty($OTLdata)) {
  19991. $this->otl->trimOTLdata($OTLdata, true, true);
  19992. } // *OTL*
  19993. // SET FONT SIZE/STYLE from $chunk[n]
  19994. // FONTSIZE
  19995. if (isset($chunk[11]) and $chunk[11] != '') {
  19996. if ($this->shrin_k) {
  19997. $this->SetFontSize($chunk[11] / $this->shrin_k, false);
  19998. } else {
  19999. $this->SetFontSize($chunk[11], false);
  20000. }
  20001. }
  20002. // FONTFAMILY
  20003. if (isset($chunk[4]) and $chunk[4] != '') {
  20004. $font = $this->SetFont($chunk[4], $this->FontStyle, 0, false);
  20005. }
  20006. // B I
  20007. if (isset($chunk[2]) and $chunk[2] != '') {
  20008. $this->SetStyles($chunk[2]);
  20009. }
  20010. $lbw = $rbw = 0; // Border widths
  20011. if (isset($chunk[16]) && !empty($chunk[16])) { //Border
  20012. $this->spanborddet = $chunk[16];
  20013. $lbw = (isset($this->spanborddet['L']['w']) ? $this->spanborddet['L']['w'] : 0);
  20014. $rbw = (isset($this->spanborddet['R']['w']) ? $this->spanborddet['R']['w'] : 0);
  20015. }
  20016. if (isset($chunk[15])) { // Word spacing
  20017. $this->wSpacingCSS = $chunk[15];
  20018. if ($this->wSpacingCSS && strtoupper($this->wSpacingCSS) != 'NORMAL') {
  20019. $this->minwSpacing = $this->ConvertSize($this->wSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  20020. }
  20021. }
  20022. if (isset($chunk[14])) { // Letter spacing
  20023. $this->lSpacingCSS = $chunk[14];
  20024. if (($this->lSpacingCSS || $this->lSpacingCSS === '0') && strtoupper($this->lSpacingCSS) != 'NORMAL') {
  20025. $this->fixedlSpacing = $this->ConvertSize($this->lSpacingCSS, $this->FontSize) / $this->shrin_k; // mPDF 5.7.3
  20026. }
  20027. }
  20028. if (isset($chunk[8])) { // mPDF 5.7.1
  20029. $this->textvar = $chunk[8];
  20030. }
  20031. // mPDF 6
  20032. // If overflow==wrap ($checkletter) OR (No word breaks and contains CJK)
  20033. if ($checkletter || (!preg_match('/(\xe2\x80\x8b| )/', trim($line)) && preg_match("/([" . $this->pregCJKchars . "])/u", $line) )) {
  20034. if (preg_match("/([" . $this->pregCJKchars . "])/u", $line)) {
  20035. $checkCJK = true;
  20036. } else {
  20037. $checkCJK = false;
  20038. }
  20039. $letters = preg_split('//u', $line);
  20040. foreach ($letters as $k => $letter) {
  20041. // mPDF 6
  20042. if ($checkCJK) {
  20043. if (preg_match("/[" . $this->CJKleading . "]/u", $letter) && $k > 0) {
  20044. $letter = $letters[$k - 1] . $letter;
  20045. }
  20046. if (preg_match("/[" . $this->CJKfollowing . "]/u", $letter) && $k < (count($letters) - 1)) {
  20047. $letter = $letter . $letters[$k + 1];
  20048. }
  20049. }
  20050. $letterwidth = $this->GetStringWidth($letter, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
  20051. // so don't have to split OTLdata for each word
  20052. if ($k == 0) {
  20053. $letterwidth += $lbw;
  20054. }
  20055. if ($k == (count($letters) - 1)) {
  20056. $letterwidth += $rbw;
  20057. }
  20058. //Warn user that maxwidth is insufficient
  20059. if ($letterwidth > $maxwidth + 0.0001) {
  20060. if ($letterwidth > $biggestword) {
  20061. $biggestword = $letterwidth;
  20062. }
  20063. $toonarrow = true;
  20064. }
  20065. }
  20066. } else {
  20067. // mPDF 6
  20068. // Need to account for any XAdvance in GPOSinfo (OTLdata = $chunk[18])
  20069. $wordXAdvance = array();
  20070. if (isset($chunk[18]) && $chunk[18]) {
  20071. preg_match_all('/(\xe2\x80\x8b| )/', $line, $spaces, PREG_OFFSET_CAPTURE); // U+200B Zero Width word boundary, or space
  20072. $lastoffset = 0;
  20073. $k = -1; // Added so that if no spaces found, "last word" later is calculated for the one and only word
  20074. foreach ($spaces[0] as $k => $m) {
  20075. $offset = $m[1];
  20076. // ...TableCheckMinWidth...
  20077. // At this point, BIDI not applied, Writing direction is not set, and XAdvanceL balances XAdvanceR
  20078. for ($n = $lastoffset; $n < $offset; $n++) {
  20079. if (isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
  20080. if (isset($wordXAdvance[$k])) {
  20081. $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  20082. } else {
  20083. $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  20084. }
  20085. }
  20086. }
  20087. $lastoffset = $offset + 1;
  20088. }
  20089. $k++; // last word
  20090. foreach ($chunk[18]['GPOSinfo'] AS $n => $gpos) {
  20091. if ($n >= $lastoffset && isset($chunk[18]['GPOSinfo'][$n]['XAdvanceL'])) {
  20092. if (isset($wordXAdvance[$k])) {
  20093. $wordXAdvance[$k] += $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  20094. } else {
  20095. $wordXAdvance[$k] = $chunk[18]['GPOSinfo'][$n]['XAdvanceL'];
  20096. }
  20097. }
  20098. }
  20099. }
  20100. $words = preg_split('/(\xe2\x80\x8b| )/', $line); // U+200B Zero Width word boundary, or space
  20101. foreach ($words as $k => $word) {
  20102. $word = trim($word);
  20103. $wordwidth = $this->GetStringWidth($word, false, false, $chunk[8]); // Pass $textvar ($chunk[8]), but do OTLdata here
  20104. // so don't have to split OTLdata for each word
  20105. if (isset($wordXAdvance[$k])) {
  20106. $wordwidth += ($wordXAdvance[$k] * 1000 / $this->CurrentFont['unitsPerEm']) * ($this->FontSize / 1000);
  20107. }
  20108. if ($k == 0) {
  20109. $wordwidth += $lbw;
  20110. }
  20111. if ($k == (count($words) - 1)) {
  20112. $wordwidth += $rbw;
  20113. }
  20114. // mPDF 6
  20115. if (count($words) == 1 && substr($chunk[0], 0, 1) != ' ') {
  20116. $acclength += $wordwidth;
  20117. } elseif (count($words) > 1 && $k == 0 && substr($chunk[0], 0, 1) != ' ') {
  20118. $acclength += $wordwidth;
  20119. } else {
  20120. $acclength = $wordwidth;
  20121. }
  20122. $acclongest = max($acclongest, $acclength);
  20123. if (count($words) == 1 && substr($chunk[0], -1, 1) == ' ') {
  20124. $acclength = 0;
  20125. } elseif (count($words) > 1 && ($k != (count($words) - 1) || substr($chunk[0], -1, 1) == ' ')) {
  20126. $acclength = 0;
  20127. }
  20128. //Warn user that maxwidth is insufficient
  20129. if ($wordwidth > $maxwidth + 0.0001) {
  20130. if ($wordwidth > $biggestword) {
  20131. $biggestword = $wordwidth;
  20132. }
  20133. $toonarrow = true;
  20134. }
  20135. }
  20136. }
  20137. // mPDF 6 Accumulated length of biggest word - across multiple chunks
  20138. if ($acclongest > $maxwidth + 0.0001) {
  20139. if ($acclongest > $biggestword) {
  20140. $biggestword = $acclongest;
  20141. }
  20142. $toonarrow = true;
  20143. }
  20144. // RESET FONT SIZE/STYLE
  20145. // RESETTING VALUES
  20146. //Now we must deactivate what we have used
  20147. if (isset($chunk[2]) and $chunk[2] != '') {
  20148. $this->ResetStyles();
  20149. }
  20150. if (isset($chunk[4]) and $chunk[4] != '') {
  20151. $this->SetFont($this->default_font, $this->FontStyle, 0, false);
  20152. }
  20153. if (isset($chunk[11]) and $chunk[11] != '') {
  20154. $this->SetFontSize($this->default_font_size, false);
  20155. }
  20156. $this->spanborddet = array();
  20157. $this->textvar = 0x00; // mPDF 5.7.1
  20158. $this->OTLtags = array();
  20159. $this->lSpacingCSS = '';
  20160. $this->wSpacingCSS = '';
  20161. $this->fixedlSpacing = false;
  20162. $this->minwSpacing = 0;
  20163. }
  20164. //Return -(wordsize) if word is bigger than maxwidth
  20165. // ADDED
  20166. if (($toonarrow) && ($this->table_error_report)) {
  20167. throw new MpdfException("Word is too long to fit in table - " . $this->table_error_report_param);
  20168. }
  20169. if ($toonarrow)
  20170. return -$biggestword;
  20171. else
  20172. return 1;
  20173. }
  20174. function shrinkTable(&$table, $k)
  20175. {
  20176. $table['border_spacing_H'] /= $k;
  20177. $table['border_spacing_V'] /= $k;
  20178. $table['padding']['T'] /= $k;
  20179. $table['padding']['R'] /= $k;
  20180. $table['padding']['B'] /= $k;
  20181. $table['padding']['L'] /= $k;
  20182. $table['margin']['T'] /= $k;
  20183. $table['margin']['R'] /= $k;
  20184. $table['margin']['B'] /= $k;
  20185. $table['margin']['L'] /= $k;
  20186. $table['border_details']['T']['w'] /= $k;
  20187. $table['border_details']['R']['w'] /= $k;
  20188. $table['border_details']['B']['w'] /= $k;
  20189. $table['border_details']['L']['w'] /= $k;
  20190. if (isset($table['max_cell_border_width']['T']))
  20191. $table['max_cell_border_width']['T'] /= $k;
  20192. if (isset($table['max_cell_border_width']['R']))
  20193. $table['max_cell_border_width']['R'] /= $k;
  20194. if (isset($table['max_cell_border_width']['B']))
  20195. $table['max_cell_border_width']['B'] /= $k;
  20196. if (isset($table['max_cell_border_width']['L']))
  20197. $table['max_cell_border_width']['L'] /= $k;
  20198. if ($this->simpleTables) {
  20199. $table['simple']['border_details']['T']['w'] /= $k;
  20200. $table['simple']['border_details']['R']['w'] /= $k;
  20201. $table['simple']['border_details']['B']['w'] /= $k;
  20202. $table['simple']['border_details']['L']['w'] /= $k;
  20203. }
  20204. $table['miw'] /= $k;
  20205. $table['maw'] /= $k;
  20206. for ($j = 0; $j < $table['nc']; $j++) { //columns
  20207. $table['wc'][$j]['miw'] /= $k;
  20208. $table['wc'][$j]['maw'] /= $k;
  20209. if (isset($table['decimal_align'][$j]['maxs0']) && $table['decimal_align'][$j]['maxs0']) {
  20210. $table['decimal_align'][$j]['maxs0'] /= $k;
  20211. }
  20212. if (isset($table['decimal_align'][$j]['maxs1']) && $table['decimal_align'][$j]['maxs1']) {
  20213. $table['decimal_align'][$j]['maxs1'] /= $k;
  20214. }
  20215. if (isset($table['wc'][$j]['absmiw']) && $table['wc'][$j]['absmiw'])
  20216. $table['wc'][$j]['absmiw'] /= $k;
  20217. for ($i = 0; $i < $table['nr']; $i++) { //rows
  20218. $c = &$table['cells'][$i][$j];
  20219. if (isset($c) && $c) {
  20220. if (!$this->simpleTables) {
  20221. if ($this->packTableData) {
  20222. $cell = $this->_unpackCellBorder($c['borderbin']);
  20223. $cell['border_details']['T']['w'] /= $k;
  20224. $cell['border_details']['R']['w'] /= $k;
  20225. $cell['border_details']['B']['w'] /= $k;
  20226. $cell['border_details']['L']['w'] /= $k;
  20227. $cell['border_details']['mbw']['TL'] /= $k;
  20228. $cell['border_details']['mbw']['TR'] /= $k;
  20229. $cell['border_details']['mbw']['BL'] /= $k;
  20230. $cell['border_details']['mbw']['BR'] /= $k;
  20231. $cell['border_details']['mbw']['LT'] /= $k;
  20232. $cell['border_details']['mbw']['LB'] /= $k;
  20233. $cell['border_details']['mbw']['RT'] /= $k;
  20234. $cell['border_details']['mbw']['RB'] /= $k;
  20235. $c['borderbin'] = $this->_packCellBorder($cell);
  20236. } else {
  20237. $c['border_details']['T']['w'] /= $k;
  20238. $c['border_details']['R']['w'] /= $k;
  20239. $c['border_details']['B']['w'] /= $k;
  20240. $c['border_details']['L']['w'] /= $k;
  20241. $c['border_details']['mbw']['TL'] /= $k;
  20242. $c['border_details']['mbw']['TR'] /= $k;
  20243. $c['border_details']['mbw']['BL'] /= $k;
  20244. $c['border_details']['mbw']['BR'] /= $k;
  20245. $c['border_details']['mbw']['LT'] /= $k;
  20246. $c['border_details']['mbw']['LB'] /= $k;
  20247. $c['border_details']['mbw']['RT'] /= $k;
  20248. $c['border_details']['mbw']['RB'] /= $k;
  20249. }
  20250. }
  20251. $c['padding']['T'] /= $k;
  20252. $c['padding']['R'] /= $k;
  20253. $c['padding']['B'] /= $k;
  20254. $c['padding']['L'] /= $k;
  20255. if (isset($c['maxs'])) {
  20256. $c['maxs'] /= $k;
  20257. }
  20258. if (isset($c['w'])) {
  20259. $c['w'] /= $k;
  20260. }
  20261. $c['s'] /= $k;
  20262. $c['maw'] /= $k;
  20263. $c['miw'] /= $k;
  20264. if (isset($c['h'])) {
  20265. $c['h'] /= $k;
  20266. } // mPDF 5.7.4
  20267. if (isset($c['absmiw']))
  20268. $c['absmiw'] /= $k;
  20269. if (isset($c['nestedmaw']))
  20270. $c['nestedmaw'] /= $k;
  20271. if (isset($c['nestedmiw']))
  20272. $c['nestedmiw'] /= $k;
  20273. if (isset($c['textbuffer'])) {
  20274. foreach ($c['textbuffer'] AS $n => $tb) {
  20275. if (!empty($tb[16])) {
  20276. $c['textbuffer'][$n][16]['T']['w'] /= $k;
  20277. $c['textbuffer'][$n][16]['B']['w'] /= $k;
  20278. $c['textbuffer'][$n][16]['L']['w'] /= $k;
  20279. $c['textbuffer'][$n][16]['R']['w'] /= $k;
  20280. }
  20281. }
  20282. }
  20283. unset($c);
  20284. }
  20285. }//rows
  20286. }//columns
  20287. }
  20288. function read_short(&$fh)
  20289. {
  20290. $s = fread($fh, 2);
  20291. $a = (ord($s[0]) << 8) + ord($s[1]);
  20292. if ($a & (1 << 15)) {
  20293. $a = ($a - (1 << 16));
  20294. }
  20295. return $a;
  20296. }
  20297. function _packCellBorder($cell)
  20298. {
  20299. if (!is_array($cell) || !isset($cell)) {
  20300. return '';
  20301. }
  20302. if (!$this->packTableData) {
  20303. return $cell;
  20304. }
  20305. // = 186 bytes
  20306. $bindata = pack("nnda6A10nnda6A10nnda6A10nnda6A10nd9", $cell['border'], $cell['border_details']['R']['s'], $cell['border_details']['R']['w'], $cell['border_details']['R']['c'], $cell['border_details']['R']['style'], $cell['border_details']['R']['dom'], $cell['border_details']['L']['s'], $cell['border_details']['L']['w'], $cell['border_details']['L']['c'], $cell['border_details']['L']['style'], $cell['border_details']['L']['dom'], $cell['border_details']['T']['s'], $cell['border_details']['T']['w'], $cell['border_details']['T']['c'], $cell['border_details']['T']['style'], $cell['border_details']['T']['dom'], $cell['border_details']['B']['s'], $cell['border_details']['B']['w'], $cell['border_details']['B']['c'], $cell['border_details']['B']['style'], $cell['border_details']['B']['dom'], $cell['border_details']['mbw']['BL'], $cell['border_details']['mbw']['BR'], $cell['border_details']['mbw']['RT'], $cell['border_details']['mbw']['RB'], $cell['border_details']['mbw']['TL'], $cell['border_details']['mbw']['TR'], $cell['border_details']['mbw']['LT'], $cell['border_details']['mbw']['LB'], (isset($cell['border_details']['cellposdom']) ? $cell['border_details']['cellposdom'] : 0)
  20307. );
  20308. return $bindata;
  20309. }
  20310. function _getBorderWidths($bindata)
  20311. {
  20312. if (!$bindata) {
  20313. return array(0, 0, 0, 0);
  20314. }
  20315. if (!$this->packTableData) {
  20316. return array($bindata['border_details']['T']['w'], $bindata['border_details']['R']['w'], $bindata['border_details']['B']['w'], $bindata['border_details']['L']['w']);
  20317. }
  20318. $bd = unpack("nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd", $bindata);
  20319. $cell['border_details']['R']['w'] = $bd['rw'];
  20320. $cell['border_details']['L']['w'] = $bd['lw'];
  20321. $cell['border_details']['T']['w'] = $bd['tw'];
  20322. $cell['border_details']['B']['w'] = $bd['bw'];
  20323. return array($bd['tw'], $bd['rw'], $bd['bw'], $bd['lw']);
  20324. }
  20325. function _unpackCellBorder($bindata)
  20326. {
  20327. if (!$bindata) {
  20328. return array();
  20329. }
  20330. if (!$this->packTableData) {
  20331. return $bindata;
  20332. }
  20333. $bd = unpack("nbord/nrs/drw/a6rca/A10rst/nrd/nls/dlw/a6lca/A10lst/nld/nts/dtw/a6tca/A10tst/ntd/nbs/dbw/a6bca/A10bst/nbd/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd", $bindata);
  20334. $cell['border'] = $bd['bord'];
  20335. $cell['border_details']['R']['s'] = $bd['rs'];
  20336. $cell['border_details']['R']['w'] = $bd['rw'];
  20337. $cell['border_details']['R']['c'] = str_pad($bd['rca'], 6, "\x00");
  20338. $cell['border_details']['R']['style'] = trim($bd['rst']);
  20339. $cell['border_details']['R']['dom'] = $bd['rd'];
  20340. $cell['border_details']['L']['s'] = $bd['ls'];
  20341. $cell['border_details']['L']['w'] = $bd['lw'];
  20342. $cell['border_details']['L']['c'] = str_pad($bd['lca'], 6, "\x00");
  20343. $cell['border_details']['L']['style'] = trim($bd['lst']);
  20344. $cell['border_details']['L']['dom'] = $bd['ld'];
  20345. $cell['border_details']['T']['s'] = $bd['ts'];
  20346. $cell['border_details']['T']['w'] = $bd['tw'];
  20347. $cell['border_details']['T']['c'] = str_pad($bd['tca'], 6, "\x00");
  20348. $cell['border_details']['T']['style'] = trim($bd['tst']);
  20349. $cell['border_details']['T']['dom'] = $bd['td'];
  20350. $cell['border_details']['B']['s'] = $bd['bs'];
  20351. $cell['border_details']['B']['w'] = $bd['bw'];
  20352. $cell['border_details']['B']['c'] = str_pad($bd['bca'], 6, "\x00");
  20353. $cell['border_details']['B']['style'] = trim($bd['bst']);
  20354. $cell['border_details']['B']['dom'] = $bd['bd'];
  20355. $cell['border_details']['mbw']['BL'] = $bd['mbl'];
  20356. $cell['border_details']['mbw']['BR'] = $bd['mbr'];
  20357. $cell['border_details']['mbw']['RT'] = $bd['mrt'];
  20358. $cell['border_details']['mbw']['RB'] = $bd['mrb'];
  20359. $cell['border_details']['mbw']['TL'] = $bd['mtl'];
  20360. $cell['border_details']['mbw']['TR'] = $bd['mtr'];
  20361. $cell['border_details']['mbw']['LT'] = $bd['mlt'];
  20362. $cell['border_details']['mbw']['LB'] = $bd['mlb'];
  20363. $cell['border_details']['cellposdom'] = $bd['cpd'];
  20364. return($cell);
  20365. }
  20366. ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
  20367. ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
  20368. ////////////////////////TABLE CODE (from PDFTable)/////////////////////////////////////
  20369. //table Array of (w, h, bc, nr, wc, hr, cells)
  20370. //w Width of table
  20371. //h Height of table
  20372. //nc Number column
  20373. //nr Number row
  20374. //hr List of height of each row
  20375. //wc List of width of each column
  20376. //cells List of cells of each rows, cells[i][j] is a cell in the table
  20377. function _tableColumnWidth(&$table, $firstpass = false)
  20378. {
  20379. $cs = &$table['cells'];
  20380. $nc = $table['nc'];
  20381. $nr = $table['nr'];
  20382. $listspan = array();
  20383. if ($table['borders_separate']) {
  20384. $tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H'];
  20385. } else {
  20386. $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
  20387. }
  20388. // ADDED table['l'][colno]
  20389. // = total length of text approx (using $c['s']) in that column - used to approximately distribute col widths in _tableWidth
  20390. //
  20391. for ($j = 0; $j < $nc; $j++) { //columns
  20392. $wc = &$table['wc'][$j];
  20393. for ($i = 0; $i < $nr; $i++) { //rows
  20394. if (isset($cs[$i][$j]) && $cs[$i][$j]) {
  20395. $c = &$cs[$i][$j];
  20396. if ($this->simpleTables) {
  20397. if ($table['borders_separate']) { // NB twice border width
  20398. $extrcw = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
  20399. } else {
  20400. $extrcw = $table['simple']['border_details']['L']['w'] / 2 + $table['simple']['border_details']['R']['w'] / 2 + $c['padding']['L'] + $c['padding']['R'];
  20401. }
  20402. } else {
  20403. if ($this->packTableData) {
  20404. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
  20405. } else {
  20406. $br = $c['border_details']['R']['w'];
  20407. $bl = $c['border_details']['L']['w'];
  20408. }
  20409. if ($table['borders_separate']) { // NB twice border width
  20410. $extrcw = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
  20411. } else {
  20412. $extrcw = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
  20413. }
  20414. }
  20415. //$mw = $this->GetStringWidth('W') + $extrcw ;
  20416. $mw = $extrcw; // mPDF 6
  20417. if (substr($c['a'], 0, 1) == 'D') {
  20418. $mw = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'] + $extrcw;
  20419. }
  20420. $c['absmiw'] = $mw;
  20421. if (isset($c['R']) && $c['R']) {
  20422. $c['maw'] = $c['miw'] = $this->FontSize + $extrcw;
  20423. if (isset($c['w'])) { // If cell width is specified
  20424. if ($c['miw'] < $c['w']) {
  20425. $c['miw'] = $c['w'];
  20426. }
  20427. }
  20428. if (!isset($c['colspan'])) {
  20429. if ($wc['miw'] < $c['miw']) {
  20430. $wc['miw'] = $c['miw'];
  20431. }
  20432. if ($wc['maw'] < $c['maw']) {
  20433. $wc['maw'] = $c['maw'];
  20434. }
  20435. if ($firstpass) {
  20436. if (isset($table['l'][$j])) {
  20437. $table['l'][$j] += $c['miw'];
  20438. } else {
  20439. $table['l'][$j] = $c['miw'];
  20440. }
  20441. }
  20442. }
  20443. if ($c['miw'] > $wc['miw']) {
  20444. $wc['miw'] = $c['miw'];
  20445. }
  20446. if ($wc['miw'] > $wc['maw']) {
  20447. $wc['maw'] = $wc['miw'];
  20448. }
  20449. continue;
  20450. }
  20451. if ($firstpass) {
  20452. if (isset($c['s'])) {
  20453. $c['s'] += $extrcw;
  20454. }
  20455. if (isset($c['maxs'])) {
  20456. $c['maxs'] += $extrcw;
  20457. }
  20458. if (isset($c['nestedmiw'])) {
  20459. $c['nestedmiw'] += $extrcw;
  20460. }
  20461. if (isset($c['nestedmaw'])) {
  20462. $c['nestedmaw'] += $extrcw;
  20463. }
  20464. }
  20465. // If minimum width has already been set by a nested table or inline object (image/form), use it
  20466. if (isset($c['nestedmiw']) && (!isset($this->table[1][1]['overflow']) || $this->table[1][1]['overflow'] != 'visible')) {
  20467. $miw = $c['nestedmiw'];
  20468. } else {
  20469. $miw = $mw;
  20470. }
  20471. if (isset($c['maxs']) && $c['maxs'] != '') {
  20472. $c['s'] = $c['maxs'];
  20473. }
  20474. // If maximum width has already been set by a nested table, use it
  20475. if (isset($c['nestedmaw'])) {
  20476. $c['maw'] = $c['nestedmaw'];
  20477. } else
  20478. $c['maw'] = $c['s'];
  20479. if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
  20480. if (($c['maw'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
  20481. $c['maw'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
  20482. }
  20483. }
  20484. if (isset($c['nowrap']) && $c['nowrap']) {
  20485. $miw = $c['maw'];
  20486. }
  20487. if (isset($c['wpercent']) && $firstpass) {
  20488. if (isset($c['colspan'])) { // Not perfect - but % set on colspan is shared equally on cols.
  20489. for ($k = 0; $k < $c['colspan']; $k++) {
  20490. $table['wc'][($j + $k)]['wpercent'] = $c['wpercent'] / $c['colspan'];
  20491. }
  20492. } else {
  20493. if (isset($table['w']) && $table['w']) {
  20494. $c['w'] = $c['wpercent'] / 100 * ($table['w'] - $tblbw );
  20495. }
  20496. $wc['wpercent'] = $c['wpercent'];
  20497. }
  20498. }
  20499. if (isset($table['overflow']) && $table['overflow'] == 'visible' && $table['level'] == 1) {
  20500. if (isset($c['w']) && ($c['w'] + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
  20501. $c['w'] = $this->blk[$this->blklvl]['inner_width'] - $tblbw;
  20502. }
  20503. }
  20504. if (isset($c['w'])) { // If cell width is specified
  20505. if ($miw < $c['w']) {
  20506. $c['miw'] = $c['w'];
  20507. } // Cell min width = that specified
  20508. if ($miw > $c['w']) {
  20509. $c['miw'] = $c['w'] = $miw;
  20510. } // If width specified is less than minimum allowed (W) increase it
  20511. // mPDF 5.7.4 Do not set column width in colspan
  20512. // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
  20513. if (!isset($c['colspan'])) {
  20514. if (!isset($wc['w'])) {
  20515. $wc['w'] = 1;
  20516. } // If the Col width is not specified = set it to 1
  20517. }
  20518. // mPDF 5.7.3 cf. http://www.mpdf1.com/forum/discussion/1648/nested-table-bug-
  20519. $c['maw'] = $c['w'];
  20520. } else {
  20521. $c['miw'] = $miw;
  20522. } // If cell width not specified -> set Cell min width it to minimum allowed (W)
  20523. if (isset($c['miw']) && $c['maw'] < $c['miw']) {
  20524. $c['maw'] = $c['miw'];
  20525. } // If Cell max width < Minwidth - increase it to =
  20526. if (!isset($c['colspan'])) {
  20527. if (isset($c['miw']) && $wc['miw'] < $c['miw']) {
  20528. $wc['miw'] = $c['miw'];
  20529. } // Update Col Minimum and maximum widths
  20530. if ($wc['maw'] < $c['maw']) {
  20531. $wc['maw'] = $c['maw'];
  20532. }
  20533. if ((isset($wc['absmiw']) && $wc['absmiw'] < $c['absmiw']) || !isset($wc['absmiw'])) {
  20534. $wc['absmiw'] = $c['absmiw'];
  20535. } // Update Col Minimum and maximum widths
  20536. if (isset($table['l'][$j])) {
  20537. $table['l'][$j] += $c['s'];
  20538. } else {
  20539. $table['l'][$j] = $c['s'];
  20540. }
  20541. } else {
  20542. $listspan[] = array($i, $j);
  20543. }
  20544. //Check if minimum width of the whole column is big enough for largest word to fit
  20545. // mPDF 6
  20546. if (isset($c['textbuffer'])) {
  20547. if (isset($table['overflow']) && $table['overflow'] == 'wrap') {
  20548. $letter = true;
  20549. } // check for maximum width of letters
  20550. else {
  20551. $letter = false;
  20552. }
  20553. $minwidth = $this->TableCheckMinWidth($wc['miw'] - $extrcw, 0, $c['textbuffer'], $letter);
  20554. } else {
  20555. $minwidth = 0;
  20556. }
  20557. if ($minwidth < 0) {
  20558. //increase minimum width
  20559. if (!isset($c['colspan'])) {
  20560. $wc['miw'] = max($wc['miw'], ((-$minwidth) + $extrcw));
  20561. } else {
  20562. $c['miw'] = max($c['miw'], ((-$minwidth) + $extrcw));
  20563. }
  20564. }
  20565. if (!isset($c['colspan'])) {
  20566. if ($wc['miw'] > $wc['maw']) {
  20567. $wc['maw'] = $wc['miw'];
  20568. } //update maximum width, if needed
  20569. }
  20570. }
  20571. unset($c);
  20572. }//rows
  20573. }//columns
  20574. // COLUMN SPANS
  20575. $wc = &$table['wc'];
  20576. foreach ($listspan as $span) {
  20577. list($i, $j) = $span;
  20578. $c = &$cs[$i][$j];
  20579. $lc = $j + $c['colspan'];
  20580. if ($lc > $nc) {
  20581. $lc = $nc;
  20582. }
  20583. $wis = $wisa = 0;
  20584. $was = $wasa = 0;
  20585. $list = array();
  20586. for ($k = $j; $k < $lc; $k++) {
  20587. if (isset($table['l'][$k])) {
  20588. if ($c['R']) {
  20589. $table['l'][$k] += $c['miw'] / $c['colspan'];
  20590. } else {
  20591. $table['l'][$k] += $c['s'] / $c['colspan'];
  20592. }
  20593. } else {
  20594. if ($c['R']) {
  20595. $table['l'][$k] = $c['miw'] / $c['colspan'];
  20596. } else {
  20597. $table['l'][$k] = $c['s'] / $c['colspan'];
  20598. }
  20599. }
  20600. $wis += $wc[$k]['miw']; // $wis is the sum of the column miw in the colspan
  20601. $was += $wc[$k]['maw']; // $was is the sum of the column maw in the colspan
  20602. if (!isset($c['w'])) {
  20603. $list[] = $k;
  20604. $wisa += $wc[$k]['miw']; // $wisa is the sum of the column miw in cells with no width specified in the colspan
  20605. $wasa += $wc[$k]['maw']; // $wasa is the sum of the column maw in cells with no width specified in the colspan
  20606. }
  20607. }
  20608. if ($c['miw'] > $wis) {
  20609. if (!$wis) {
  20610. for ($k = $j; $k < $lc; $k++) {
  20611. $wc[$k]['miw'] = $c['miw'] / $c['colspan'];
  20612. }
  20613. } elseif (!count($list)) {
  20614. $wi = $c['miw'] - $wis;
  20615. for ($k = $j; $k < $lc; $k++) {
  20616. $wc[$k]['miw'] += ($wc[$k]['miw'] / $wis) * $wi;
  20617. }
  20618. } else {
  20619. $wi = $c['miw'] - $wis;
  20620. // mPDF 5.7.2 Extra min width distributed proportionately to all cells in colspan without a specified width
  20621. // cf. http://www.mpdf1.com/forum/discussion/1607#Item_4
  20622. foreach ($list as $k) {
  20623. if (!isset($wc[$k]['w']) || !$wc[$k]['w'])
  20624. $wc[$k]['miw'] += ($wc[$k]['miw'] / $wisa) * $wi;
  20625. } // mPDF 5.7.2
  20626. }
  20627. }
  20628. if ($c['maw'] > $was) {
  20629. if (!$wis) {
  20630. for ($k = $j; $k < $lc; $k++) {
  20631. $wc[$k]['maw'] = $c['maw'] / $c['colspan'];
  20632. }
  20633. } elseif (!count($list)) {
  20634. $wi = $c['maw'] - $was;
  20635. for ($k = $j; $k < $lc; $k++) {
  20636. $wc[$k]['maw'] += ($wc[$k]['maw'] / $was) * $wi;
  20637. }
  20638. } else {
  20639. $wi = $c['maw'] - $was;
  20640. // mPDF 5.7.4 Extra max width distributed evenly to all cells in colspan without a specified width
  20641. // cf. http://www.mpdf1.com/forum/discussion/2221/colspan-bug
  20642. foreach ($list as $k) {
  20643. $wc[$k]['maw'] += $wi / count($list);
  20644. }
  20645. }
  20646. }
  20647. unset($c);
  20648. }
  20649. $checkminwidth = 0;
  20650. $checkmaxwidth = 0;
  20651. $totallength = 0;
  20652. for ($i = 0; $i < $nc; $i++) {
  20653. $checkminwidth += $table['wc'][$i]['miw'];
  20654. $checkmaxwidth += $table['wc'][$i]['maw'];
  20655. $totallength += $table['l'][$i];
  20656. }
  20657. if (!isset($table['w']) && $firstpass) {
  20658. $sumpc = 0;
  20659. $notset = 0;
  20660. for ($i = 0; $i < $nc; $i++) {
  20661. if (isset($table['wc'][$i]['wpercent']) && $table['wc'][$i]['wpercent']) {
  20662. $sumpc += $table['wc'][$i]['wpercent'];
  20663. } else {
  20664. $notset++;
  20665. }
  20666. }
  20667. // If sum of widths as % >= 100% and not all columns are set
  20668. // Set a nominal width of 1% for unset columns
  20669. if ($sumpc >= 100 && $notset) {
  20670. for ($i = 0; $i < $nc; $i++) {
  20671. if ((!isset($table['wc'][$i]['wpercent']) || !$table['wc'][$i]['wpercent']) &&
  20672. (!isset($table['wc'][$i]['w']) || !$table['wc'][$i]['w'])) {
  20673. $table['wc'][$i]['wpercent'] = 1;
  20674. }
  20675. }
  20676. }
  20677. if ($sumpc) { // if any percents are set
  20678. $sumnonpc = (100 - $sumpc);
  20679. $sumpc = max($sumpc, 100);
  20680. $miwleft = 0;
  20681. $miwleftcount = 0;
  20682. $miwsurplusnonpc = 0;
  20683. $maxcalcmiw = 0;
  20684. $mawleft = 0;
  20685. $mawleftcount = 0;
  20686. $mawsurplusnonpc = 0;
  20687. $maxcalcmaw = 0;
  20688. $mawnon = 0;
  20689. $miwnon = 0;
  20690. for ($i = 0; $i < $nc; $i++) {
  20691. if (isset($table['wc'][$i]['wpercent'])) {
  20692. $maxcalcmiw = max($maxcalcmiw, ($table['wc'][$i]['miw'] * $sumpc / $table['wc'][$i]['wpercent']));
  20693. $maxcalcmaw = max($maxcalcmaw, ($table['wc'][$i]['maw'] * $sumpc / $table['wc'][$i]['wpercent']));
  20694. } else {
  20695. $miwleft += $table['wc'][$i]['miw'];
  20696. $mawleft += $table['wc'][$i]['maw'];
  20697. if (!isset($table['wc'][$i]['w'])) {
  20698. $miwleftcount++;
  20699. $mawleftcount++;
  20700. }
  20701. }
  20702. }
  20703. if ($miwleft && $sumnonpc > 0) {
  20704. $miwnon = $miwleft * 100 / $sumnonpc;
  20705. }
  20706. if ($mawleft && $sumnonpc > 0) {
  20707. $mawnon = $mawleft * 100 / $sumnonpc;
  20708. }
  20709. if (($miwnon > $checkminwidth || $maxcalcmiw > $checkminwidth) && $this->keep_table_proportions) {
  20710. if ($miwnon > $maxcalcmiw) {
  20711. $miwsurplusnonpc = round((($miwnon * $sumnonpc / 100) - $miwleft), 3);
  20712. $checkminwidth = $miwnon;
  20713. } else {
  20714. $checkminwidth = $maxcalcmiw;
  20715. }
  20716. for ($i = 0; $i < $nc; $i++) {
  20717. if (isset($table['wc'][$i]['wpercent'])) {
  20718. $newmiw = $checkminwidth * $table['wc'][$i]['wpercent'] / 100;
  20719. if ($table['wc'][$i]['miw'] < $newmiw) {
  20720. $table['wc'][$i]['miw'] = $newmiw;
  20721. }
  20722. $table['wc'][$i]['w'] = 1;
  20723. } elseif ($miwsurplusnonpc && !$table['wc'][$i]['w']) {
  20724. $table['wc'][$i]['miw'] += $miwsurplusnonpc / $miwleftcount;
  20725. }
  20726. }
  20727. }
  20728. if (($mawnon > $checkmaxwidth || $maxcalcmaw > $checkmaxwidth)) {
  20729. if ($mawnon > $maxcalcmaw) {
  20730. $mawsurplusnonpc = round((($mawnon * $sumnonpc / 100) - $mawleft), 3);
  20731. $checkmaxwidth = $mawnon;
  20732. } else {
  20733. $checkmaxwidth = $maxcalcmaw;
  20734. }
  20735. for ($i = 0; $i < $nc; $i++) {
  20736. if (isset($table['wc'][$i]['wpercent'])) {
  20737. $newmaw = $checkmaxwidth * $table['wc'][$i]['wpercent'] / 100;
  20738. if ($table['wc'][$i]['maw'] < $newmaw) {
  20739. $table['wc'][$i]['maw'] = $newmaw;
  20740. }
  20741. $table['wc'][$i]['w'] = 1;
  20742. } elseif ($mawsurplusnonpc && !$table['wc'][$i]['w']) {
  20743. $table['wc'][$i]['maw'] += $mawsurplusnonpc / $mawleftcount;
  20744. }
  20745. if ($table['wc'][$i]['maw'] < $table['wc'][$i]['miw']) {
  20746. $table['wc'][$i]['maw'] = $table['wc'][$i]['miw'];
  20747. }
  20748. }
  20749. }
  20750. if ($checkminwidth > $checkmaxwidth) {
  20751. $checkmaxwidth = $checkminwidth;
  20752. }
  20753. }
  20754. }
  20755. if (isset($table['wpercent']) && $table['wpercent']) {
  20756. $checkminwidth *= (100 / $table['wpercent']);
  20757. $checkmaxwidth *= (100 / $table['wpercent']);
  20758. }
  20759. $checkminwidth += $tblbw;
  20760. $checkmaxwidth += $tblbw;
  20761. // Table['miw'] set by percent in first pass may be larger than sum of column miw
  20762. if ((isset($table['miw']) && $checkminwidth > $table['miw']) || !isset($table['miw'])) {
  20763. $table['miw'] = $checkminwidth;
  20764. }
  20765. if ((isset($table['maw']) && $checkmaxwidth > $table['maw']) || !isset($table['maw'])) {
  20766. $table['maw'] = $checkmaxwidth;
  20767. }
  20768. $table['tl'] = $totallength;
  20769. // mPDF 6
  20770. if ($this->table_rotate) {
  20771. $mxw = $this->tbrot_maxw;
  20772. } else {
  20773. $mxw = $this->blk[$this->blklvl]['inner_width'];
  20774. }
  20775. if (!isset($table['overflow'])) {
  20776. $table['overflow'] = null;
  20777. }
  20778. if ($table['overflow'] == 'visible') {
  20779. return array(0, 0);
  20780. } elseif ($table['overflow'] == 'hidden' && !$this->table_rotate && !$this->ColActive && $checkminwidth > $mxw) {
  20781. $table['w'] = $table['miw'];
  20782. return array(0, 0);
  20783. }
  20784. //elseif ($table['overflow']=='wrap') { return array(0,0); } // mPDF 6
  20785. if (isset($table['w']) && $table['w']) {
  20786. if ($table['w'] >= $checkminwidth && $table['w'] <= $mxw) {
  20787. $table['maw'] = $mxw = $table['w'];
  20788. } elseif ($table['w'] >= $checkminwidth && $table['w'] > $mxw && $this->keep_table_proportions) {
  20789. $checkminwidth = $table['w'];
  20790. } elseif ($table['w'] < $checkminwidth && $checkminwidth < $mxw && $this->keep_table_proportions) {
  20791. $table['maw'] = $table['w'] = $checkminwidth;
  20792. } // mPDF 5.7.4
  20793. else {
  20794. unset($table['w']);
  20795. }
  20796. }
  20797. $ratio = $checkminwidth / $mxw;
  20798. if ($checkminwidth > $mxw) {
  20799. return array(($ratio + 0.001), $checkminwidth);
  20800. } // 0.001 to allow for rounded numbers when resizing
  20801. unset($cs);
  20802. return array(0, 0);
  20803. }
  20804. function _tableWidth(&$table)
  20805. {
  20806. $widthcols = &$table['wc'];
  20807. $numcols = $table['nc'];
  20808. $tablewidth = 0;
  20809. if ($table['borders_separate']) {
  20810. $tblbw = $table['border_details']['L']['w'] + $table['border_details']['R']['w'] + $table['margin']['L'] + $table['margin']['R'] + $table['padding']['L'] + $table['padding']['R'] + $table['border_spacing_H'];
  20811. } else {
  20812. $tblbw = $table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2 + $table['margin']['L'] + $table['margin']['R'];
  20813. }
  20814. if ($table['level'] > 1 && isset($table['w'])) {
  20815. if (isset($table['wpercent']) && $table['wpercent']) {
  20816. $table['w'] = $temppgwidth = (($table['w'] - $tblbw) * $table['wpercent'] / 100) + $tblbw;
  20817. } else {
  20818. $temppgwidth = $table['w'];
  20819. }
  20820. } elseif ($this->table_rotate) {
  20821. $temppgwidth = $this->tbrot_maxw;
  20822. // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin)
  20823. // then allow for this
  20824. $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'];
  20825. if ($enddiv / $temppgwidth < 0.05) {
  20826. $temppgwidth -= $enddiv;
  20827. }
  20828. } else {
  20829. if (isset($table['w']) && $table['w'] < $this->blk[$this->blklvl]['inner_width']) {
  20830. $notfullwidth = 1;
  20831. $temppgwidth = $table['w'];
  20832. } elseif ($table['overflow'] == 'visible' && $table['level'] == 1) {
  20833. $temppgwidth = null;
  20834. } elseif ($table['overflow'] == 'hidden' && !$this->ColActive && isset($table['w']) && $table['w'] > $this->blk[$this->blklvl]['inner_width'] && $table['w'] == $table['miw']) {
  20835. //$temppgwidth = $this->blk[$this->blklvl]['inner_width'];
  20836. $temppgwidth = $table['w'];
  20837. } else {
  20838. $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
  20839. }
  20840. }
  20841. $totaltextlength = 0; // Added - to sum $table['l'][colno]
  20842. $totalatextlength = 0; // Added - to sum $table['l'][colno] for those columns where width not set
  20843. $percentages_set = 0;
  20844. for ($i = 0; $i < $numcols; $i++) {
  20845. if (isset($widthcols[$i]['wpercent'])) {
  20846. $tablewidth += $widthcols[$i]['maw'];
  20847. $percentages_set = 1;
  20848. } elseif (isset($widthcols[$i]['w'])) {
  20849. $tablewidth += $widthcols[$i]['miw'];
  20850. } else {
  20851. $tablewidth += $widthcols[$i]['maw'];
  20852. }
  20853. $totaltextlength += $table['l'][$i];
  20854. }
  20855. if (!$totaltextlength) {
  20856. $totaltextlength = 1;
  20857. }
  20858. $tablewidth += $tblbw; // Outer half of table borders
  20859. if ($tablewidth > $temppgwidth) {
  20860. $table['w'] = $temppgwidth;
  20861. }
  20862. // if any widths set as percentages and max width fits < page width
  20863. elseif ($tablewidth < $temppgwidth && !isset($table['w']) && $percentages_set) {
  20864. $table['w'] = $table['maw'];
  20865. }
  20866. // if table width is set and is > allowed width
  20867. if (isset($table['w']) && $table['w'] > $temppgwidth) {
  20868. $table['w'] = $temppgwidth;
  20869. }
  20870. // IF the table width is now set - Need to distribute columns widths
  20871. // mPDF 5.7.3
  20872. // If the table width is already set to the maximum width (e.g. nested table), then use maximum column widths exactly
  20873. if (isset($table['w']) && ($table['w'] == $tablewidth) && !$percentages_set) {
  20874. // This sets the columns all to maximum width
  20875. for ($i = 0; $i < $numcols; $i++) {
  20876. $widthcols[$i] = $widthcols[$i]['maw'];
  20877. }
  20878. }
  20879. // elseif the table width is set distribute width using algorithm
  20880. elseif (isset($table['w'])) {
  20881. $wis = $wisa = 0;
  20882. $list = array();
  20883. $notsetlist = array();
  20884. for ($i = 0; $i < $numcols; $i++) {
  20885. $wis += $widthcols[$i]['miw'];
  20886. if (!isset($widthcols[$i]['w']) || ($widthcols[$i]['w'] && $table['w'] > $temppgwidth && !$this->keep_table_proportions && !$notfullwidth )) {
  20887. $list[] = $i;
  20888. $wisa += $widthcols[$i]['miw'];
  20889. $totalatextlength += $table['l'][$i];
  20890. }
  20891. }
  20892. if (!$totalatextlength) {
  20893. $totalatextlength = 1;
  20894. }
  20895. // Allocate spare (more than col's minimum width) across the cols according to their approx total text length
  20896. // Do it by setting minimum width here
  20897. if ($table['w'] > $wis + $tblbw) {
  20898. // First set any cell widths set as percentages
  20899. if ($table['w'] < $temppgwidth || $this->keep_table_proportions) {
  20900. for ($k = 0; $k < $numcols; $k++) {
  20901. if (isset($widthcols[$k]['wpercent'])) {
  20902. $curr = $widthcols[$k]['miw'];
  20903. $widthcols[$k]['miw'] = ($table['w'] - $tblbw) * $widthcols[$k]['wpercent'] / 100;
  20904. $wis += $widthcols[$k]['miw'] - $curr;
  20905. $wisa += $widthcols[$k]['miw'] - $curr;
  20906. }
  20907. }
  20908. }
  20909. // Now allocate surplus up to maximum width of each column
  20910. $surplus = 0;
  20911. $ttl = 0; // number of surplus columns
  20912. if (!count($list)) {
  20913. $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
  20914. for ($k = 0; $k < $numcols; $k++) {
  20915. $spareratio = ($table['l'][$k] / $totaltextlength); // gives ratio to divide up free space
  20916. // Don't allocate more than Maximum required width - save rest in surplus
  20917. if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
  20918. $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
  20919. $widthcols[$k]['miw'] = $widthcols[$k]['maw'];
  20920. } else {
  20921. $notsetlist[] = $k;
  20922. $ttl += $table['l'][$k];
  20923. $widthcols[$k]['miw'] += ($wi * $spareratio);
  20924. }
  20925. }
  20926. } else {
  20927. $wi = ($table['w'] - ($wis + $tblbw)); // i.e. extra space to distribute
  20928. foreach ($list as $k) {
  20929. $spareratio = ($table['l'][$k] / $totalatextlength); // gives ratio to divide up free space
  20930. // Don't allocate more than Maximum required width - save rest in surplus
  20931. if ($widthcols[$k]['miw'] + ($wi * $spareratio) >= $widthcols[$k]['maw']) { // mPDF 5.7.3
  20932. $surplus += ($wi * $spareratio) - ($widthcols[$k]['maw'] - $widthcols[$k]['miw']);
  20933. $widthcols[$k]['miw'] = $widthcols[$k]['maw'];
  20934. } else {
  20935. $notsetlist[] = $k;
  20936. $ttl += $table['l'][$k];
  20937. $widthcols[$k]['miw'] += ($wi * $spareratio);
  20938. }
  20939. }
  20940. }
  20941. // If surplus still left over apportion it across columns
  20942. if ($surplus) {
  20943. // if some are set only add to remaining - otherwise add to all of them
  20944. if (count($notsetlist) && count($notsetlist) < $numcols) {
  20945. foreach ($notsetlist AS $i) {
  20946. if ($ttl)
  20947. $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
  20948. }
  20949. }
  20950. // If some widths are defined, and others have been added up to their maxmum
  20951. elseif (count($list) && count($list) < $numcols) {
  20952. foreach ($list AS $i) {
  20953. $widthcols[$i]['miw'] += $surplus / count($list);
  20954. }
  20955. } elseif ($numcols) { // If all columns
  20956. $ttl = array_sum($table['l']);
  20957. for ($i = 0; $i < $numcols; $i++) {
  20958. $widthcols[$i]['miw'] += $surplus * $table['l'][$i] / $ttl;
  20959. }
  20960. }
  20961. }
  20962. }
  20963. // This sets the columns all to minimum width (which has been increased above if appropriate)
  20964. for ($i = 0; $i < $numcols; $i++) {
  20965. $widthcols[$i] = $widthcols[$i]['miw'];
  20966. }
  20967. // TABLE NOT WIDE ENOUGH EVEN FOR MINIMUM CONTENT WIDTH
  20968. // If sum of column widths set are too wide for table
  20969. $checktablewidth = 0;
  20970. for ($i = 0; $i < $numcols; $i++) {
  20971. $checktablewidth += $widthcols[$i];
  20972. }
  20973. if ($checktablewidth > ($temppgwidth + 0.001 - $tblbw)) {
  20974. $usedup = 0;
  20975. $numleft = 0;
  20976. for ($i = 0; $i < $numcols; $i++) {
  20977. if ((isset($widthcols[$i]) && $widthcols[$i] > (($temppgwidth - $tblbw) / $numcols)) && (!isset($widthcols[$i]['w']))) {
  20978. $numleft++;
  20979. unset($widthcols[$i]);
  20980. } else {
  20981. $usedup += $widthcols[$i];
  20982. }
  20983. }
  20984. for ($i = 0; $i < $numcols; $i++) {
  20985. if (!isset($widthcols[$i]) || !$widthcols[$i]) {
  20986. $widthcols[$i] = ((($temppgwidth - $tblbw) - $usedup) / ($numleft));
  20987. }
  20988. }
  20989. }
  20990. } else { //table has no width defined
  20991. $table['w'] = $tablewidth;
  20992. for ($i = 0; $i < $numcols; $i++) {
  20993. if (isset($widthcols[$i]['wpercent']) && $this->keep_table_proportions) {
  20994. $colwidth = $widthcols[$i]['maw'];
  20995. } elseif (isset($widthcols[$i]['w'])) {
  20996. $colwidth = $widthcols[$i]['miw'];
  20997. } else {
  20998. $colwidth = $widthcols[$i]['maw'];
  20999. }
  21000. unset($widthcols[$i]);
  21001. $widthcols[$i] = $colwidth;
  21002. }
  21003. }
  21004. if ($table['overflow'] == 'visible' && $table['level'] == 1) {
  21005. if ($tablewidth > $this->blk[$this->blklvl]['inner_width']) {
  21006. for ($j = 0; $j < $numcols; $j++) { //columns
  21007. for ($i = 0; $i < $table['nr']; $i++) { //rows
  21008. if (isset($table['cells'][$i][$j]) && $table['cells'][$i][$j]) {
  21009. $colspan = (isset($table['cells'][$i][$j]['colspan']) ? $table['cells'][$i][$j]['colspan'] : 1);
  21010. if ($colspan > 1) {
  21011. $w = 0;
  21012. for ($c = $j; $c < ($j + $colspan); $c++) {
  21013. $w += $widthcols[$c];
  21014. }
  21015. if ($w > $this->blk[$this->blklvl]['inner_width']) {
  21016. $diff = $w - ($this->blk[$this->blklvl]['inner_width'] - $tblbw);
  21017. for ($c = $j; $c < ($j + $colspan); $c++) {
  21018. $widthcols[$c] -= $diff * ($widthcols[$c] / $w);
  21019. }
  21020. $table['w'] -= $diff;
  21021. $table['csp'][$j] = $w - $diff;
  21022. }
  21023. }
  21024. }
  21025. }
  21026. }
  21027. }
  21028. $pgNo = 0;
  21029. $currWc = 0;
  21030. for ($i = 0; $i < $numcols; $i++) { //columns
  21031. if (isset($table['csp'][$i])) {
  21032. $w = $table['csp'][$i];
  21033. unset($table['csp'][$i]);
  21034. } else {
  21035. $w = $widthcols[$i];
  21036. }
  21037. if (($currWc + $w + $tblbw) > $this->blk[$this->blklvl]['inner_width']) {
  21038. $pgNo++;
  21039. $currWc = $widthcols[$i];
  21040. } else {
  21041. $currWc += $widthcols[$i];
  21042. }
  21043. $table['colPg'][$i] = $pgNo;
  21044. }
  21045. }
  21046. }
  21047. function _tableHeight(&$table)
  21048. {
  21049. $level = $table['level'];
  21050. $levelid = $table['levelid'];
  21051. $cells = &$table['cells'];
  21052. $numcols = $table['nc'];
  21053. $numrows = $table['nr'];
  21054. $listspan = array();
  21055. $checkmaxheight = 0;
  21056. $headerrowheight = 0;
  21057. $checkmaxheightplus = 0;
  21058. $headerrowheightplus = 0;
  21059. $firstrowheight = 0;
  21060. $footerrowheight = 0;
  21061. $footerrowheightplus = 0;
  21062. if ($this->table_rotate) {
  21063. $temppgheight = $this->tbrot_maxh;
  21064. $remainingpage = $this->tbrot_maxh;
  21065. } else {
  21066. $temppgheight = ($this->h - $this->bMargin - $this->tMargin) - $this->kwt_height;
  21067. $remainingpage = ($this->h - $this->bMargin - $this->y) - $this->kwt_height;
  21068. // If it is less than 1/20th of the remaining page height to finish the DIV (i.e. DIV padding + table bottom margin)
  21069. // then allow for this
  21070. $enddiv = $this->blk[$this->blklvl]['padding_bottom'] + $this->blk[$this->blklvl]['border_bottom']['w'] + $table['margin']['B'];
  21071. if ($remainingpage > $enddiv && $enddiv / $remainingpage < 0.05) {
  21072. $remainingpage -= $enddiv;
  21073. } elseif ($remainingpage == 0) {
  21074. $remainingpage = 0.001;
  21075. }
  21076. if ($temppgheight > $enddiv && $enddiv / $temppgheight < 0.05) {
  21077. $temppgheight -= $enddiv;
  21078. } elseif ($temppgheight == 0) {
  21079. $temppgheight = 0.001;
  21080. }
  21081. }
  21082. if ($remainingpage < 0) {
  21083. $remainingpage = 0.001;
  21084. }
  21085. if ($temppgheight < 0) {
  21086. $temppgheight = 0.001;
  21087. }
  21088. for ($i = 0; $i < $numrows; $i++) { //rows
  21089. $heightrow = &$table['hr'][$i];
  21090. for ($j = 0; $j < $numcols; $j++) { //columns
  21091. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  21092. $c = &$cells[$i][$j];
  21093. if ($this->simpleTables) {
  21094. if ($table['borders_separate']) { // NB twice border width
  21095. $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) + ($c['padding']['L'] + $c['padding']['R']) + $table['border_spacing_H'];
  21096. $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) + ($c['padding']['T'] + $c['padding']['B']) + $table['border_spacing_V'];
  21097. } else {
  21098. $extraWLR = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + ($c['padding']['L'] + $c['padding']['R']);
  21099. $extrh = ($table['simple']['border_details']['T']['w'] + $table['simple']['border_details']['B']['w']) / 2 + ($c['padding']['T'] + $c['padding']['B']);
  21100. }
  21101. } else {
  21102. if ($this->packTableData) {
  21103. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
  21104. } else {
  21105. $bt = $c['border_details']['T']['w'];
  21106. $bb = $c['border_details']['B']['w'];
  21107. $br = $c['border_details']['R']['w'];
  21108. $bl = $c['border_details']['L']['w'];
  21109. }
  21110. if ($table['borders_separate']) { // NB twice border width
  21111. $extraWLR = $bl + $br + $c['padding']['L'] + $c['padding']['R'] + $table['border_spacing_H'];
  21112. $extrh = $bt + $bb + $c['padding']['T'] + $c['padding']['B'] + $table['border_spacing_V'];
  21113. } else {
  21114. $extraWLR = $bl / 2 + $br / 2 + $c['padding']['L'] + $c['padding']['R'];
  21115. $extrh = $bt / 2 + $bb / 2 + $c['padding']['T'] + $c['padding']['B'];
  21116. }
  21117. }
  21118. if ($table['overflow'] == 'visible' && $level == 1)
  21119. list($x, $cw) = $this->_splitTableGetWidth($table, $i, $j);
  21120. else
  21121. list($x, $cw) = $this->_tableGetWidth($table, $i, $j);
  21122. // Get CELL HEIGHT
  21123. // ++ extra parameter forces wrap to break word
  21124. if ($c['R'] && isset($c['textbuffer'])) {
  21125. $str = '';
  21126. foreach ($c['textbuffer'] AS $t) {
  21127. $str .= $t[0] . ' ';
  21128. }
  21129. $str = rtrim($str);
  21130. $s_fs = $this->FontSizePt;
  21131. $s_f = $this->FontFamily;
  21132. $s_st = $this->FontStyle;
  21133. $this->SetFont($c['textbuffer'][0][4], $c['textbuffer'][0][2], $c['textbuffer'][0][11] / $this->shrin_k, true, true);
  21134. $tempch = $this->GetStringWidth($str, true, $c['textbuffer'][0][18], $c['textbuffer'][0][8]);
  21135. if ($c['R'] >= 45 && $c['R'] < 90) {
  21136. $tempch = ((sin(deg2rad($c['R']))) * $tempch ) + ((sin(deg2rad($c['R']))) * (($c['textbuffer'][0][11] / _MPDFK) / $this->shrin_k));
  21137. }
  21138. $this->SetFont($s_f, $s_st, $s_fs, true, true);
  21139. $ch = ($tempch ) + $extrh;
  21140. } else {
  21141. if (isset($c['textbuffer']) && !empty($c['textbuffer'])) {
  21142. $this->cellLineHeight = $c['cellLineHeight'];
  21143. $this->cellLineStackingStrategy = $c['cellLineStackingStrategy'];
  21144. $this->cellLineStackingShift = $c['cellLineStackingShift'];
  21145. $this->divwidth = $cw - $extraWLR;
  21146. $tempch = $this->printbuffer($c['textbuffer'], '', true, true);
  21147. } else {
  21148. $tempch = 0;
  21149. }
  21150. // Added cellpadding top and bottom. (Lineheight already adjusted)
  21151. $ch = $tempch + $extrh;
  21152. }
  21153. //If height is defined and it is bigger than calculated $ch then update values
  21154. if (isset($c['h']) && $c['h'] > $ch) {
  21155. $c['mih'] = $ch; //in order to keep valign working
  21156. $ch = $c['h'];
  21157. } else
  21158. $c['mih'] = $ch;
  21159. if (isset($c['rowspan']))
  21160. $listspan[] = array($i, $j);
  21161. elseif ($heightrow < $ch)
  21162. $heightrow = $ch;
  21163. // this is the extra used in _tableWrite to determine whether to trigger a page change
  21164. if ($table['borders_separate']) {
  21165. if ($i == ($numrows - 1) || (isset($c['rowspan']) && ($i + $c['rowspan']) == ($numrows))) {
  21166. $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  21167. } else {
  21168. $extra = $table['border_spacing_V'] / 2;
  21169. }
  21170. } else {
  21171. if (!$this->simpleTables) {
  21172. $extra = $bb / 2;
  21173. } elseif ($this->simpleTables) {
  21174. $extra = $table['simple']['border_details']['B']['w'] / 2;
  21175. }
  21176. }
  21177. if (isset($table['is_thead'][$i]) && $table['is_thead'][$i]) {
  21178. if ($j == 0) {
  21179. $headerrowheight += $ch;
  21180. $headerrowheightplus += $ch + $extra;
  21181. }
  21182. } elseif (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
  21183. if ($j == 0) {
  21184. $footerrowheight += $ch;
  21185. $footerrowheightplus += $ch + $extra;
  21186. }
  21187. } else {
  21188. $checkmaxheight = max($checkmaxheight, $ch);
  21189. $checkmaxheightplus = max($checkmaxheightplus, $ch + $extra);
  21190. }
  21191. if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
  21192. $firstrowheight = max($ch, $firstrowheight);
  21193. }
  21194. unset($c);
  21195. }
  21196. }//end of columns
  21197. }//end of rows
  21198. $heightrow = &$table['hr'];
  21199. foreach ($listspan as $span) {
  21200. list($i, $j) = $span;
  21201. $c = &$cells[$i][$j];
  21202. $lr = $i + $c['rowspan'];
  21203. if ($lr > $numrows)
  21204. $lr = $numrows;
  21205. $hs = $hsa = 0;
  21206. $list = array();
  21207. for ($k = $i; $k < $lr; $k++) {
  21208. $hs += $heightrow[$k];
  21209. // mPDF 6
  21210. $sh = false; // specified height
  21211. for ($m = 0; $m < $numcols; $m++) { //columns
  21212. $tc = &$cells[$k][$m];
  21213. if (isset($tc['rowspan'])) {
  21214. continue;
  21215. }
  21216. if (isset($tc['h'])) {
  21217. $sh = true;
  21218. break;
  21219. }
  21220. }
  21221. if (!$sh) {
  21222. $list[] = $k;
  21223. }
  21224. }
  21225. if ($table['borders_separate']) {
  21226. if ($i == ($numrows - 1) || ($i + $c['rowspan']) == ($numrows)) {
  21227. $extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  21228. } else {
  21229. $extra = $table['border_spacing_V'] / 2;
  21230. }
  21231. } else {
  21232. if (!$this->simpleTables) {
  21233. if ($this->packTableData) {
  21234. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($c['borderbin']);
  21235. } else {
  21236. $bb = $c['border_details']['B']['w'];
  21237. }
  21238. $extra = $bb / 2;
  21239. } elseif ($this->simpleTables) {
  21240. $extra = $table['simple']['border_details']['B']['w'] / 2;
  21241. }
  21242. }
  21243. if (!empty($table['is_thead'][$i])) {
  21244. $headerrowheight = max($headerrowheight, $hs);
  21245. $headerrowheightplus = max($headerrowheightplus, $hs + $extra);
  21246. } elseif (!empty($table['is_tfoot'][$i])) {
  21247. $footerrowheight = max($footerrowheight, $hs);
  21248. $footerrowheightplus = max($footerrowheightplus, $hs + $extra);
  21249. } else {
  21250. $checkmaxheight = max($checkmaxheight, $hs);
  21251. $checkmaxheightplus = max($checkmaxheightplus, $hs + $extra);
  21252. }
  21253. if ($this->tableLevel == 1 && $i == (isset($table['headernrows']) ? $table['headernrows'] : 0)) {
  21254. $firstrowheight = max($hs, $firstrowheight);
  21255. }
  21256. if ($c['mih'] > $hs) {
  21257. if (!$hs) {
  21258. for ($k = $i; $k < $lr; $k++)
  21259. $heightrow[$k] = $c['mih'] / $c['rowspan'];
  21260. } elseif (!count($list)) { // no rows in the rowspan have a height specified, so share amongst all rows equally
  21261. $hi = $c['mih'] - $hs;
  21262. for ($k = $i; $k < $lr; $k++)
  21263. $heightrow[$k] += ($heightrow[$k] / $hs) * $hi;
  21264. } else {
  21265. $hi = $c['mih'] - $hs; // mPDF 6
  21266. foreach ($list as $k)
  21267. $heightrow[$k] += $hi / (count($list)); // mPDF 6
  21268. }
  21269. }
  21270. unset($c);
  21271. // If rowspans overlap so that one or more rows do not have a height set...
  21272. // i.e. for one or more rows, the only cells (explicit) in that row have rowspan>1
  21273. // so heightrow is still == 0
  21274. if ($heightrow[$i] == 0) {
  21275. // Get row extent to analyse above and below
  21276. $top = $i;
  21277. foreach ($listspan as $checkspan) {
  21278. list($cki, $ckj) = $checkspan;
  21279. $c = &$cells[$cki][$ckj];
  21280. if (isset($c['rowspan']) && $c['rowspan'] > 1) {
  21281. if (($cki + $c['rowspan'] - 1) >= $i) {
  21282. $top = min($top, $cki);
  21283. }
  21284. }
  21285. }
  21286. $bottom = $i + $c['rowspan'] - 1;
  21287. // Check for overconstrained conditions
  21288. for ($k = $top; $k <= $bottom; $k++) {
  21289. // if ['hr'] for any of the others is also 0, then abort (too complicated)
  21290. if ($k != $i && $heightrow[$k] == 0) {
  21291. break(1);
  21292. }
  21293. // check again that top and bottom are not crossed by rowspans - or abort (too complicated)
  21294. if ($k == $top) {
  21295. // ???? take account of colspan as well???
  21296. for ($m = 0; $m < $numcols; $m++) { //columns
  21297. if (!isset($cells[$k][$m]) || $cells[$k][$m] == 0) {
  21298. break(2);
  21299. }
  21300. }
  21301. } elseif ($k == $bottom) {
  21302. // ???? take account of colspan as well???
  21303. for ($m = 0; $m < $numcols; $m++) { //columns
  21304. $c = &$cells[$k][$m];
  21305. if (isset($c['rowspan']) && $c['rowspan'] > 1) {
  21306. break(2);
  21307. }
  21308. }
  21309. }
  21310. }
  21311. // By columns add up col height using ['h'] if set or ['mih'] if not
  21312. // Intentionally do not substract border-spacing
  21313. $colH = array();
  21314. $extH = 0;
  21315. $newhr = array();
  21316. for ($m = 0; $m < $numcols; $m++) { //columns
  21317. for ($k = $top; $k <= $bottom; $k++) {
  21318. if (isset($cells[$k][$m]) && $cells[$k][$m] != 0) {
  21319. $c = &$cells[$k][$m];
  21320. if (isset($c['h']) && $c['h']) {
  21321. $useh = $c['h'];
  21322. }
  21323. // ???? take account of colspan as well???
  21324. else {
  21325. $useh = $c['mih'];
  21326. }
  21327. if (isset($colH[$m])) {
  21328. $colH[$m] += $useh;
  21329. } else {
  21330. $colH[$m] = $useh;
  21331. }
  21332. if (!isset($c['rowspan']) || $c['rowspan'] < 2) {
  21333. $newhr[$k] = max((isset($newhr[$k]) ? $newhr[$k] : 0), $useh);
  21334. }
  21335. }
  21336. }
  21337. $extH = max($extH, $colH[$m]); // mPDF 6
  21338. }
  21339. $newhr[$i] = $extH - array_sum($newhr);
  21340. for ($k = $top; $k <= $bottom; $k++) {
  21341. $heightrow[$k] = $newhr[$k];
  21342. }
  21343. }
  21344. unset($c);
  21345. }
  21346. $table['h'] = array_sum($heightrow);
  21347. unset($heightrow);
  21348. if ($table['borders_separate']) {
  21349. $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['border_details']['T']['w'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] + $table['padding']['T'] + $table['padding']['B'];
  21350. } else {
  21351. $table['h'] += $table['margin']['T'] + $table['margin']['B'] + $table['max_cell_border_width']['T'] / 2 + $table['max_cell_border_width']['B'] / 2;
  21352. }
  21353. $maxrowheight = $checkmaxheightplus + $headerrowheightplus + $footerrowheightplus;
  21354. $maxfirstrowheight = $firstrowheight + $headerrowheightplus + $footerrowheightplus; // includes thead, 1st row and tfoot
  21355. return array($table['h'], $maxrowheight, $temppgheight, $remainingpage, $maxfirstrowheight);
  21356. }
  21357. function _tableGetWidth(&$table, $i, $j)
  21358. {
  21359. $cell = &$table['cells'][$i][$j];
  21360. if ($cell) {
  21361. if (isset($cell['x0'])) {
  21362. return array($cell['x0'], $cell['w0']);
  21363. }
  21364. $x = 0;
  21365. $widthcols = &$table['wc'];
  21366. for ($k = 0; $k < $j; $k++)
  21367. $x += $widthcols[$k];
  21368. $w = $widthcols[$j];
  21369. if (isset($cell['colspan'])) {
  21370. for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--)
  21371. $w += $widthcols[$k];
  21372. }
  21373. $cell['x0'] = $x;
  21374. $cell['w0'] = $w;
  21375. return array($x, $w);
  21376. }
  21377. return array(0, 0);
  21378. }
  21379. function _splitTableGetWidth(&$table, $i, $j)
  21380. {
  21381. $cell = &$table['cells'][$i][$j];
  21382. if ($cell) {
  21383. if (isset($cell['x0']))
  21384. return array($cell['x0'], $cell['w0']);
  21385. $x = 0;
  21386. $widthcols = &$table['wc'];
  21387. $pg = $table['colPg'][$j];
  21388. for ($k = 0; $k < $j; $k++) {
  21389. if ($table['colPg'][$k] == $pg)
  21390. $x += $widthcols[$k];
  21391. }
  21392. $w = $widthcols[$j];
  21393. if (isset($cell['colspan'])) {
  21394. for ($k = $j + $cell['colspan'] - 1; $k > $j; $k--)
  21395. if ($table['colPg'][$k] == $pg)
  21396. $w += $widthcols[$k];
  21397. }
  21398. $cell['x0'] = $x;
  21399. $cell['w0'] = $w;
  21400. return array($x, $w);
  21401. }
  21402. return array(0, 0);
  21403. }
  21404. function _tableGetHeight(&$table, $i, $j)
  21405. {
  21406. $cell = &$table['cells'][$i][$j];
  21407. if ($cell) {
  21408. if (isset($cell['y0']))
  21409. return array($cell['y0'], $cell['h0']);
  21410. $y = 0;
  21411. $heightrow = &$table['hr'];
  21412. for ($k = 0; $k < $i; $k++)
  21413. $y += $heightrow[$k];
  21414. $h = $heightrow[$i];
  21415. if (isset($cell['rowspan'])) {
  21416. for ($k = $i + $cell['rowspan'] - 1; $k > $i; $k--)
  21417. $h += $heightrow[$k];
  21418. }
  21419. $cell['y0'] = $y;
  21420. $cell['h0'] = $h;
  21421. return array($y, $h);
  21422. }
  21423. return array(0, 0);
  21424. }
  21425. function _tableGetMaxRowHeight($table, $row)
  21426. {
  21427. if ($row == $table['nc'] - 1) {
  21428. return $table['hr'][$row];
  21429. }
  21430. $maxrowheight = $table['hr'][$row];
  21431. for ($i = $row + 1; $i < $table['nr']; $i++) {
  21432. $cellsset = 0;
  21433. for ($j = 0; $j < $table['nc']; $j++) {
  21434. if ($table['cells'][$i][$j]) {
  21435. if (isset($table['cells'][$i][$j]['colspan'])) {
  21436. $cellsset += $table['cells'][$i][$j]['colspan'];
  21437. } else
  21438. $cellsset += 1;
  21439. }
  21440. }
  21441. if ($cellsset == $table['nc']) {
  21442. return $maxrowheight;
  21443. } else {
  21444. $maxrowheight += $table['hr'][$i];
  21445. }
  21446. }
  21447. return $maxrowheight;
  21448. }
  21449. // CHANGED TO ALLOW TABLE BORDER TO BE SPECIFIED CORRECTLY - added border_details
  21450. function _tableRect($x, $y, $w, $h, $bord = -1, $details = array(), $buffer = false, $bSeparate = false, $cort = 'cell', $tablecorner = '', $bsv = 0, $bsh = 0)
  21451. {
  21452. $cellBorderOverlay = array();
  21453. if ($bord == -1) {
  21454. $this->Rect($x, $y, $w, $h);
  21455. } elseif ($this->simpleTables && ($cort == 'cell')) {
  21456. $this->SetLineWidth($details['L']['w']);
  21457. if ($details['L']['c']) {
  21458. $this->SetDColor($details['L']['c']);
  21459. } else {
  21460. $this->SetDColor($this->ConvertColor(0));
  21461. }
  21462. $this->SetLineJoin(0);
  21463. $this->Rect($x, $y, $w, $h);
  21464. } elseif ($bord) {
  21465. if (!$bSeparate && $buffer) {
  21466. $priority = 'LRTB';
  21467. for ($p = 0; $p < strlen($priority); $p++) {
  21468. $side = $priority[$p];
  21469. $details['p'] = $side;
  21470. $dom = 0;
  21471. if (isset($details[$side]['w'])) {
  21472. $dom += ($details[$side]['w'] * 100000);
  21473. }
  21474. if (isset($details[$side]['style'])) {
  21475. $dom += (array_search($details[$side]['style'], $this->borderstyles) * 100);
  21476. }
  21477. if (isset($details[$side]['dom'])) {
  21478. $dom += ($details[$side]['dom'] * 10);
  21479. }
  21480. // Precedence to darker colours at joins
  21481. $coldom = 0;
  21482. if (isset($details[$side]['c']) && is_array($details[$side]['c'])) {
  21483. if ($details[$side]['c']{0} == 3) { // RGB
  21484. $coldom = 10 - (((ord($details[$side]['c']{1}) * 1.00) + (ord($details[$side]['c']{2}) * 1.00) + (ord($details[$side]['c']{3}) * 1.00)) / 76.5);
  21485. }
  21486. } // 10 black - 0 white
  21487. if ($coldom) {
  21488. $dom += $coldom;
  21489. }
  21490. // Lastly precedence to RIGHT and BOTTOM cells at joins
  21491. if (isset($details['cellposdom'])) {
  21492. $dom += $details['cellposdom'];
  21493. }
  21494. $save = false;
  21495. if ($side == 'T' && $this->issetBorder($bord, _BORDER_TOP)) {
  21496. $cbord = _BORDER_TOP;
  21497. $save = true;
  21498. } elseif ($side == 'L' && $this->issetBorder($bord, _BORDER_LEFT)) {
  21499. $cbord = _BORDER_LEFT;
  21500. $save = true;
  21501. } elseif ($side == 'R' && $this->issetBorder($bord, _BORDER_RIGHT)) {
  21502. $cbord = _BORDER_RIGHT;
  21503. $save = true;
  21504. } elseif ($side == 'B' && $this->issetBorder($bord, _BORDER_BOTTOM)) {
  21505. $cbord = _BORDER_BOTTOM;
  21506. $save = true;
  21507. }
  21508. if ($save) {
  21509. $this->cellBorderBuffer[] = pack("A16nCnda6A10d14", str_pad(sprintf("%08.7f", $dom), 16, "0", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 0
  21510. );
  21511. if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset' || $details[$side]['style'] == 'double') {
  21512. $details[$side]['overlay'] = true;
  21513. $this->cellBorderBuffer[] = pack("A16nCnda6A10d14", str_pad(sprintf("%08.7f", ($dom + 4)), 16, "0", STR_PAD_LEFT), $cbord, ord($side), $details[$side]['s'], $details[$side]['w'], $details[$side]['c'], $details[$side]['style'], $x, $y, $w, $h, $details['mbw']['BL'], $details['mbw']['BR'], $details['mbw']['RT'], $details['mbw']['RB'], $details['mbw']['TL'], $details['mbw']['TR'], $details['mbw']['LT'], $details['mbw']['LB'], $details['cellposdom'], 1
  21514. );
  21515. }
  21516. }
  21517. }
  21518. return;
  21519. }
  21520. if (isset($details['p']) && strlen($details['p']) > 1) {
  21521. $priority = $details['p'];
  21522. } else {
  21523. $priority = 'LTRB';
  21524. }
  21525. $Tw = 0;
  21526. $Rw = 0;
  21527. $Bw = 0;
  21528. $Lw = 0;
  21529. if (isset($details['T']['w'])) {
  21530. $Tw = $details['T']['w'];
  21531. }
  21532. if (isset($details['R']['w'])) {
  21533. $Rw = $details['R']['w'];
  21534. }
  21535. if (isset($details['B']['w'])) {
  21536. $Bw = $details['B']['w'];
  21537. }
  21538. if (isset($details['L']['w'])) {
  21539. $Lw = $details['L']['w'];
  21540. }
  21541. $x2 = $x + $w;
  21542. $y2 = $y + $h;
  21543. $oldlinewidth = $this->LineWidth;
  21544. for ($p = 0; $p < strlen($priority); $p++) {
  21545. $side = $priority[$p];
  21546. $xadj = 0;
  21547. $xadj2 = 0;
  21548. $yadj = 0;
  21549. $yadj2 = 0;
  21550. $print = false;
  21551. if ($Tw && $side == 'T' && $this->issetBorder($bord, _BORDER_TOP)) { // TOP
  21552. $ly1 = $y;
  21553. $ly2 = $y;
  21554. $lx1 = $x;
  21555. $lx2 = $x2;
  21556. $this->SetLineWidth($Tw);
  21557. if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
  21558. if ($Tw > $Lw)
  21559. $xadj = ($Tw - $Lw) / 2;
  21560. if ($Tw < $Lw)
  21561. $xadj = ($Tw + $Lw) / 2;
  21562. }
  21563. else {
  21564. $xadj = $Tw / 2 - $bsh / 2;
  21565. }
  21566. if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
  21567. if ($Tw > $Rw)
  21568. $xadj2 = ($Tw - $Rw) / 2;
  21569. if ($Tw < $Rw)
  21570. $xadj2 = ($Tw + $Rw) / 2;
  21571. }
  21572. else {
  21573. $xadj2 = $Tw / 2 - $bsh / 2;
  21574. }
  21575. if (!$bSeparate && $details['mbw']['TL']) {
  21576. $xadj = ($Tw - $details['mbw']['TL']) / 2;
  21577. }
  21578. if (!$bSeparate && $details['mbw']['TR']) {
  21579. $xadj2 = ($Tw - $details['mbw']['TR']) / 2;
  21580. }
  21581. $print = true;
  21582. }
  21583. if ($Lw && $side == 'L' && $this->issetBorder($bord, _BORDER_LEFT)) { // LEFT
  21584. $ly1 = $y;
  21585. $ly2 = $y2;
  21586. $lx1 = $x;
  21587. $lx2 = $x;
  21588. $this->SetLineWidth($Lw);
  21589. if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
  21590. if ($Lw > $Tw)
  21591. $yadj = ($Lw - $Tw) / 2;
  21592. if ($Lw < $Tw)
  21593. $yadj = ($Lw + $Tw) / 2;
  21594. }
  21595. else {
  21596. $yadj = $Lw / 2 - $bsv / 2;
  21597. }
  21598. if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
  21599. if ($Lw > $Bw)
  21600. $yadj2 = ($Lw - $Bw) / 2;
  21601. if ($Lw < $Bw)
  21602. $yadj2 = ($Lw + $Bw) / 2;
  21603. }
  21604. else {
  21605. $yadj2 = $Lw / 2 - $bsv / 2;
  21606. }
  21607. if (!$bSeparate && $details['mbw']['LT']) {
  21608. $yadj = ($Lw - $details['mbw']['LT']) / 2;
  21609. }
  21610. if (!$bSeparate && $details['mbw']['LB']) {
  21611. $yadj2 = ($Lw - $details['mbw']['LB']) / 2;
  21612. }
  21613. $print = true;
  21614. }
  21615. if ($Rw && $side == 'R' && $this->issetBorder($bord, _BORDER_RIGHT)) { // RIGHT
  21616. $ly1 = $y;
  21617. $ly2 = $y2;
  21618. $lx1 = $x2;
  21619. $lx2 = $x2;
  21620. $this->SetLineWidth($Rw);
  21621. if ($cort == 'cell' || strpos($tablecorner, 'T') !== false) {
  21622. if ($Rw < $Tw)
  21623. $yadj = ($Rw + $Tw) / 2;
  21624. if ($Rw > $Tw)
  21625. $yadj = ($Rw - $Tw) / 2;
  21626. }
  21627. else {
  21628. $yadj = $Rw / 2 - $bsv / 2;
  21629. }
  21630. if ($cort == 'cell' || strpos($tablecorner, 'B') !== false) {
  21631. if ($Rw > $Bw)
  21632. $yadj2 = ($Rw - $Bw) / 2;
  21633. if ($Rw < $Bw)
  21634. $yadj2 = ($Rw + $Bw) / 2;
  21635. }
  21636. else {
  21637. $yadj2 = $Rw / 2 - $bsv / 2;
  21638. }
  21639. if (!$bSeparate && $details['mbw']['RT']) {
  21640. $yadj = ($Rw - $details['mbw']['RT']) / 2;
  21641. }
  21642. if (!$bSeparate && $details['mbw']['RB']) {
  21643. $yadj2 = ($Rw - $details['mbw']['RB']) / 2;
  21644. }
  21645. $print = true;
  21646. }
  21647. if ($Bw && $side == 'B' && $this->issetBorder($bord, _BORDER_BOTTOM)) { // BOTTOM
  21648. $ly1 = $y2;
  21649. $ly2 = $y2;
  21650. $lx1 = $x;
  21651. $lx2 = $x2;
  21652. $this->SetLineWidth($Bw);
  21653. if ($cort == 'cell' || strpos($tablecorner, 'L') !== false) {
  21654. if ($Bw > $Lw)
  21655. $xadj = ($Bw - $Lw) / 2;
  21656. if ($Bw < $Lw)
  21657. $xadj = ($Bw + $Lw) / 2;
  21658. }
  21659. else {
  21660. $xadj = $Bw / 2 - $bsh / 2;
  21661. }
  21662. if ($cort == 'cell' || strpos($tablecorner, 'R') !== false) {
  21663. if ($Bw > $Rw)
  21664. $xadj2 = ($Bw - $Rw) / 2;
  21665. if ($Bw < $Rw)
  21666. $xadj2 = ($Bw + $Rw) / 2;
  21667. }
  21668. else {
  21669. $xadj2 = $Bw / 2 - $bsh / 2;
  21670. }
  21671. if (!$bSeparate && $details['mbw']['BL']) {
  21672. $xadj = ($Bw - $details['mbw']['BL']) / 2;
  21673. }
  21674. if (!$bSeparate && $details['mbw']['BR']) {
  21675. $xadj2 = ($Bw - $details['mbw']['BR']) / 2;
  21676. }
  21677. $print = true;
  21678. }
  21679. // Now draw line
  21680. if ($print) {
  21681. /* -- TABLES-ADVANCED-BORDERS -- */
  21682. if ($details[$side]['style'] == 'double') {
  21683. if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
  21684. if ($details[$side]['c']) {
  21685. $this->SetDColor($details[$side]['c']);
  21686. } else {
  21687. $this->SetDColor($this->ConvertColor(0));
  21688. }
  21689. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  21690. }
  21691. if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
  21692. if ($bSeparate && $cort == 'table') {
  21693. if ($side == 'T') {
  21694. $xadj -= $this->LineWidth / 2;
  21695. $xadj2 -= $this->LineWidth;
  21696. if ($this->issetBorder($bord, _BORDER_LEFT)) {
  21697. $xadj += $this->LineWidth / 2;
  21698. }
  21699. if ($this->issetBorder($bord, _BORDER_RIGHT)) {
  21700. $xadj2 += $this->LineWidth;
  21701. }
  21702. }
  21703. if ($side == 'L') {
  21704. $yadj -= $this->LineWidth / 2;
  21705. $yadj2 -= $this->LineWidth;
  21706. if ($this->issetBorder($bord, _BORDER_TOP)) {
  21707. $yadj += $this->LineWidth / 2;
  21708. }
  21709. if ($this->issetBorder($bord, _BORDER_BOTTOM)) {
  21710. $yadj2 += $this->LineWidth;
  21711. }
  21712. }
  21713. if ($side == 'B') {
  21714. $xadj -= $this->LineWidth / 2;
  21715. $xadj2 -= $this->LineWidth;
  21716. if ($this->issetBorder($bord, _BORDER_LEFT)) {
  21717. $xadj += $this->LineWidth / 2;
  21718. }
  21719. if ($this->issetBorder($bord, _BORDER_RIGHT)) {
  21720. $xadj2 += $this->LineWidth;
  21721. }
  21722. }
  21723. if ($side == 'R') {
  21724. $yadj -= $this->LineWidth / 2;
  21725. $yadj2 -= $this->LineWidth;
  21726. if ($this->issetBorder($bord, _BORDER_TOP)) {
  21727. $yadj += $this->LineWidth / 2;
  21728. }
  21729. if ($this->issetBorder($bord, _BORDER_BOTTOM)) {
  21730. $yadj2 += $this->LineWidth;
  21731. }
  21732. }
  21733. }
  21734. $this->SetLineWidth($this->LineWidth / 3);
  21735. $tbcol = $this->ConvertColor(255);
  21736. for ($l = 0; $l <= $this->blklvl; $l++) {
  21737. if ($this->blk[$l]['bgcolor']) {
  21738. $tbcol = ($this->blk[$l]['bgcolorarray']);
  21739. }
  21740. }
  21741. if ($bSeparate) {
  21742. $cellBorderOverlay[] = array(
  21743. 'x' => $lx1 + $xadj,
  21744. 'y' => $ly1 + $yadj,
  21745. 'x2' => $lx2 - $xadj2,
  21746. 'y2' => $ly2 - $yadj2,
  21747. 'col' => $tbcol,
  21748. 'lw' => $this->LineWidth,
  21749. );
  21750. } else {
  21751. $this->SetDColor($tbcol);
  21752. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  21753. }
  21754. }
  21755. } elseif (isset($details[$side]['style']) && ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'groove' || $details[$side]['style'] == 'inset' || $details[$side]['style'] == 'outset')) {
  21756. if (!isset($details[$side]['overlay']) || !$details[$side]['overlay'] || $bSeparate) {
  21757. if ($details[$side]['c']) {
  21758. $this->SetDColor($details[$side]['c']);
  21759. } else {
  21760. $this->SetDColor($this->ConvertColor(0));
  21761. }
  21762. if ($details[$side]['style'] == 'outset' || $details[$side]['style'] == 'groove') {
  21763. $nc = $this->_darkenColor($details[$side]['c']);
  21764. $this->SetDColor($nc);
  21765. } elseif ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
  21766. $nc = $this->_lightenColor($details[$side]['c']);
  21767. $this->SetDColor($nc);
  21768. }
  21769. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  21770. }
  21771. if ((isset($details[$side]['overlay']) && $details[$side]['overlay']) || $bSeparate) {
  21772. if ($details[$side]['c']) {
  21773. $this->SetDColor($details[$side]['c']);
  21774. } else {
  21775. $this->SetDColor($this->ConvertColor(0));
  21776. }
  21777. $doubleadj = ($this->LineWidth) / 3;
  21778. $this->SetLineWidth($this->LineWidth / 2);
  21779. $xadj3 = $yadj3 = $wadj3 = $hadj3 = 0;
  21780. if ($details[$side]['style'] == 'ridge' || $details[$side]['style'] == 'inset') {
  21781. $nc = $this->_darkenColor($details[$side]['c']);
  21782. if ($bSeparate && $cort == 'table') {
  21783. if ($side == 'T') {
  21784. $yadj3 = $this->LineWidth / 2;
  21785. $xadj3 = -$this->LineWidth / 2;
  21786. $wadj3 = $this->LineWidth;
  21787. if ($this->issetBorder($bord, _BORDER_LEFT)) {
  21788. $xadj3 += $this->LineWidth;
  21789. $wadj3 -= $this->LineWidth;
  21790. }
  21791. if ($this->issetBorder($bord, _BORDER_RIGHT)) {
  21792. $wadj3 -= $this->LineWidth * 2;
  21793. }
  21794. }
  21795. if ($side == 'L') {
  21796. $xadj3 = $this->LineWidth / 2;
  21797. $yadj3 = -$this->LineWidth / 2;
  21798. $hadj3 = $this->LineWidth;
  21799. if ($this->issetBorder($bord, _BORDER_TOP)) {
  21800. $yadj3 += $this->LineWidth;
  21801. $hadj3 -= $this->LineWidth;
  21802. }
  21803. if ($this->issetBorder($bord, _BORDER_BOTTOM)) {
  21804. $hadj3 -= $this->LineWidth * 2;
  21805. }
  21806. }
  21807. if ($side == 'B') {
  21808. $yadj3 = $this->LineWidth / 2;
  21809. $xadj3 = -$this->LineWidth / 2;
  21810. $wadj3 = $this->LineWidth;
  21811. }
  21812. if ($side == 'R') {
  21813. $xadj3 = $this->LineWidth / 2;
  21814. $yadj3 = -$this->LineWidth / 2;
  21815. $hadj3 = $this->LineWidth;
  21816. }
  21817. } elseif ($side == 'T') {
  21818. $yadj3 = $this->LineWidth / 2;
  21819. $xadj3 = $this->LineWidth / 2;
  21820. $wadj3 = -$this->LineWidth * 2;
  21821. } elseif ($side == 'L') {
  21822. $xadj3 = $this->LineWidth / 2;
  21823. $yadj3 = $this->LineWidth / 2;
  21824. $hadj3 = -$this->LineWidth * 2;
  21825. } elseif ($side == 'B' && $bSeparate) {
  21826. $yadj3 = $this->LineWidth / 2;
  21827. $wadj3 = $this->LineWidth / 2;
  21828. } elseif ($side == 'R' && $bSeparate) {
  21829. $xadj3 = $this->LineWidth / 2;
  21830. $hadj3 = $this->LineWidth / 2;
  21831. } elseif ($side == 'B') {
  21832. $yadj3 = $this->LineWidth / 2;
  21833. $xadj3 = $this->LineWidth / 2;
  21834. } elseif ($side == 'R') {
  21835. $xadj3 = $this->LineWidth / 2;
  21836. $yadj3 = $this->LineWidth / 2;
  21837. }
  21838. } else {
  21839. $nc = $this->_lightenColor($details[$side]['c']);
  21840. if ($bSeparate && $cort == 'table') {
  21841. if ($side == 'T') {
  21842. $yadj3 = $this->LineWidth / 2;
  21843. $xadj3 = -$this->LineWidth / 2;
  21844. $wadj3 = $this->LineWidth;
  21845. if ($this->issetBorder($bord, _BORDER_LEFT)) {
  21846. $xadj3 += $this->LineWidth;
  21847. $wadj3 -= $this->LineWidth;
  21848. }
  21849. }
  21850. if ($side == 'L') {
  21851. $xadj3 = $this->LineWidth / 2;
  21852. $yadj3 = -$this->LineWidth / 2;
  21853. $hadj3 = $this->LineWidth;
  21854. if ($this->issetBorder($bord, _BORDER_TOP)) {
  21855. $yadj3 += $this->LineWidth;
  21856. $hadj3 -= $this->LineWidth;
  21857. }
  21858. }
  21859. if ($side == 'B') {
  21860. $yadj3 = $this->LineWidth / 2;
  21861. $xadj3 = -$this->LineWidth / 2;
  21862. $wadj3 = $this->LineWidth;
  21863. if ($this->issetBorder($bord, _BORDER_LEFT)) {
  21864. $xadj3 += $this->LineWidth;
  21865. $wadj3 -= $this->LineWidth;
  21866. }
  21867. }
  21868. if ($side == 'R') {
  21869. $xadj3 = $this->LineWidth / 2;
  21870. $yadj3 = -$this->LineWidth / 2;
  21871. $hadj3 = $this->LineWidth;
  21872. if ($this->issetBorder($bord, _BORDER_TOP)) {
  21873. $yadj3 += $this->LineWidth;
  21874. $hadj3 -= $this->LineWidth;
  21875. }
  21876. }
  21877. } elseif ($side == 'T') {
  21878. $yadj3 = $this->LineWidth / 2;
  21879. $xadj3 = $this->LineWidth / 2;
  21880. } elseif ($side == 'L') {
  21881. $xadj3 = $this->LineWidth / 2;
  21882. $yadj3 = $this->LineWidth / 2;
  21883. } elseif ($side == 'B' && $bSeparate) {
  21884. $yadj3 = $this->LineWidth / 2;
  21885. $xadj3 = $this->LineWidth / 2;
  21886. } elseif ($side == 'R' && $bSeparate) {
  21887. $xadj3 = $this->LineWidth / 2;
  21888. $yadj3 = $this->LineWidth / 2;
  21889. } elseif ($side == 'B') {
  21890. $yadj3 = $this->LineWidth / 2;
  21891. $xadj3 = -$this->LineWidth / 2;
  21892. $wadj3 = $this->LineWidth;
  21893. } elseif ($side == 'R') {
  21894. $xadj3 = $this->LineWidth / 2;
  21895. $yadj3 = -$this->LineWidth / 2;
  21896. $hadj3 = $this->LineWidth;
  21897. }
  21898. }
  21899. if ($bSeparate) {
  21900. $cellBorderOverlay[] = array(
  21901. 'x' => $lx1 + $xadj + $xadj3,
  21902. 'y' => $ly1 + $yadj + $yadj3,
  21903. 'x2' => $lx2 - $xadj2 + $xadj3 + $wadj3,
  21904. 'y2' => $ly2 - $yadj2 + $yadj3 + $hadj3,
  21905. 'col' => $nc,
  21906. 'lw' => $this->LineWidth,
  21907. );
  21908. } else {
  21909. $this->SetDColor($nc);
  21910. $this->Line($lx1 + $xadj + $xadj3, $ly1 + $yadj + $yadj3, $lx2 - $xadj2 + $xadj3 + $wadj3, $ly2 - $yadj2 + $yadj3 + $hadj3);
  21911. }
  21912. }
  21913. } else {
  21914. /* -- END TABLES-ADVANCED-BORDERS -- */
  21915. if ($details[$side]['style'] == 'dashed') {
  21916. $dashsize = 2; // final dash will be this + 1*linewidth
  21917. $dashsizek = 1.5; // ratio of Dash/Blank
  21918. $this->SetDash($dashsize, ($dashsize / $dashsizek) + ($this->LineWidth * 2));
  21919. } elseif ($details[$side]['style'] == 'dotted') {
  21920. $this->SetLineJoin(1);
  21921. $this->SetLineCap(1);
  21922. $this->SetDash(0.001, ($this->LineWidth * 2));
  21923. }
  21924. if ($details[$side]['c']) {
  21925. $this->SetDColor($details[$side]['c']);
  21926. } else {
  21927. $this->SetDColor($this->ConvertColor(0));
  21928. }
  21929. $this->Line($lx1 + $xadj, $ly1 + $yadj, $lx2 - $xadj2, $ly2 - $yadj2);
  21930. /* -- TABLES-ADVANCED-BORDERS -- */
  21931. }
  21932. /* -- END TABLES-ADVANCED-BORDERS -- */
  21933. // Reset Corners
  21934. $this->SetDash();
  21935. //BUTT style line cap
  21936. $this->SetLineCap(2);
  21937. }
  21938. }
  21939. if ($bSeparate && count($cellBorderOverlay)) {
  21940. foreach ($cellBorderOverlay AS $cbo) {
  21941. $this->SetLineWidth($cbo['lw']);
  21942. $this->SetDColor($cbo['col']);
  21943. $this->Line($cbo['x'], $cbo['y'], $cbo['x2'], $cbo['y2']);
  21944. }
  21945. }
  21946. // $this->SetLineWidth($oldlinewidth);
  21947. // $this->SetDColor($this->ConvertColor(0));
  21948. }
  21949. }
  21950. /* -- TABLES -- */
  21951. /* -- TABLES-ADVANCED-BORDERS -- */
  21952. function _lightenColor($c)
  21953. {
  21954. if (is_array($c)) {
  21955. throw new MpdfException('Color error in _lightencolor');
  21956. }
  21957. if ($c{0} == 3 || $c{0} == 5) { // RGB
  21958. list($h, $s, $l) = $this->rgb2hsl(ord($c{1}) / 255, ord($c{2}) / 255, ord($c{3}) / 255);
  21959. $l += ((1 - $l) * 0.8);
  21960. list($r, $g, $b) = $this->hsl2rgb($h, $s, $l);
  21961. $ret = array(3, $r, $g, $b);
  21962. } elseif ($c{0} == 4 || $c{0} == 6) { // CMYK
  21963. $ret = array(4, max(0, (ord($c{1}) - 20)), max(0, (ord($c{2}) - 20)), max(0, (ord($c{3}) - 20)), max(0, (ord($c{4}) - 20)));
  21964. } elseif ($c{0} == 1) { // Grayscale
  21965. $ret = array(1, min(255, (ord($c{1}) + 32)));
  21966. }
  21967. $c = array_pad($ret, 6, 0);
  21968. $cstr = pack("a1ccccc", $c[0], ($c[1] & 0xFF), ($c[2] & 0xFF), ($c[3] & 0xFF), ($c[4] & 0xFF), ($c[5] & 0xFF));
  21969. return $cstr;
  21970. }
  21971. function _darkenColor($c)
  21972. {
  21973. if (is_array($c)) {
  21974. throw new MpdfException('Color error in _darkenColor');
  21975. }
  21976. if ($c{0} == 3 || $c{0} == 5) { // RGB
  21977. list($h, $s, $l) = $this->rgb2hsl(ord($c{1}) / 255, ord($c{2}) / 255, ord($c{3}) / 255);
  21978. $s *= 0.25;
  21979. $l *= 0.75;
  21980. list($r, $g, $b) = $this->hsl2rgb($h, $s, $l);
  21981. $ret = array(3, $r, $g, $b);
  21982. } elseif ($c{0} == 4 || $c{0} == 6) { // CMYK
  21983. $ret = array(4, min(100, (ord($c{1}) + 20)), min(100, (ord($c{2}) + 20)), min(100, (ord($c{3}) + 20)), min(100, (ord($c{4}) + 20)));
  21984. } elseif ($c{0} == 1) { // Grayscale
  21985. $ret = array(1, max(0, (ord($c{1}) - 32)));
  21986. }
  21987. $c = array_pad($ret, 6, 0);
  21988. $cstr = pack("a1ccccc", $c[0], ($c[1] & 0xFF), ($c[2] & 0xFF), ($c[3] & 0xFF), ($c[4] & 0xFF), ($c[5] & 0xFF));
  21989. return $cstr;
  21990. }
  21991. /* -- END TABLES-ADVANCED-BORDERS -- */
  21992. function setBorder(&$var, $flag, $set = true)
  21993. {
  21994. $flag = intval($flag);
  21995. if ($set) {
  21996. $set = true;
  21997. }
  21998. $var = intval($var);
  21999. $var = $set ? ($var | $flag) : ($var & ~$flag);
  22000. }
  22001. function issetBorder($var, $flag)
  22002. {
  22003. $flag = intval($flag);
  22004. $var = intval($var);
  22005. return (($var & $flag) == $flag);
  22006. }
  22007. function _table2cellBorder(&$tableb, &$cbdb, &$cellb, $bval)
  22008. {
  22009. if ($tableb && $tableb['w'] > $cbdb['w']) {
  22010. $cbdb = $tableb;
  22011. $this->setBorder($cellb, $bval);
  22012. } elseif ($tableb && $tableb['w'] == $cbdb['w'] && array_search($tableb['style'], $this->borderstyles) > array_search($cbdb['style'], $this->borderstyles)) {
  22013. $cbdb = $tableb;
  22014. $this->setBorder($cellb, $bval);
  22015. }
  22016. }
  22017. // FIX BORDERS ********************************************
  22018. function _fixTableBorders(&$table)
  22019. {
  22020. if (!$table['borders_separate'] && $table['border_details']['L']['w']) {
  22021. $table['max_cell_border_width']['L'] = $table['border_details']['L']['w'];
  22022. }
  22023. if (!$table['borders_separate'] && $table['border_details']['R']['w']) {
  22024. $table['max_cell_border_width']['R'] = $table['border_details']['R']['w'];
  22025. }
  22026. if (!$table['borders_separate'] && $table['border_details']['T']['w']) {
  22027. $table['max_cell_border_width']['T'] = $table['border_details']['T']['w'];
  22028. }
  22029. if (!$table['borders_separate'] && $table['border_details']['B']['w']) {
  22030. $table['max_cell_border_width']['B'] = $table['border_details']['B']['w'];
  22031. }
  22032. if ($this->simpleTables) {
  22033. return;
  22034. }
  22035. $cells = &$table['cells'];
  22036. $numcols = $table['nc'];
  22037. $numrows = $table['nr'];
  22038. /* -- TABLES-ADVANCED-BORDERS -- */
  22039. if (isset($table['topntail']) && $table['topntail']) {
  22040. $tntborddet = $this->border_details($table['topntail']);
  22041. }
  22042. if (isset($table['thead-underline']) && $table['thead-underline']) {
  22043. $thuborddet = $this->border_details($table['thead-underline']);
  22044. }
  22045. /* -- END TABLES-ADVANCED-BORDERS -- */
  22046. for ($i = 0; $i < $numrows; $i++) { //Rows
  22047. for ($j = 0; $j < $numcols; $j++) { //Columns
  22048. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  22049. $cell = &$cells[$i][$j];
  22050. if ($this->packTableData) {
  22051. $cbord = $this->_unpackCellBorder($cell['borderbin']);
  22052. } else {
  22053. $cbord = &$cells[$i][$j];
  22054. }
  22055. // mPDF 5.7.3
  22056. if (!$cbord['border'] && $cbord['border'] !== 0 && isset($table['border']) && $table['border'] && $this->table_border_attr_set) {
  22057. $cbord['border'] = $table['border'];
  22058. $cbord['border_details'] = $table['border_details'];
  22059. }
  22060. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  22061. $ccolsp = $cell['colspan'];
  22062. } else {
  22063. $ccolsp = 1;
  22064. }
  22065. if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
  22066. $crowsp = $cell['rowspan'];
  22067. } else {
  22068. $crowsp = 1;
  22069. }
  22070. $cbord['border_details']['cellposdom'] = ((($i + 1) / $numrows) / 10000 ) + ((($j + 1) / $numcols) / 10 );
  22071. // Inherit Cell border from Table border
  22072. if ($this->table_border_css_set && !$table['borders_separate']) {
  22073. if ($i == 0) {
  22074. $this->_table2cellBorder($table['border_details']['T'], $cbord['border_details']['T'], $cbord['border'], _BORDER_TOP);
  22075. }
  22076. if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
  22077. $this->_table2cellBorder($table['border_details']['B'], $cbord['border_details']['B'], $cbord['border'], _BORDER_BOTTOM);
  22078. }
  22079. if ($j == 0) {
  22080. $this->_table2cellBorder($table['border_details']['L'], $cbord['border_details']['L'], $cbord['border'], _BORDER_LEFT);
  22081. }
  22082. if ($j == ($numcols - 1) || ($j + $ccolsp) == ($numcols)) {
  22083. $this->_table2cellBorder($table['border_details']['R'], $cbord['border_details']['R'], $cbord['border'], _BORDER_RIGHT);
  22084. }
  22085. }
  22086. /* -- TABLES-ADVANCED-BORDERS -- */
  22087. $fixbottom = true;
  22088. if (isset($table['topntail']) && $table['topntail']) {
  22089. if ($i == 0) {
  22090. $cbord['border_details']['T'] = $tntborddet;
  22091. $this->setBorder($cbord['border'], _BORDER_TOP);
  22092. }
  22093. if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
  22094. $cbord['border_details']['B'] = $tntborddet;
  22095. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22096. $fixbottom = false;
  22097. } elseif ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows']) {
  22098. if (!$table['borders_separate']) {
  22099. $cbord['border_details']['T'] = $tntborddet;
  22100. $this->setBorder($cbord['border'], _BORDER_TOP);
  22101. }
  22102. }
  22103. if ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'] - 1)) {
  22104. if (!$table['borders_separate']) {
  22105. $cbord['border_details']['B'] = $tntborddet;
  22106. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22107. $fixbottom = false;
  22108. }
  22109. } elseif ($this->tableLevel == 1 && $table['footernrows'] > 0 && $i == ($numrows - $table['footernrows'])) {
  22110. $cbord['border_details']['T'] = $tntborddet;
  22111. $this->setBorder($cbord['border'], _BORDER_TOP);
  22112. }
  22113. if ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
  22114. if (!$table['borders_separate']) {
  22115. $cbord['border_details']['T'] = $tntborddet;
  22116. $this->setBorder($cbord['border'], _BORDER_TOP);
  22117. }
  22118. }
  22119. if ($i == ($numrows - 1) || ($i + $crowsp) == ($numrows)) {
  22120. $cbord['border_details']['B'] = $tntborddet;
  22121. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22122. }
  22123. }
  22124. if (isset($table['thead-underline']) && $table['thead-underline']) {
  22125. if ($table['borders_separate']) {
  22126. if ($i == 0) {
  22127. $cbord['border_details']['B'] = $thuborddet;
  22128. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22129. $fixbottom = false;
  22130. }
  22131. } else {
  22132. if ($this->tableLevel == 1 && $table['headernrows'] > 0 && $i == $table['headernrows'] - 1) {
  22133. $cbord['border_details']['T'] = $thuborddet;
  22134. $this->setBorder($cbord['border'], _BORDER_TOP);
  22135. } elseif ($this->tabletheadjustfinished) { // $this->tabletheadjustfinished called from tableheader
  22136. $cbord['border_details']['T'] = $thuborddet;
  22137. $this->setBorder($cbord['border'], _BORDER_TOP);
  22138. }
  22139. }
  22140. }
  22141. // Collapse Border - Algorithm for conflicting borders
  22142. // Hidden >> Width >> double>solid>dashed>dotted... >> style set on cell>table >> top/left>bottom/right
  22143. // Do not turn off border which is overridden
  22144. // Needed for page break for TOP/BOTTOM both to be defined in Collapsed borders
  22145. // Means it is painted twice. (Left/Right can still disable overridden border)
  22146. if (!$table['borders_separate']) {
  22147. if (($i < ($numrows - 1) || ($i + $crowsp) < $numrows ) && $fixbottom) { // Bottom
  22148. for ($cspi = 0; $cspi < $ccolsp; $cspi++) {
  22149. // already defined Top for adjacent cell below
  22150. if (isset($cells[($i + $crowsp)][$j + $cspi])) {
  22151. if ($this->packTableData) {
  22152. $adjc = $cells[($i + $crowsp)][$j + $cspi];
  22153. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  22154. } else {
  22155. $celladj = & $cells[($i + $crowsp)][$j + $cspi];
  22156. }
  22157. } else {
  22158. $celladj = false;
  22159. }
  22160. if ($celladj && $celladj['border_details']['T']['s'] == 1) {
  22161. $csadj = $celladj['border_details']['T']['w'];
  22162. $csthis = $cbord['border_details']['B']['w'];
  22163. // Hidden
  22164. if ($cbord['border_details']['B']['style'] == 'hidden') {
  22165. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  22166. $this->setBorder($celladj['border'], _BORDER_TOP, false);
  22167. $this->setBorder($cbord['border'], _BORDER_BOTTOM, false);
  22168. } elseif ($celladj['border_details']['T']['style'] == 'hidden') {
  22169. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  22170. $this->setBorder($cbord['border'], _BORDER_BOTTOM, false);
  22171. $this->setBorder($celladj['border'], _BORDER_TOP, false);
  22172. }
  22173. // Width
  22174. elseif ($csthis > $csadj) {
  22175. if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
  22176. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  22177. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22178. }
  22179. } elseif ($csadj > $csthis) {
  22180. if ($ccolsp < 2) { // don't overwrite this cell if it spans
  22181. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  22182. $this->setBorder($celladj['border'], _BORDER_TOP);
  22183. }
  22184. }
  22185. // double>solid>dashed>dotted...
  22186. elseif (array_search($cbord['border_details']['B']['style'], $this->borderstyles) > array_search($celladj['border_details']['T']['style'], $this->borderstyles)) {
  22187. if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
  22188. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  22189. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22190. }
  22191. } elseif (array_search($celladj['border_details']['T']['style'], $this->borderstyles) > array_search($cbord['border_details']['B']['style'], $this->borderstyles)) {
  22192. if ($ccolsp < 2) { // don't overwrite this cell if it spans
  22193. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  22194. $this->setBorder($celladj['border'], _BORDER_TOP);
  22195. }
  22196. }
  22197. // Style set on cell vs. table
  22198. elseif ($celladj['border_details']['T']['dom'] > $cbord['border_details']['B']['dom']) {
  22199. if ($ccolsp < 2) { // don't overwrite this cell if it spans
  22200. $cbord['border_details']['B'] = $celladj['border_details']['T'];
  22201. $this->setBorder($celladj['border'], _BORDER_TOP);
  22202. }
  22203. }
  22204. // Style set on cell vs. table - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
  22205. else {
  22206. if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
  22207. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  22208. $this->setBorder($cbord['border'], _BORDER_BOTTOM);
  22209. }
  22210. }
  22211. } elseif ($celladj) {
  22212. if (!isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) || (isset($cells[($i + $crowsp)][$j + $cspi]['colspan']) && $cells[($i + $crowsp)][$j + $cspi]['colspan'] < 2)) { // don't overwrite bordering cells that span
  22213. $celladj['border_details']['T'] = $cbord['border_details']['B'];
  22214. }
  22215. }
  22216. // mPDF 5.7.4
  22217. if ($celladj && $this->packTableData) {
  22218. $cells[$i + $crowsp][$j + $cspi]['borderbin'] = $this->_packCellBorder($celladj);
  22219. }
  22220. unset($celladj);
  22221. }
  22222. }
  22223. if ($j < ($numcols - 1) || ($j + $ccolsp) < $numcols) { // Right-Left
  22224. for ($cspi = 0; $cspi < $crowsp; $cspi++) {
  22225. // already defined Left for adjacent cell to R
  22226. if (isset($cells[($i + $cspi)][$j + $ccolsp])) {
  22227. if ($this->packTableData) {
  22228. $adjc = $cells[($i + $cspi)][$j + $ccolsp];
  22229. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  22230. } else {
  22231. $celladj = & $cells[$i + $cspi][$j + $ccolsp];
  22232. }
  22233. } else {
  22234. $celladj = false;
  22235. }
  22236. if ($celladj && $celladj['border_details']['L']['s'] == 1) {
  22237. $csadj = $celladj['border_details']['L']['w'];
  22238. $csthis = $cbord['border_details']['R']['w'];
  22239. // Hidden
  22240. if ($cbord['border_details']['R']['style'] == 'hidden') {
  22241. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  22242. $this->setBorder($celladj['border'], _BORDER_LEFT, false);
  22243. $this->setBorder($cbord['border'], _BORDER_RIGHT, false);
  22244. } elseif ($celladj['border_details']['L']['style'] == 'hidden') {
  22245. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  22246. $this->setBorder($cbord['border'], _BORDER_RIGHT, false);
  22247. $this->setBorder($celladj['border'], _BORDER_LEFT, false);
  22248. }
  22249. // Width
  22250. elseif ($csthis > $csadj) {
  22251. if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
  22252. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  22253. $this->setBorder($cbord['border'], _BORDER_RIGHT);
  22254. $this->setBorder($celladj['border'], _BORDER_LEFT, false);
  22255. }
  22256. } elseif ($csadj > $csthis) {
  22257. if ($crowsp < 2) { // don't overwrite this cell if it spans
  22258. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  22259. $this->setBorder($cbord['border'], _BORDER_RIGHT, false);
  22260. $this->setBorder($celladj['border'], _BORDER_LEFT);
  22261. }
  22262. }
  22263. // double>solid>dashed>dotted...
  22264. elseif (array_search($cbord['border_details']['R']['style'], $this->borderstyles) > array_search($celladj['border_details']['L']['style'], $this->borderstyles)) {
  22265. if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
  22266. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  22267. $this->setBorder($celladj['border'], _BORDER_LEFT, false);
  22268. $this->setBorder($cbord['border'], _BORDER_RIGHT);
  22269. }
  22270. } elseif (array_search($celladj['border_details']['L']['style'], $this->borderstyles) > array_search($cbord['border_details']['R']['style'], $this->borderstyles)) {
  22271. if ($crowsp < 2) { // don't overwrite this cell if it spans
  22272. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  22273. $this->setBorder($cbord['border'], _BORDER_RIGHT, false);
  22274. $this->setBorder($celladj['border'], _BORDER_LEFT);
  22275. }
  22276. }
  22277. // Style set on cell vs. table
  22278. elseif ($celladj['border_details']['L']['dom'] > $cbord['border_details']['R']['dom']) {
  22279. if ($crowsp < 2) { // don't overwrite this cell if it spans
  22280. $cbord['border_details']['R'] = $celladj['border_details']['L'];
  22281. $this->setBorder($celladj['border'], _BORDER_LEFT);
  22282. }
  22283. }
  22284. // Style set on cell vs. table - OR - LEFT/TOP (cell) in preference to BOTTOM/RIGHT
  22285. else {
  22286. if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
  22287. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  22288. $this->setBorder($cbord['border'], _BORDER_RIGHT);
  22289. }
  22290. }
  22291. } elseif ($celladj) {
  22292. // if right-cell border is not set
  22293. if (!isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) || (isset($cells[($i + $cspi)][$j + $ccolsp]['rowspan']) && $cells[($i + $cspi)][$j + $ccolsp]['rowspan'] < 2)) { // don't overwrite bordering cells that span
  22294. $celladj['border_details']['L'] = $cbord['border_details']['R'];
  22295. }
  22296. }
  22297. // mPDF 5.7.4
  22298. if ($celladj && $this->packTableData) {
  22299. $cells[$i + $cspi][$j + $ccolsp]['borderbin'] = $this->_packCellBorder($celladj);
  22300. }
  22301. unset($celladj);
  22302. }
  22303. }
  22304. }
  22305. // Set maximum cell border width meeting at LRTB edges of cell - used for extended cell border
  22306. // ['border_details']['mbw']['LT'] = meeting border width - Left border - Top end
  22307. if (!$table['borders_separate']) {
  22308. $cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $cbord['border_details']['L']['w']);
  22309. $cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $cbord['border_details']['R']['w']);
  22310. $cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $cbord['border_details']['T']['w']);
  22311. $cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $cbord['border_details']['B']['w']);
  22312. $cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $cbord['border_details']['L']['w']);
  22313. $cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $cbord['border_details']['R']['w']);
  22314. $cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $cbord['border_details']['T']['w']);
  22315. $cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $cbord['border_details']['B']['w']);
  22316. if (($i + $crowsp) < $numrows && isset($cells[$i + $crowsp][$j])) { // Has Bottom adjoining cell
  22317. if ($this->packTableData) {
  22318. $adjc = $cells[$i + $crowsp][$j];
  22319. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  22320. } else {
  22321. $celladj = & $cells[$i + $crowsp][$j];
  22322. }
  22323. $cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $celladj['border_details']['L']['w'], $celladj['border_details']['mbw']['TL']);
  22324. $cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $celladj['border_details']['R']['w'], $celladj['border_details']['mbw']['TR']);
  22325. $cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $celladj['border_details']['mbw']['LT']);
  22326. $cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $celladj['border_details']['mbw']['RT']);
  22327. unset($celladj);
  22328. }
  22329. if (($j + $ccolsp) < $numcols && isset($cells[$i][$j + $ccolsp])) { // Has Right adjoining cell
  22330. if ($this->packTableData) {
  22331. $adjc = $cells[$i][$j + $ccolsp];
  22332. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  22333. } else {
  22334. $celladj = & $cells[$i][$j + $ccolsp];
  22335. }
  22336. $cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $celladj['border_details']['T']['w'], $celladj['border_details']['mbw']['LT']);
  22337. $cbord['border_details']['mbw']['RB'] = max($cbord['border_details']['mbw']['RB'], $celladj['border_details']['B']['w'], $celladj['border_details']['mbw']['LB']);
  22338. $cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $celladj['border_details']['mbw']['TL']);
  22339. $cbord['border_details']['mbw']['BR'] = max($cbord['border_details']['mbw']['BR'], $celladj['border_details']['mbw']['BL']);
  22340. unset($celladj);
  22341. }
  22342. if ($i > 0 && isset($cells[$i - 1][$j]) && (($this->packTableData && $cells[$i - 1][$j]['borderbin']) || $cells[$i - 1][$j]['border'])) { // Has Top adjoining cell
  22343. if ($this->packTableData) {
  22344. $adjc = $cells[$i - 1][$j];
  22345. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  22346. } else {
  22347. $celladj = & $cells[$i - 1][$j];
  22348. }
  22349. $cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['L']['w'], $celladj['border_details']['mbw']['BL']);
  22350. $cbord['border_details']['mbw']['TR'] = max($cbord['border_details']['mbw']['TR'], $celladj['border_details']['R']['w'], $celladj['border_details']['mbw']['BR']);
  22351. $cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $celladj['border_details']['mbw']['LB']);
  22352. $cbord['border_details']['mbw']['RT'] = max($cbord['border_details']['mbw']['RT'], $celladj['border_details']['mbw']['RB']);
  22353. if ($celladj['border_details']['mbw']['BL']) {
  22354. $celladj['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['BL']);
  22355. }
  22356. if ($celladj['border_details']['mbw']['BR']) {
  22357. $celladj['border_details']['mbw']['BR'] = max($celladj['border_details']['mbw']['BR'], $cbord['border_details']['mbw']['TR']);
  22358. }
  22359. if ($this->packTableData) {
  22360. $cells[$i - 1][$j]['borderbin'] = $this->_packCellBorder($celladj);
  22361. }
  22362. unset($celladj);
  22363. }
  22364. if ($j > 0 && isset($cells[$i][$j - 1]) && (($this->packTableData && $cells[$i][$j - 1]['borderbin']) || $cells[$i][$j - 1]['border'])) { // Has Left adjoining cell
  22365. if ($this->packTableData) {
  22366. $adjc = $cells[$i][$j - 1];
  22367. $celladj = $this->_unpackCellBorder($adjc['borderbin']);
  22368. } else {
  22369. $celladj = & $cells[$i][$j - 1];
  22370. }
  22371. $cbord['border_details']['mbw']['LT'] = max($cbord['border_details']['mbw']['LT'], $celladj['border_details']['T']['w'], $celladj['border_details']['mbw']['RT']);
  22372. $cbord['border_details']['mbw']['LB'] = max($cbord['border_details']['mbw']['LB'], $celladj['border_details']['B']['w'], $celladj['border_details']['mbw']['RB']);
  22373. $cbord['border_details']['mbw']['BL'] = max($cbord['border_details']['mbw']['BL'], $celladj['border_details']['mbw']['BR']);
  22374. $cbord['border_details']['mbw']['TL'] = max($cbord['border_details']['mbw']['TL'], $celladj['border_details']['mbw']['TR']);
  22375. if ($celladj['border_details']['mbw']['RT']) {
  22376. $celladj['border_details']['mbw']['RT'] = max($celladj['border_details']['mbw']['RT'], $cbord['border_details']['mbw']['LT']);
  22377. }
  22378. if ($celladj['border_details']['mbw']['RB']) {
  22379. $celladj['border_details']['mbw']['RB'] = max($celladj['border_details']['mbw']['RB'], $cbord['border_details']['mbw']['LB']);
  22380. }
  22381. if ($this->packTableData) {
  22382. $cells[$i][$j - 1]['borderbin'] = $this->_packCellBorder($celladj);
  22383. }
  22384. unset($celladj);
  22385. }
  22386. // Update maximum cell border width at LRTB edges of table - used for overall table width
  22387. if ($j == 0 && $cbord['border_details']['L']['w']) {
  22388. $table['max_cell_border_width']['L'] = max($table['max_cell_border_width']['L'], $cbord['border_details']['L']['w']);
  22389. }
  22390. if (($j == ($numcols - 1) || ($j + $ccolsp) == $numcols ) && $cbord['border_details']['R']['w']) {
  22391. $table['max_cell_border_width']['R'] = max($table['max_cell_border_width']['R'], $cbord['border_details']['R']['w']);
  22392. }
  22393. if ($i == 0 && $cbord['border_details']['T']['w']) {
  22394. $table['max_cell_border_width']['T'] = max($table['max_cell_border_width']['T'], $cbord['border_details']['T']['w']);
  22395. }
  22396. if (($i == ($numrows - 1) || ($i + $crowsp) == $numrows ) && $cbord['border_details']['B']['w']) {
  22397. $table['max_cell_border_width']['B'] = max($table['max_cell_border_width']['B'], $cbord['border_details']['B']['w']);
  22398. }
  22399. }
  22400. /* -- END TABLES-ADVANCED-BORDERS -- */
  22401. if ($this->packTableData) {
  22402. $cell['borderbin'] = $this->_packCellBorder($cbord);
  22403. }
  22404. unset($cbord);
  22405. unset($cell);
  22406. }
  22407. }
  22408. }
  22409. unset($cell);
  22410. }
  22411. // END FIX BORDERS ************************************************************************************
  22412. function _reverseTableDir(&$table)
  22413. {
  22414. $cells = &$table['cells'];
  22415. $numcols = $table['nc'];
  22416. $numrows = $table['nr'];
  22417. for ($i = 0; $i < $numrows; $i++) { //Rows
  22418. $row = array();
  22419. for ($j = ($numcols - 1); $j >= 0; $j--) { //Columns
  22420. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  22421. $cell = &$cells[$i][$j];
  22422. $col = $numcols - $j - 1;
  22423. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  22424. $col -= ($cell['colspan'] - 1);
  22425. }
  22426. // Nested content
  22427. if (isset($cell['textbuffer'])) {
  22428. for ($n = 0; $n < count($cell['textbuffer']); $n++) {
  22429. $t = $cell['textbuffer'][$n][0];
  22430. if (substr($t, 0, 19) == "\xbb\xa4\xactype=nestedtable") {
  22431. $objattr = $this->_getObjAttr($t);
  22432. $objattr['col'] = $col;
  22433. $cell['textbuffer'][$n][0] = "\xbb\xa4\xactype=nestedtable,objattr=" . serialize($objattr) . "\xbb\xa4\xac";
  22434. $this->table[($this->tableLevel + 1)][$objattr['nestedcontent']]['nestedpos'][1] = $col;
  22435. }
  22436. }
  22437. }
  22438. $row[$col] = $cells[$i][$j];
  22439. unset($cell);
  22440. }
  22441. }
  22442. for ($f = 0; $f < $numcols; $f++) {
  22443. if (!isset($row[$f])) {
  22444. $row[$f] = 0;
  22445. }
  22446. }
  22447. $table['cells'][$i] = $row;
  22448. }
  22449. }
  22450. function _tableWrite(&$table, $split = false, $startrow = 0, $startcol = 0, $splitpg = 0, $rety = 0)
  22451. {
  22452. $level = $table['level'];
  22453. $levelid = $table['levelid'];
  22454. $cells = &$table['cells'];
  22455. $numcols = $table['nc'];
  22456. $numrows = $table['nr'];
  22457. $maxbwtop = 0;
  22458. if ($this->ColActive && $level == 1) {
  22459. $this->breakpoints[$this->CurrCol][] = $this->y;
  22460. } // *COLUMNS*
  22461. if (!$split || ($startrow == 0 && $splitpg == 0) || $startrow > 0) {
  22462. // TABLE TOP MARGIN
  22463. if ($table['margin']['T']) {
  22464. if (!$this->table_rotate && $level == 1) {
  22465. $this->DivLn($table['margin']['T'], $this->blklvl, true, 1); // collapsible
  22466. } else {
  22467. $this->y += ($table['margin']['T']);
  22468. }
  22469. }
  22470. // Advance down page by half width of top border
  22471. if ($table['borders_separate']) {
  22472. if ($startrow > 0 && (!isset($table['is_thead']) || count($table['is_thead']) == 0))
  22473. $adv = $table['border_spacing_V'] / 2;
  22474. else
  22475. $adv = $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
  22476. }
  22477. else {
  22478. $adv = $table['max_cell_border_width']['T'] / 2;
  22479. }
  22480. if (!$this->table_rotate && $level == 1) {
  22481. $this->DivLn($adv);
  22482. } else {
  22483. $this->y += $adv;
  22484. }
  22485. }
  22486. if ($level == 1) {
  22487. $this->x = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];
  22488. $x0 = $this->x;
  22489. $y0 = $this->y;
  22490. $right = $x0 + $this->blk[$this->blklvl]['inner_width'];
  22491. $outerfilled = $this->y; // Keep track of how far down the outer DIV bgcolor is painted (NB rowspans)
  22492. $this->outerfilled = $this->y;
  22493. $this->colsums = array();
  22494. } else {
  22495. $x0 = $this->x;
  22496. $y0 = $this->y;
  22497. $right = $x0 + $table['w'];
  22498. }
  22499. if ($this->table_rotate) {
  22500. $temppgwidth = $this->tbrot_maxw;
  22501. $this->PageBreakTrigger = $pagetrigger = $y0 + ($this->blk[$this->blklvl]['inner_width']);
  22502. if ($level == 1) {
  22503. $this->tbrot_y0 = $this->y - $adv - $table['margin']['T'];
  22504. $this->tbrot_x0 = $this->x;
  22505. $this->tbrot_w = $table['w'];
  22506. if ($table['borders_separate']) {
  22507. $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
  22508. } else {
  22509. $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['max_cell_border_width']['T'];
  22510. }
  22511. }
  22512. } else {
  22513. $this->PageBreakTrigger = $pagetrigger = ($this->h - $this->bMargin);
  22514. if ($level == 1) {
  22515. $temppgwidth = $this->blk[$this->blklvl]['inner_width'];
  22516. if (isset($table['a']) and ( $table['w'] < $this->blk[$this->blklvl]['inner_width'])) {
  22517. if ($table['a'] == 'C') {
  22518. $x0 += ((($right - $x0) - $table['w']) / 2);
  22519. } elseif ($table['a'] == 'R') {
  22520. $x0 = $right - $table['w'];
  22521. }
  22522. }
  22523. } else {
  22524. $temppgwidth = $table['w'];
  22525. }
  22526. }
  22527. if (!isset($table['overflow'])) {
  22528. $table['overflow'] = null;
  22529. }
  22530. if ($table['overflow'] == 'hidden' && $level == 1 && !$this->table_rotate && !$this->ColActive) {
  22531. //Bounding rectangle to clip
  22532. $this->tableClipPath = sprintf('q %.3F %.3F %.3F %.3F re W n', $x0 * _MPDFK, $this->h * _MPDFK, $this->blk[$this->blklvl]['inner_width'] * _MPDFK, -$this->h * _MPDFK);
  22533. $this->_out($this->tableClipPath);
  22534. } else {
  22535. $this->tableClipPath = '';
  22536. }
  22537. if ($table['borders_separate']) {
  22538. $indent = $table['margin']['L'] + $table['border_details']['L']['w'] + $table['padding']['L'] + $table['border_spacing_H'] / 2;
  22539. } else {
  22540. $indent = $table['margin']['L'] + $table['max_cell_border_width']['L'] / 2;
  22541. }
  22542. $x0 += $indent;
  22543. $returny = 0;
  22544. $lastCol = 0;
  22545. $tableheader = array();
  22546. $tablefooter = array();
  22547. $tableheaderrowheight = 0;
  22548. $tablefooterrowheight = 0;
  22549. $footery = 0;
  22550. // mPD 3.0 Set the Page & Column where table starts
  22551. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  22552. $tablestartpage = 'EVEN';
  22553. } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD
  22554. $tablestartpage = 'ODD';
  22555. } else {
  22556. $tablestartpage = '';
  22557. }
  22558. if ($this->ColActive) {
  22559. $tablestartcolumn = $this->CurrCol;
  22560. } else {
  22561. $tablestartcolumn = '';
  22562. }
  22563. $y = $h = 0;
  22564. for ($i = 0; $i < $numrows; $i++) { //Rows
  22565. if ($this->progressBar) {
  22566. $this->UpdateProgressBar(7, intval(30 + ($i * 40 / $numrows)), ' ');
  22567. } // *PROGRESS-BAR*
  22568. if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i] && $level == 1) {
  22569. $tablefooterrowheight += $table['hr'][$i];
  22570. $tablefooter[$i][0]['trbackground-images'] = $table['trbackground-images'][$i];
  22571. $tablefooter[$i][0]['trgradients'] = $table['trgradients'][$i];
  22572. $tablefooter[$i][0]['trbgcolor'] = $table['bgcolor'][$i];
  22573. for ($j = $startcol; $j < $numcols; $j++) { //Columns
  22574. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  22575. $cell = &$cells[$i][$j];
  22576. if ($split) {
  22577. if ($table['colPg'][$j] != $splitpg) {
  22578. continue;
  22579. }
  22580. list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
  22581. $js = $j - $startcol;
  22582. } else {
  22583. list($x, $w) = $this->_tableGetWidth($table, $i, $j);
  22584. $js = $j;
  22585. }
  22586. list($y, $h) = $this->_tableGetHeight($table, $i, $j);
  22587. $x += $x0;
  22588. $y += $y0;
  22589. //Get info of tfoot ==>> table footer
  22590. $tablefooter[$i][$js]['x'] = $x;
  22591. $tablefooter[$i][$js]['y'] = $y;
  22592. $tablefooter[$i][$js]['h'] = $h;
  22593. $tablefooter[$i][$js]['w'] = $w;
  22594. if (isset($cell['textbuffer'])) {
  22595. $tablefooter[$i][$js]['textbuffer'] = $cell['textbuffer'];
  22596. } else {
  22597. $tablefooter[$i][$js]['textbuffer'] = '';
  22598. }
  22599. $tablefooter[$i][$js]['a'] = $cell['a'];
  22600. $tablefooter[$i][$js]['R'] = $cell['R'];
  22601. $tablefooter[$i][$js]['va'] = $cell['va'];
  22602. $tablefooter[$i][$js]['mih'] = $cell['mih'];
  22603. if (isset($cell['gradient']))
  22604. $tablefooter[$i][$js]['gradient'] = $cell['gradient']; // *BACKGROUNDS*
  22605. if (isset($cell['background-image']))
  22606. $tablefooter[$i][$js]['background-image'] = $cell['background-image']; // *BACKGROUNDS*
  22607. //CELL FILL BGCOLOR
  22608. if (!$this->simpleTables) {
  22609. if ($this->packTableData) {
  22610. $c = $this->_unpackCellBorder($cell['borderbin']);
  22611. $tablefooter[$i][$js]['border'] = $c['border'];
  22612. $tablefooter[$i][$js]['border_details'] = $c['border_details'];
  22613. } else {
  22614. $tablefooter[$i][$js]['border'] = $cell['border'];
  22615. $tablefooter[$i][$js]['border_details'] = $cell['border_details'];
  22616. }
  22617. } elseif ($this->simpleTables) {
  22618. $tablefooter[$i][$js]['border'] = $table['simple']['border'];
  22619. $tablefooter[$i][$js]['border_details'] = $table['simple']['border_details'];
  22620. }
  22621. $tablefooter[$i][$js]['bgcolor'] = $cell['bgcolor'];
  22622. $tablefooter[$i][$js]['padding'] = $cell['padding'];
  22623. if (isset($cell['rowspan']))
  22624. $tablefooter[$i][$js]['rowspan'] = $cell['rowspan'];
  22625. if (isset($cell['colspan']))
  22626. $tablefooter[$i][$js]['colspan'] = $cell['colspan'];
  22627. if (isset($cell['direction']))
  22628. $tablefooter[$i][$js]['direction'] = $cell['direction'];
  22629. if (isset($cell['cellLineHeight']))
  22630. $tablefooter[$i][$js]['cellLineHeight'] = $cell['cellLineHeight'];
  22631. if (isset($cell['cellLineStackingStrategy']))
  22632. $tablefooter[$i][$js]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
  22633. if (isset($cell['cellLineStackingShift']))
  22634. $tablefooter[$i][$js]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
  22635. }
  22636. }
  22637. }
  22638. }
  22639. if ($level == 1) {
  22640. $this->_out('___TABLE___BACKGROUNDS' . $this->uniqstr);
  22641. }
  22642. $tableheaderadj = 0;
  22643. $tablefooteradj = 0;
  22644. $tablestartpageno = $this->page;
  22645. //Draw Table Contents and Borders
  22646. for ($i = 0; $i < $numrows; $i++) { //Rows
  22647. if ($split && $startrow > 0) {
  22648. $thnr = (isset($table['is_thead']) ? count($table['is_thead']) : 0);
  22649. if ($i >= $thnr && $i < $startrow) {
  22650. continue;
  22651. }
  22652. if ($i == $startrow) {
  22653. $returny = $rety - $tableheaderrowheight;
  22654. }
  22655. }
  22656. // Get Maximum row/cell height in row - including rowspan>1 + 1 overlapping
  22657. $maxrowheight = $this->_tableGetMaxRowHeight($table, $i);
  22658. $skippage = false;
  22659. $newpagestarted = false;
  22660. for ($j = $startcol; $j < $numcols; $j++) { //Columns
  22661. if ($split) {
  22662. if ($table['colPg'][$j] > $splitpg) {
  22663. break;
  22664. }
  22665. $lastCol = $j;
  22666. }
  22667. if (isset($cells[$i][$j]) && $cells[$i][$j]) {
  22668. $cell = &$cells[$i][$j];
  22669. if ($split) {
  22670. $lastCol = $j + (isset($cell['colspan']) ? ($cell['colspan'] - 1) : 0);
  22671. list($x, $w) = $this->_splitTableGetWidth($table, $i, $j);
  22672. } else {
  22673. list($x, $w) = $this->_tableGetWidth($table, $i, $j);
  22674. }
  22675. list($y, $h) = $this->_tableGetHeight($table, $i, $j);
  22676. $x += $x0;
  22677. $y += $y0;
  22678. $y -= $returny;
  22679. if ($table['borders_separate']) {
  22680. if (!empty($tablefooter) || $i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) {
  22681. $extra = $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  22682. //$extra = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V']/2;
  22683. } else {
  22684. $extra = $table['border_spacing_V'] / 2;
  22685. }
  22686. } else {
  22687. $extra = $table['max_cell_border_width']['B'] / 2;
  22688. }
  22689. if ($j == $startcol && ((($y + $maxrowheight + $extra ) > ($pagetrigger + 0.001)) || (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && ($y + $maxrowheight + $tablefooterrowheight + $extra) > $pagetrigger) && ($this->tableLevel == 1 && $i < ($numrows - $table['headernrows']))) && ($y0 > 0 || $x0 > 0) && !$this->InFooter && $this->autoPageBreak) {
  22690. if (!$skippage) {
  22691. $finalSpread = true;
  22692. $firstSpread = true;
  22693. if ($split) {
  22694. for ($t = $startcol; $t < $numcols; $t++) {
  22695. // Are there more columns to print on a next page?
  22696. if ($table['colPg'][$t] > $splitpg) {
  22697. $finalSpread = false;
  22698. break;
  22699. }
  22700. }
  22701. if ($startcol > 0) {
  22702. $firstSpread = false;
  22703. }
  22704. }
  22705. if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
  22706. $this->y = $y;
  22707. $ya = $this->y;
  22708. $this->TableHeaderFooter($tablefooter, $tablestartpage, $tablestartcolumn, 'F', $level, $firstSpread, $finalSpread);
  22709. if ($this->table_rotate) {
  22710. $this->tbrot_h += $this->y - $ya;
  22711. }
  22712. $tablefooteradj = $this->y - $ya;
  22713. }
  22714. $y -= $y0;
  22715. $returny += $y;
  22716. $oldcolumn = $this->CurrCol;
  22717. if ($this->AcceptPageBreak()) {
  22718. $newpagestarted = true;
  22719. $this->y = $y + $y0;
  22720. // Move down to account for border-spacing or
  22721. // extra half border width in case page breaks in middle
  22722. if ($i > 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
  22723. if ($table['borders_separate']) {
  22724. $adv = $table['border_spacing_V'] / 2;
  22725. // If table footer
  22726. if (($this->keepColumns || !$this->ColActive) && !empty($tablefooter) && $i > 0) {
  22727. $adv += ($table['padding']['B'] + $table['border_details']['B']['w']);
  22728. }
  22729. } else {
  22730. $maxbwtop = 0;
  22731. $maxbwbottom = 0;
  22732. if (!$this->simpleTables) {
  22733. if (!empty($tablefooter)) {
  22734. $maxbwbottom = $table['max_cell_border_width']['B'];
  22735. } else {
  22736. $brow = $i - 1;
  22737. for ($ctj = 0; $ctj < $numcols; $ctj++) {
  22738. if (isset($cells[$brow][$ctj]) && $cells[$brow][$ctj]) {
  22739. if ($this->packTableData) {
  22740. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$brow][$ctj]['borderbin']);
  22741. } else {
  22742. $bb = $cells[$brow][$ctj]['border_details']['B']['w'];
  22743. }
  22744. $maxbwbottom = max($maxbwbottom, $bb);
  22745. }
  22746. }
  22747. }
  22748. if (!empty($tableheader)) {
  22749. $maxbwtop = $table['max_cell_border_width']['T'];
  22750. } else {
  22751. $trow = $i - 1;
  22752. for ($ctj = 0; $ctj < $numcols; $ctj++) {
  22753. if (isset($cells[$trow][$ctj]) && $cells[$trow][$ctj]) {
  22754. if ($this->packTableData) {
  22755. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$trow][$ctj]['borderbin']);
  22756. } else {
  22757. $bt = $cells[$trow][$ctj]['border_details']['T']['w'];
  22758. }
  22759. $maxbwtop = max($maxbwtop, $bt);
  22760. }
  22761. }
  22762. }
  22763. } elseif ($this->simpleTables) {
  22764. $maxbwtop = $table['simple']['border_details']['T']['w'];
  22765. $maxbwbottom = $table['simple']['border_details']['B']['w'];
  22766. }
  22767. $adv = $maxbwbottom / 2;
  22768. }
  22769. $this->y += $adv;
  22770. }
  22771. // Rotated table split over pages - needs this->y for borders/backgrounds
  22772. if ($i > 0 && $this->table_rotate && $level == 1) {
  22773. // $this->y = $y0 + $this->tbrot_w;
  22774. }
  22775. if ($this->tableClipPath) {
  22776. $this->_out("Q");
  22777. }
  22778. $bx = $x0;
  22779. $by = $y0;
  22780. if ($table['borders_separate']) {
  22781. $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
  22782. if ($tablestartpageno != $this->page) { // IF already broken across a previous pagebreak
  22783. $by += $table['max_cell_border_width']['T'] / 2;
  22784. if (empty($tableheader)) {
  22785. $by -= ($table['border_spacing_V'] / 2);
  22786. }
  22787. } else {
  22788. $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
  22789. }
  22790. } elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
  22791. $by += $maxbwtop / 2;
  22792. }
  22793. $by -= $tableheaderadj;
  22794. $bh = $this->y - $by + $tablefooteradj;
  22795. if (!$table['borders_separate']) {
  22796. $bh -= $adv;
  22797. }
  22798. if ($split) {
  22799. $bw = 0;
  22800. for ($t = $startcol; $t < $numcols; $t++) {
  22801. if ($table['colPg'][$t] == $splitpg) {
  22802. $bw += $table['wc'][$t];
  22803. }
  22804. if ($table['colPg'][$t] > $splitpg) {
  22805. break;
  22806. }
  22807. }
  22808. if ($table['borders_separate']) {
  22809. if ($firstSpread) {
  22810. $bw += $table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'];
  22811. } else {
  22812. $bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
  22813. $bw += $table['border_spacing_H'];
  22814. }
  22815. if ($finalSpread) {
  22816. $bw += $table['padding']['R'] + $table['border_details']['R']['w'] / 2 + $table['border_spacing_H'];
  22817. }
  22818. }
  22819. } else {
  22820. $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  22821. }
  22822. if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tablefooter) && $i > 0 && $table['border_details']['B']['w']) {
  22823. $prevDrawColor = $this->DrawColor;
  22824. $lw = $this->LineWidth;
  22825. $this->SetLineWidth($this->splitTableBorderWidth);
  22826. $this->SetDColor($table['border_details']['B']['c']);
  22827. $this->SetLineJoin(0);
  22828. $this->SetLineCap(0);
  22829. $blx = $bx;
  22830. $blw = $bw;
  22831. if (!$table['borders_separate']) {
  22832. $blx -= ($table['max_cell_border_width']['L'] / 2);
  22833. $blw += ($table['max_cell_border_width']['L'] / 2 + $table['max_cell_border_width']['R'] / 2);
  22834. }
  22835. $this->Line($blx, $this->y + ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y + ($this->splitTableBorderWidth / 2));
  22836. $this->DrawColor = $prevDrawColor;
  22837. $this->_out($this->DrawColor);
  22838. $this->SetLineWidth($lw);
  22839. $this->SetLineJoin(2);
  22840. $this->SetLineCap(2);
  22841. }
  22842. if (!$this->ColActive && ($i > 0 || $j > 0)) {
  22843. if (isset($table['bgcolor'][-1])) {
  22844. $color = $this->ConvertColor($table['bgcolor'][-1]);
  22845. if ($color) {
  22846. if (!$table['borders_separate']) {
  22847. $bh -= $table['max_cell_border_width']['B'] / 2;
  22848. }
  22849. $this->tableBackgrounds[$level * 9][] = array('gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color);
  22850. }
  22851. }
  22852. /* -- BACKGROUNDS -- */
  22853. if (isset($table['gradient'])) {
  22854. $g = $this->grad->parseBackgroundGradient($table['gradient']);
  22855. if ($g) {
  22856. $this->tableBackgrounds[$level * 9 + 1][] = array('gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  22857. }
  22858. }
  22859. if (isset($table['background-image'])) {
  22860. if ($table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
  22861. $g = $this->grad->parseMozGradient($table['background-image']['gradient']);
  22862. if ($g) {
  22863. $this->tableBackgrounds[$level * 9 + 1][] = array('gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  22864. }
  22865. } else {
  22866. $image_id = $table['background-image']['image_id'];
  22867. $orig_w = $table['background-image']['orig_w'];
  22868. $orig_h = $table['background-image']['orig_h'];
  22869. $x_pos = $table['background-image']['x_pos'];
  22870. $y_pos = $table['background-image']['y_pos'];
  22871. $x_repeat = $table['background-image']['x_repeat'];
  22872. $y_repeat = $table['background-image']['y_repeat'];
  22873. $resize = $table['background-image']['resize'];
  22874. $opacity = $table['background-image']['opacity'];
  22875. $itype = $table['background-image']['itype'];
  22876. $this->tableBackgrounds[$level * 9 + 2][] = array('x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  22877. }
  22878. }
  22879. /* -- END BACKGROUNDS -- */
  22880. }
  22881. // $this->AcceptPageBreak() has moved tablebuffer to $this->pages content
  22882. if ($this->tableBackgrounds) {
  22883. $s = $this->PrintTableBackgrounds();
  22884. if ($this->bufferoutput) {
  22885. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
  22886. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
  22887. } else {
  22888. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  22889. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
  22890. }
  22891. $this->tableBackgrounds = array();
  22892. }
  22893. if ($split) {
  22894. if ($i == 0 && $j == 0) {
  22895. $y0 = -1;
  22896. } elseif ($finalSpread) {
  22897. $splitpg = 0;
  22898. $startcol = 0;
  22899. $startrow = $i;
  22900. } else {
  22901. $splitpg++;
  22902. $startcol = $t;
  22903. $returny -= $y;
  22904. }
  22905. return array(false, $startrow, $startcol, $splitpg, $returny, $y0);
  22906. }
  22907. $this->AddPage($this->CurOrientation);
  22908. $this->_out('___TABLE___BACKGROUNDS' . $this->uniqstr);
  22909. if ($this->tableClipPath) {
  22910. $this->_out($this->tableClipPath);
  22911. }
  22912. // Added to correct for OddEven Margins
  22913. $x = $x + $this->MarginCorrection;
  22914. $x0 = $x0 + $this->MarginCorrection;
  22915. if ($this->splitTableBorderWidth && ($this->keepColumns || !$this->ColActive) && empty($tableheader) && $i > 0 && $table['border_details']['T']['w']) {
  22916. $prevDrawColor = $this->DrawColor;
  22917. $lw = $this->LineWidth;
  22918. $this->SetLineWidth($this->splitTableBorderWidth);
  22919. $this->SetDColor($table['border_details']['T']['c']);
  22920. $this->SetLineJoin(0);
  22921. $this->SetLineCap(0);
  22922. $blx += $this->MarginCorrection;
  22923. $this->Line($blx, $this->y - ($this->splitTableBorderWidth / 2), $blx + $blw, $this->y - ($this->splitTableBorderWidth / 2));
  22924. $this->DrawColor = $prevDrawColor;
  22925. $this->_out($this->DrawColor);
  22926. $this->SetLineWidth($lw);
  22927. $this->SetLineJoin(2);
  22928. $this->SetLineCap(2);
  22929. }
  22930. // Move down to account for half of top border-spacing or
  22931. // extra half border width in case page was broken in middle
  22932. if ($i > 0 && !$this->table_rotate && $level == 1 && $table['headernrows'] == 0) {
  22933. if ($table['borders_separate']) {
  22934. $adv = $table['border_spacing_V'] / 2;
  22935. } else {
  22936. $maxbwtop = 0;
  22937. for ($ctj = 0; $ctj < $numcols; $ctj++) {
  22938. if (isset($cells[$i][$ctj]) && $cells[$i][$ctj]) {
  22939. if (!$this->simpleTables) {
  22940. if ($this->packTableData) {
  22941. list($bt, $br, $bb, $bl) = $this->_getBorderWidths($cells[$i][$ctj]['borderbin']);
  22942. } else {
  22943. $bt = $cells[$i][$ctj]['border_details']['T']['w'];
  22944. }
  22945. $maxbwtop = max($maxbwtop, $bt);
  22946. } elseif ($this->simpleTables) {
  22947. $maxbwtop = max($maxbwtop, $table['simple']['border_details']['T']['w']);
  22948. }
  22949. }
  22950. }
  22951. $adv = $maxbwtop / 2;
  22952. }
  22953. $this->y += $adv;
  22954. }
  22955. if ($this->table_rotate) {
  22956. $this->tbrot_x0 = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['padding_left'] + $this->blk[$this->blklvl]['border_left']['w'];
  22957. if ($table['borders_separate']) {
  22958. $this->tbrot_h = $table['margin']['T'] + $table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2;
  22959. } else {
  22960. $this->tbrot_h = $table['margin']['T'] + $table['max_cell_border_width']['T'];
  22961. }
  22962. $this->tbrot_y0 = $this->y;
  22963. $pagetrigger = $y0 - $tableheaderadj + ($this->blk[$this->blklvl]['inner_width']);
  22964. } else {
  22965. $pagetrigger = $this->PageBreakTrigger;
  22966. }
  22967. if ($this->kwt_saved && $level == 1) {
  22968. $this->kwt_moved = true;
  22969. }
  22970. if (!empty($tableheader)) {
  22971. $ya = $this->y;
  22972. $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
  22973. if ($this->table_rotate) {
  22974. $this->tbrot_h = $this->y - $ya;
  22975. }
  22976. $tableheaderadj = $this->y - $ya;
  22977. } elseif ($i == 0 && !$this->table_rotate && $level == 1 && !$this->ColActive) {
  22978. // Advance down page
  22979. if ($table['borders_separate']) {
  22980. $adv = $table['border_spacing_V'] / 2 + $table['border_details']['T']['w'] + $table['padding']['T'];
  22981. } else {
  22982. $adv = $table['max_cell_border_width']['T'] / 2;
  22983. }
  22984. if ($adv) {
  22985. if ($this->table_rotate) {
  22986. $this->y += ($adv);
  22987. } else {
  22988. $this->DivLn($adv, $this->blklvl, true);
  22989. }
  22990. }
  22991. }
  22992. $outerfilled = 0;
  22993. $y = $y0 = $this->y;
  22994. }
  22995. /* -- COLUMNS -- */
  22996. // COLS
  22997. // COLUMN CHANGE
  22998. if ($this->CurrCol != $oldcolumn) {
  22999. // Added to correct for Columns
  23000. $x += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  23001. $x0 += $this->ChangeColumn * ($this->ColWidth + $this->ColGap);
  23002. if ($this->CurrCol == 0) { // just added a page - possibly with tableheader
  23003. $y0 = $this->y; // this->y0 is global used by Columns - $y0 is internal to tablewrite
  23004. } else {
  23005. $y0 = $this->y0; // this->y0 is global used by Columns - $y0 is internal to tablewrite
  23006. }
  23007. $y = $y0;
  23008. $outerfilled = 0;
  23009. if ($this->CurrCol != 0 && ($this->keepColumns && $this->ColActive) && !empty($tableheader) && $i > 0) {
  23010. $this->x = $x;
  23011. $this->y = $y;
  23012. $this->TableHeaderFooter($tableheader, $tablestartpage, $tablestartcolumn, 'H', $level);
  23013. $y0 = $y = $this->y;
  23014. }
  23015. }
  23016. /* -- END COLUMNS -- */
  23017. }
  23018. $skippage = true;
  23019. }
  23020. $this->x = $x;
  23021. $this->y = $y;
  23022. if ($this->kwt_saved && $level == 1) {
  23023. $this->printkwtbuffer();
  23024. $x0 = $x = $this->x;
  23025. $y0 = $y = $this->y;
  23026. $this->kwt_moved = false;
  23027. $this->kwt_saved = false;
  23028. }
  23029. // Set the Page & Column where table actually starts
  23030. if ($i == 0 && $j == 0 && $level == 1) {
  23031. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  23032. $tablestartpage = 'EVEN';
  23033. } elseif (($this->mirrorMargins) && (($this->page) % 2 == 1)) { // ODD
  23034. $tablestartpage = 'ODD';
  23035. } else {
  23036. $tablestartpage = '';
  23037. }
  23038. $tablestartpageno = $this->page;
  23039. if ($this->ColActive) {
  23040. $tablestartcolumn = $this->CurrCol;
  23041. } // *COLUMNS*
  23042. }
  23043. //ALIGN
  23044. $align = $cell['a'];
  23045. /* -- COLUMNS -- */
  23046. // If outside columns, this is done in PaintDivBB
  23047. if ($this->ColActive) {
  23048. //OUTER FILL BGCOLOR of DIVS
  23049. if ($this->blklvl > 0 && ($j == 0) && !$this->table_rotate && $level == 1) {
  23050. $firstblockfill = $this->GetFirstBlockFill();
  23051. if ($firstblockfill && $this->blklvl >= $firstblockfill) {
  23052. $divh = $maxrowheight;
  23053. // Last row
  23054. if ((!isset($cell['rowspan']) && $i == $numrows - 1) || (isset($cell['rowspan']) && (($i == $numrows - 1 && $cell['rowspan'] < 2) || ($cell['rowspan'] > 1 && ($i + $cell['rowspan'] - 1) == $numrows - 1)))) {
  23055. if ($table['borders_separate']) {
  23056. $adv = $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  23057. } else {
  23058. $adv = $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
  23059. }
  23060. $divh += $adv; //last row: fill bottom half of bottom border (y advanced at end)
  23061. }
  23062. if (($this->y + $divh) > $outerfilled) { // if not already painted by previous rowspan
  23063. $bak_x = $this->x;
  23064. $bak_y = $this->y;
  23065. if ($outerfilled > $this->y) {
  23066. $divh = ($this->y + $divh) - $outerfilled;
  23067. $this->y = $outerfilled;
  23068. }
  23069. $this->DivLn($divh, -3, false);
  23070. $outerfilled = $this->y + $divh;
  23071. // Reset current block fill
  23072. $bcor = $this->blk[$this->blklvl]['bgcolorarray'];
  23073. if ($bcor)
  23074. $this->SetFColor($bcor);
  23075. $this->x = $bak_x;
  23076. $this->y = $bak_y;
  23077. }
  23078. }
  23079. }
  23080. }
  23081. //TABLE BACKGROUND FILL BGCOLOR - for cellSpacing
  23082. if ($this->ColActive) {
  23083. if ($table['borders_separate']) {
  23084. $fill = isset($table['bgcolor'][-1]) ? $table['bgcolor'][-1] : 0;
  23085. if ($fill) {
  23086. $color = $this->ConvertColor($fill);
  23087. if ($color) {
  23088. $xadj = ($table['border_spacing_H'] / 2);
  23089. $yadj = ($table['border_spacing_V'] / 2);
  23090. $wadj = $table['border_spacing_H'];
  23091. $hadj = $table['border_spacing_V'];
  23092. if ($i == 0) { // Top
  23093. $yadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  23094. $hadj += $table['padding']['T'] + $table['border_details']['T']['w'];
  23095. }
  23096. if ($j == 0) { // Left
  23097. $xadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  23098. $wadj += $table['padding']['L'] + $table['border_details']['L']['w'];
  23099. }
  23100. if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows) || (!isset($cell['rowspan']) && ($i + 1) == $numrows)) { // Bottom
  23101. $hadj += $table['padding']['B'] + $table['border_details']['B']['w'];
  23102. }
  23103. if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols) || (!isset($cell['colspan']) && ($j + 1) == $numcols)) { // Right
  23104. $wadj += $table['padding']['R'] + $table['border_details']['R']['w'];
  23105. }
  23106. $this->SetFColor($color);
  23107. $this->Rect($x - $xadj, $y - $yadj, $w + $wadj, $h + $hadj, 'F');
  23108. }
  23109. }
  23110. }
  23111. }
  23112. /* -- END COLUMNS -- */
  23113. if ($table['empty_cells'] != 'hide' || !empty($cell['textbuffer']) || (isset($cell['nestedcontent']) && $cell['nestedcontent']) || !$table['borders_separate']) {
  23114. $paintcell = true;
  23115. } else {
  23116. $paintcell = false;
  23117. }
  23118. //Set Borders
  23119. $bord = 0;
  23120. $bord_det = array();
  23121. if (!$this->simpleTables) {
  23122. if ($this->packTableData) {
  23123. $c = $this->_unpackCellBorder($cell['borderbin']);
  23124. $bord = $c['border'];
  23125. $bord_det = $c['border_details'];
  23126. } else {
  23127. $bord = $cell['border'];
  23128. $bord_det = $cell['border_details'];
  23129. }
  23130. } elseif ($this->simpleTables) {
  23131. $bord = $table['simple']['border'];
  23132. $bord_det = $table['simple']['border_details'];
  23133. }
  23134. //TABLE ROW OR CELL FILL BGCOLOR
  23135. $fill = 0;
  23136. if (isset($cell['bgcolor']) && $cell['bgcolor'] && $cell['bgcolor'] != 'transparent') {
  23137. $fill = $cell['bgcolor'];
  23138. $leveladj = 6;
  23139. } elseif (isset($table['bgcolor'][$i]) && $table['bgcolor'][$i] && $table['bgcolor'][$i] != 'transparent') { // Row color
  23140. $fill = $table['bgcolor'][$i];
  23141. $leveladj = 3;
  23142. }
  23143. if ($fill && $paintcell) {
  23144. $color = $this->ConvertColor($fill);
  23145. if ($color) {
  23146. if ($table['borders_separate']) {
  23147. if ($this->ColActive) {
  23148. $this->SetFColor($color);
  23149. $this->Rect($x + ($table['border_spacing_H'] / 2), $y + ($table['border_spacing_V'] / 2), $w - $table['border_spacing_H'], $h - $table['border_spacing_V'], 'F');
  23150. } else {
  23151. $this->tableBackgrounds[$level * 9 + $leveladj][] = array('gradient' => false, 'x' => ($x + ($table['border_spacing_H'] / 2)), 'y' => ($y + ($table['border_spacing_V'] / 2)), 'w' => ($w - $table['border_spacing_H']), 'h' => ($h - $table['border_spacing_V']), 'col' => $color);
  23152. }
  23153. } else {
  23154. if ($this->ColActive) {
  23155. $this->SetFColor($color);
  23156. $this->Rect($x, $y, $w, $h, 'F');
  23157. } else {
  23158. $this->tableBackgrounds[$level * 9 + $leveladj][] = array('gradient' => false, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'col' => $color);
  23159. }
  23160. }
  23161. }
  23162. }
  23163. /* -- BACKGROUNDS -- */
  23164. if (isset($cell['gradient']) && $cell['gradient'] && $paintcell) {
  23165. $g = $this->grad->parseBackgroundGradient($cell['gradient']);
  23166. if ($g) {
  23167. if ($table['borders_separate']) {
  23168. $px = $x + ($table['border_spacing_H'] / 2);
  23169. $py = $y + ($table['border_spacing_V'] / 2);
  23170. $pw = $w - $table['border_spacing_H'];
  23171. $ph = $h - $table['border_spacing_V'];
  23172. } else {
  23173. $px = $x;
  23174. $py = $y;
  23175. $pw = $w;
  23176. $ph = $h;
  23177. }
  23178. if ($this->ColActive) {
  23179. $this->grad->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  23180. } else {
  23181. $this->tableBackgrounds[$level * 9 + 7][] = array('gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  23182. }
  23183. }
  23184. }
  23185. if (isset($cell['background-image']) && $paintcell) {
  23186. if (isset($cell['background-image']['gradient']) && $cell['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $cell['background-image']['gradient'])) {
  23187. $g = $this->grad->parseMozGradient($cell['background-image']['gradient']);
  23188. if ($g) {
  23189. if ($table['borders_separate']) {
  23190. $px = $x + ($table['border_spacing_H'] / 2);
  23191. $py = $y + ($table['border_spacing_V'] / 2);
  23192. $pw = $w - $table['border_spacing_H'];
  23193. $ph = $h - $table['border_spacing_V'];
  23194. } else {
  23195. $px = $x;
  23196. $py = $y;
  23197. $pw = $w;
  23198. $ph = $h;
  23199. }
  23200. if ($this->ColActive) {
  23201. $this->grad->Gradient($px, $py, $pw, $ph, $g['type'], $g['stops'], $g['colorspace'], $g['coords'], $g['extend']);
  23202. } else {
  23203. $this->tableBackgrounds[$level * 9 + 7][] = array('gradient' => true, 'x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  23204. }
  23205. }
  23206. } elseif (isset($cell['background-image']['image_id']) && $cell['background-image']['image_id']) { // Background pattern
  23207. $n = count($this->patterns) + 1;
  23208. if ($table['borders_separate']) {
  23209. $px = $x + ($table['border_spacing_H'] / 2);
  23210. $py = $y + ($table['border_spacing_V'] / 2);
  23211. $pw = $w - $table['border_spacing_H'];
  23212. $ph = $h - $table['border_spacing_V'];
  23213. } else {
  23214. $px = $x;
  23215. $py = $y;
  23216. $pw = $w;
  23217. $ph = $h;
  23218. }
  23219. if ($this->ColActive) {
  23220. list($orig_w, $orig_h, $x_repeat, $y_repeat) = $this->_resizeBackgroundImage($cell['background-image']['orig_w'], $cell['background-image']['orig_h'], $pw, $ph, $cell['background-image']['resize'], $cell['background-image']['x_repeat'], $cell['background-image']['y_repeat']);
  23221. $this->patterns[$n] = array('x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'pgh' => $this->h, 'image_id' => $cell['background-image']['image_id'], 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $cell['background-image']['x_pos'], 'y_pos' => $cell['background-image']['y_pos'], 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat);
  23222. if ($cell['background-image']['opacity'] > 0 && $cell['background-image']['opacity'] < 1) {
  23223. $opac = $this->SetAlpha($cell['background-image']['opacity'], 'Normal', true);
  23224. } else {
  23225. $opac = '';
  23226. }
  23227. $this->_out(sprintf('q /Pattern cs /P%d scn %s %.3F %.3F %.3F %.3F re f Q', $n, $opac, $px * _MPDFK, ($this->h - $py) * _MPDFK, $pw * _MPDFK, -$ph * _MPDFK));
  23228. } else {
  23229. $image_id = $cell['background-image']['image_id'];
  23230. $orig_w = $cell['background-image']['orig_w'];
  23231. $orig_h = $cell['background-image']['orig_h'];
  23232. $x_pos = $cell['background-image']['x_pos'];
  23233. $y_pos = $cell['background-image']['y_pos'];
  23234. $x_repeat = $cell['background-image']['x_repeat'];
  23235. $y_repeat = $cell['background-image']['y_repeat'];
  23236. $resize = $cell['background-image']['resize'];
  23237. $opacity = $cell['background-image']['opacity'];
  23238. $itype = $cell['background-image']['itype'];
  23239. $this->tableBackgrounds[$level * 9 + 8][] = array('x' => $px, 'y' => $py, 'w' => $pw, 'h' => $ph, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  23240. }
  23241. }
  23242. }
  23243. /* -- END BACKGROUNDS -- */
  23244. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  23245. $ccolsp = $cell['colspan'];
  23246. } else {
  23247. $ccolsp = 1;
  23248. }
  23249. if (isset($cell['rowspan']) && $cell['rowspan'] > 1) {
  23250. $crowsp = $cell['rowspan'];
  23251. } else {
  23252. $crowsp = 1;
  23253. }
  23254. // but still need to do this for repeated headers...
  23255. if (!$table['borders_separate'] && $this->tabletheadjustfinished && !$this->simpleTables) {
  23256. if (isset($table['topntail']) && $table['topntail']) {
  23257. $bord_det['T'] = $this->border_details($table['topntail']);
  23258. $bord_det['T']['w'] /= $this->shrin_k;
  23259. $this->setBorder($bord, _BORDER_TOP);
  23260. }
  23261. if (isset($table['thead-underline']) && $table['thead-underline']) {
  23262. $bord_det['T'] = $this->border_details($table['thead-underline']);
  23263. $bord_det['T']['w'] /= $this->shrin_k;
  23264. $this->setBorder($bord, _BORDER_TOP);
  23265. }
  23266. }
  23267. //Get info of first row ==>> table header
  23268. //Use > 1 row if THEAD
  23269. if (isset($table['is_thead'][$i]) && $table['is_thead'][$i] && $level == 1) {
  23270. if ($j == 0)
  23271. $tableheaderrowheight += $table['hr'][$i];
  23272. $tableheader[$i][0]['trbackground-images'] = (isset($table['trbackground-images'][$i]) ? $table['trbackground-images'][$i] : null);
  23273. $tableheader[$i][0]['trgradients'] = (isset($table['trgradients'][$i]) ? $table['trgradients'][$i] : null);
  23274. $tableheader[$i][0]['trbgcolor'] = (isset($table['bgcolor'][$i]) ? $table['bgcolor'][$i] : null);
  23275. $tableheader[$i][$j]['x'] = $x;
  23276. $tableheader[$i][$j]['y'] = $y;
  23277. $tableheader[$i][$j]['h'] = $h;
  23278. $tableheader[$i][$j]['w'] = $w;
  23279. if (isset($cell['textbuffer'])) {
  23280. $tableheader[$i][$j]['textbuffer'] = $cell['textbuffer'];
  23281. } else {
  23282. $tableheader[$i][$j]['textbuffer'] = '';
  23283. }
  23284. $tableheader[$i][$j]['a'] = $cell['a'];
  23285. $tableheader[$i][$j]['R'] = $cell['R'];
  23286. $tableheader[$i][$j]['va'] = $cell['va'];
  23287. $tableheader[$i][$j]['mih'] = $cell['mih'];
  23288. $tableheader[$i][$j]['gradient'] = (isset($cell['gradient']) ? $cell['gradient'] : null); // *BACKGROUNDS*
  23289. $tableheader[$i][$j]['background-image'] = (isset($cell['background-image']) ? $cell['background-image'] : null); // *BACKGROUNDS*
  23290. $tableheader[$i][$j]['rowspan'] = (isset($cell['rowspan']) ? $cell['rowspan'] : null);
  23291. $tableheader[$i][$j]['colspan'] = (isset($cell['colspan']) ? $cell['colspan'] : null);
  23292. $tableheader[$i][$j]['bgcolor'] = $cell['bgcolor'];
  23293. if (!$this->simpleTables) {
  23294. $tableheader[$i][$j]['border'] = $bord;
  23295. $tableheader[$i][$j]['border_details'] = $bord_det;
  23296. } elseif ($this->simpleTables) {
  23297. $tableheader[$i][$j]['border'] = $table['simple']['border'];
  23298. $tableheader[$i][$j]['border_details'] = $table['simple']['border_details'];
  23299. }
  23300. $tableheader[$i][$j]['padding'] = $cell['padding'];
  23301. if (isset($cell['direction']))
  23302. $tableheader[$i][$j]['direction'] = $cell['direction'];
  23303. if (isset($cell['cellLineHeight']))
  23304. $tableheader[$i][$j]['cellLineHeight'] = $cell['cellLineHeight'];
  23305. if (isset($cell['cellLineStackingStrategy']))
  23306. $tableheader[$i][$j]['cellLineStackingStrategy'] = $cell['cellLineStackingStrategy'];
  23307. if (isset($cell['cellLineStackingShift']))
  23308. $tableheader[$i][$j]['cellLineStackingShift'] = $cell['cellLineStackingShift'];
  23309. }
  23310. // CELL BORDER
  23311. if ($bord) {
  23312. if ($table['borders_separate'] && $paintcell) {
  23313. $this->_tableRect($x + ($table['border_spacing_H'] / 2) + ($bord_det['L']['w'] / 2), $y + ($table['border_spacing_V'] / 2) + ($bord_det['T']['w'] / 2), $w - $table['border_spacing_H'] - ($bord_det['L']['w'] / 2) - ($bord_det['R']['w'] / 2), $h - $table['border_spacing_V'] - ($bord_det['T']['w'] / 2) - ($bord_det['B']['w'] / 2), $bord, $bord_det, false, $table['borders_separate']);
  23314. } elseif (!$table['borders_separate']) {
  23315. $this->_tableRect($x, $y, $w, $h, $bord, $bord_det, true, $table['borders_separate']); // true causes buffer
  23316. }
  23317. }
  23318. //VERTICAL ALIGN
  23319. if ($cell['R'] && INTVAL($cell['R']) > 0 && INTVAL($cell['R']) < 90 && isset($cell['va']) && $cell['va'] != 'B') {
  23320. $cell['va'] = 'B';
  23321. }
  23322. if (!isset($cell['va']) || $cell['va'] == 'M')
  23323. $this->y += ($h - $cell['mih']) / 2;
  23324. elseif (isset($cell['va']) && $cell['va'] == 'B')
  23325. $this->y += $h - $cell['mih'];
  23326. // NESTED CONTENT
  23327. // TEXT (and nested tables)
  23328. $this->divwidth = $w;
  23329. if (!empty($cell['textbuffer'])) {
  23330. $this->cellTextAlign = $align;
  23331. $this->cellLineHeight = $cell['cellLineHeight'];
  23332. $this->cellLineStackingStrategy = $cell['cellLineStackingStrategy'];
  23333. $this->cellLineStackingShift = $cell['cellLineStackingShift'];
  23334. if ($level == 1) {
  23335. if (isset($table['is_tfoot'][$i]) && $table['is_tfoot'][$i]) {
  23336. if (preg_match('/{colsum([0-9]*)[_]*}/', $cell['textbuffer'][0][0], $m)) {
  23337. $rep = sprintf("%01." . intval($m[1]) . "f", $this->colsums[$j]);
  23338. $cell['textbuffer'][0][0] = preg_replace('/{colsum[0-9_]*}/', $rep, $cell['textbuffer'][0][0]);
  23339. }
  23340. } elseif (!isset($table['is_thead'][$i])) {
  23341. if (isset($this->colsums[$j])) {
  23342. $this->colsums[$j] += floatval(preg_replace('/^[^0-9\.\,]*/', '', $cell['textbuffer'][0][0]));
  23343. } else {
  23344. $this->colsums[$j] = floatval(preg_replace('/^[^0-9\.\,]*/', '', $cell['textbuffer'][0][0]));
  23345. }
  23346. }
  23347. }
  23348. $opy = $this->y;
  23349. // mPDF ITERATION
  23350. if ($this->iterationCounter) {
  23351. foreach ($cell['textbuffer'] AS $k => $t) {
  23352. if (preg_match('/{iteration ([a-zA-Z0-9_]+)}/', $t[0], $m)) {
  23353. $vname = '__' . $m[1] . '_';
  23354. if (!isset($this->$vname)) {
  23355. $this->$vname = 1;
  23356. } else {
  23357. $this->$vname++;
  23358. }
  23359. $cell['textbuffer'][$k][0] = preg_replace('/{iteration ' . $m[1] . '}/', $this->$vname, $cell['textbuffer'][$k][0]);
  23360. }
  23361. }
  23362. }
  23363. if ($cell['R']) {
  23364. $cellPtSize = $cell['textbuffer'][0][11] / $this->shrin_k;
  23365. if (!$cellPtSize) {
  23366. $cellPtSize = $this->default_font_size;
  23367. }
  23368. $cellFontHeight = ($cellPtSize / _MPDFK);
  23369. $opx = $this->x;
  23370. $angle = INTVAL($cell['R']);
  23371. // Only allow 45 to 89 degrees (when bottom-aligned) or exactly 90 or -90
  23372. if ($angle > 90) {
  23373. $angle = 90;
  23374. } elseif ($angle > 0 && $angle < 45) {
  23375. $angle = 45;
  23376. } elseif ($angle < 0) {
  23377. $angle = -90;
  23378. }
  23379. $offset = ((sin(deg2rad($angle))) * 0.37 * $cellFontHeight);
  23380. if (isset($cell['a']) && $cell['a'] == 'R') {
  23381. $this->x += ($w) + ($offset) - ($cellFontHeight / 3) - ($cell['padding']['R'] + ($table['border_spacing_H'] / 2));
  23382. } elseif (!isset($cell['a']) || $cell['a'] == 'C') {
  23383. $this->x += ($w / 2) + ($offset);
  23384. } else {
  23385. $this->x += ($offset) + ($cellFontHeight / 3) + ($cell['padding']['L'] + ($table['border_spacing_H'] / 2));
  23386. }
  23387. $str = '';
  23388. foreach ($cell['textbuffer'] AS $t) {
  23389. $str .= $t[0] . ' ';
  23390. }
  23391. $str = rtrim($str);
  23392. if (!isset($cell['va']) || $cell['va'] == 'M') {
  23393. $this->y -= ($h - $cell['mih']) / 2; //Undo what was added earlier VERTICAL ALIGN
  23394. if ($angle > 0) {
  23395. $this->y += (($h - $cell['mih']) / 2) + $cell['padding']['T'] + ($cell['mih'] - ($cell['padding']['T'] + $cell['padding']['B']));
  23396. } elseif ($angle < 0) {
  23397. $this->y += (($h - $cell['mih']) / 2) + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
  23398. }
  23399. } elseif (isset($cell['va']) && $cell['va'] == 'B') {
  23400. $this->y -= $h - $cell['mih']; //Undo what was added earlier VERTICAL ALIGN
  23401. if ($angle > 0) {
  23402. $this->y += $h - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
  23403. } elseif ($angle < 0) {
  23404. $this->y += $h - $cell['mih'] + ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
  23405. }
  23406. } elseif (isset($cell['va']) && $cell['va'] == 'T') {
  23407. if ($angle > 0) {
  23408. $this->y += $cell['mih'] - ($cell['padding']['B'] + ($table['border_spacing_V'] / 2));
  23409. } elseif ($angle < 0) {
  23410. $this->y += ($cell['padding']['T'] + ($table['border_spacing_V'] / 2));
  23411. }
  23412. }
  23413. $this->Rotate($angle, $this->x, $this->y);
  23414. $s_fs = $this->FontSizePt;
  23415. $s_f = $this->FontFamily;
  23416. $s_st = $this->FontStyle;
  23417. if (!empty($cell['textbuffer'][0][3])) { //Font Color
  23418. $cor = $cell['textbuffer'][0][3];
  23419. $this->SetTColor($cor);
  23420. }
  23421. $this->SetFont($cell['textbuffer'][0][4], $cell['textbuffer'][0][2], $cellPtSize, true, true);
  23422. $this->magic_reverse_dir($str, $this->directionality, $cell['textbuffer'][0][18]);
  23423. $this->Text($this->x, $this->y, $str, $cell['textbuffer'][0][18], $cell['textbuffer'][0][8]); // textvar
  23424. $this->Rotate(0);
  23425. $this->SetFont($s_f, $s_st, $s_fs, true, true);
  23426. $this->SetTColor(0);
  23427. $this->x = $opx;
  23428. } else {
  23429. if (!$this->simpleTables) {
  23430. if ($bord_det) {
  23431. $btlw = $bord_det['L']['w'];
  23432. $btrw = $bord_det['R']['w'];
  23433. $bttw = $bord_det['T']['w'];
  23434. } else {
  23435. $btlw = 0;
  23436. $btrw = 0;
  23437. $bttw = 0;
  23438. }
  23439. if ($table['borders_separate']) {
  23440. $xadj = $btlw + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
  23441. $wadj = $btlw + $btrw + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
  23442. $yadj = $bttw + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
  23443. } else {
  23444. $xadj = $btlw / 2 + $cell['padding']['L'];
  23445. $wadj = ($btlw + $btrw) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
  23446. $yadj = $bttw / 2 + $cell['padding']['T'];
  23447. }
  23448. } elseif ($this->simpleTables) {
  23449. if ($table['borders_separate']) { // NB twice border width
  23450. $xadj = $table['simple']['border_details']['L']['w'] + $cell['padding']['L'] + ($table['border_spacing_H'] / 2);
  23451. $wadj = $table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w'] + $cell['padding']['L'] + $cell['padding']['R'] + $table['border_spacing_H'];
  23452. $yadj = $table['simple']['border_details']['T']['w'] + $cell['padding']['T'] + ($table['border_spacing_H'] / 2);
  23453. } else {
  23454. $xadj = $table['simple']['border_details']['L']['w'] / 2 + $cell['padding']['L'];
  23455. $wadj = ($table['simple']['border_details']['L']['w'] + $table['simple']['border_details']['R']['w']) / 2 + $cell['padding']['L'] + $cell['padding']['R'];
  23456. $yadj = $table['simple']['border_details']['T']['w'] / 2 + $cell['padding']['T'];
  23457. }
  23458. }
  23459. $this->decimal_offset = 0;
  23460. if (substr($cell['a'], 0, 1) == 'D') {
  23461. if (isset($cell['colspan']) && $cell['colspan'] > 1) {
  23462. $this->cellTextAlign = $c['a'] = substr($cell['a'], 2, 1);
  23463. } else {
  23464. $smax = $table['decimal_align'][$j]['maxs0'];
  23465. $d_content = $table['decimal_align'][$j]['maxs0'] + $table['decimal_align'][$j]['maxs1'];
  23466. $this->decimal_offset = $smax;
  23467. $extra = ($w - $d_content - $wadj);
  23468. if ($extra > 0) {
  23469. if (substr($cell['a'], 2, 1) == 'R') {
  23470. $this->decimal_offset += $extra;
  23471. } elseif (substr($cell['a'], 2, 1) == 'C') {
  23472. $this->decimal_offset += ($extra) / 2;
  23473. }
  23474. }
  23475. }
  23476. }
  23477. $this->divwidth = $w - $wadj;
  23478. if ($this->divwidth == 0) {
  23479. $this->divwidth = 0.0001;
  23480. }
  23481. $this->x += $xadj;
  23482. $this->y += $yadj;
  23483. $this->printbuffer($cell['textbuffer'], '', true, false, $cell['direction']);
  23484. }
  23485. $this->y = $opy;
  23486. }
  23487. /* -- BACKGROUNDS -- */
  23488. if (!$this->ColActive) {
  23489. if (isset($table['trgradients'][$i]) && ($j == 0 || $table['borders_separate'])) {
  23490. $g = $this->grad->parseBackgroundGradient($table['trgradients'][$i]);
  23491. if ($g) {
  23492. $gx = $x0;
  23493. $gy = $y;
  23494. $gh = $h;
  23495. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  23496. if ($table['borders_separate']) {
  23497. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  23498. $clx = $x + ($table['border_spacing_H'] / 2);
  23499. $cly = $y + ($table['border_spacing_V'] / 2);
  23500. $clw = $w - $table['border_spacing_H'];
  23501. $clh = $h - $table['border_spacing_V'];
  23502. // Set clipping path
  23503. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  23504. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s);
  23505. } else {
  23506. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  23507. }
  23508. }
  23509. }
  23510. if (isset($table['trbackground-images'][$i]) && ($j == 0 || $table['borders_separate'])) {
  23511. if (isset($table['trbackground-images'][$i]['gradient']) && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['trbackground-images'][$i]['gradient'])) {
  23512. $g = $this->grad->parseMozGradient($table['trbackground-images'][$i]['gradient']);
  23513. if ($g) {
  23514. $gx = $x0;
  23515. $gy = $y;
  23516. $gh = $h;
  23517. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  23518. if ($table['borders_separate']) {
  23519. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  23520. $clx = $x + ($table['border_spacing_H'] / 2);
  23521. $cly = $y + ($table['border_spacing_V'] / 2);
  23522. $clw = $w - $table['border_spacing_H'];
  23523. $clh = $h - $table['border_spacing_V'];
  23524. // Set clipping path
  23525. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  23526. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => $s);
  23527. } else {
  23528. $this->tableBackgrounds[$level * 9 + 4][] = array('gradient' => true, 'x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  23529. }
  23530. }
  23531. } else {
  23532. $image_id = $table['trbackground-images'][$i]['image_id'];
  23533. $orig_w = $table['trbackground-images'][$i]['orig_w'];
  23534. $orig_h = $table['trbackground-images'][$i]['orig_h'];
  23535. $x_pos = $table['trbackground-images'][$i]['x_pos'];
  23536. $y_pos = $table['trbackground-images'][$i]['y_pos'];
  23537. $x_repeat = $table['trbackground-images'][$i]['x_repeat'];
  23538. $y_repeat = $table['trbackground-images'][$i]['y_repeat'];
  23539. $resize = $table['trbackground-images'][$i]['resize'];
  23540. $opacity = $table['trbackground-images'][$i]['opacity'];
  23541. $itype = $table['trbackground-images'][$i]['itype'];
  23542. $clippath = '';
  23543. $gx = $x0;
  23544. $gy = $y;
  23545. $gh = $h;
  23546. $gw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  23547. if ($table['borders_separate']) {
  23548. $gw -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['padding']['R'] + $table['border_details']['R']['w'] + $table['border_spacing_H']);
  23549. $clx = $x + ($table['border_spacing_H'] / 2);
  23550. $cly = $y + ($table['border_spacing_V'] / 2);
  23551. $clw = $w - $table['border_spacing_H'];
  23552. $clh = $h - $table['border_spacing_V'];
  23553. // Set clipping path
  23554. $s = $this->_setClippingPath($clx, $cly, $clw, $clh); // mPDF 6
  23555. $this->tableBackgrounds[$level * 9 + 5][] = array('x' => $gx + ($table['border_spacing_H'] / 2), 'y' => $gy + ($table['border_spacing_V'] / 2), 'w' => $gw - $table['border_spacing_V'], 'h' => $gh - $table['border_spacing_H'], 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => $s, 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  23556. } else {
  23557. $this->tableBackgrounds[$level * 9 + 5][] = array('x' => $gx, 'y' => $gy, 'w' => $gw, 'h' => $gh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  23558. }
  23559. }
  23560. }
  23561. }
  23562. /* -- END BACKGROUNDS -- */
  23563. // TABLE BORDER - if separate
  23564. if (($table['borders_separate'] || ($this->simpleTables && !$table['simple']['border'])) && $table['border']) {
  23565. $halfspaceL = $table['padding']['L'] + ($table['border_spacing_H'] / 2);
  23566. $halfspaceR = $table['padding']['R'] + ($table['border_spacing_H'] / 2);
  23567. $halfspaceT = $table['padding']['T'] + ($table['border_spacing_V'] / 2);
  23568. $halfspaceB = $table['padding']['B'] + ($table['border_spacing_V'] / 2);
  23569. $tbx = $x;
  23570. $tby = $y;
  23571. $tbw = $w;
  23572. $tbh = $h;
  23573. $tab_bord = 0;
  23574. $corner = '';
  23575. if ($i == 0) { // Top
  23576. $tby -= $halfspaceT + ($table['border_details']['T']['w'] / 2);
  23577. $tbh += $halfspaceT + ($table['border_details']['T']['w'] / 2);
  23578. $this->setBorder($tab_bord, _BORDER_TOP);
  23579. $corner .= 'T';
  23580. }
  23581. if ($i == ($numrows - 1) || (isset($cell['rowspan']) && ($i + $cell['rowspan']) == $numrows)) { // Bottom
  23582. $tbh += $halfspaceB + ($table['border_details']['B']['w'] / 2);
  23583. $this->setBorder($tab_bord, _BORDER_BOTTOM);
  23584. $corner .= 'B';
  23585. }
  23586. if ($j == 0) { // Left
  23587. $tbx -= $halfspaceL + ($table['border_details']['L']['w'] / 2);
  23588. $tbw += $halfspaceL + ($table['border_details']['L']['w'] / 2);
  23589. $this->setBorder($tab_bord, _BORDER_LEFT);
  23590. $corner .= 'L';
  23591. }
  23592. if ($j == ($numcols - 1) || (isset($cell['colspan']) && ($j + $cell['colspan']) == $numcols)) { // Right
  23593. $tbw += $halfspaceR + ($table['border_details']['R']['w'] / 2);
  23594. $this->setBorder($tab_bord, _BORDER_RIGHT);
  23595. $corner .= 'R';
  23596. }
  23597. $this->_tableRect($tbx, $tby, $tbw, $tbh, $tab_bord, $table['border_details'], false, $table['borders_separate'], 'table', $corner, $table['border_spacing_V'], $table['border_spacing_H']);
  23598. }
  23599. unset($cell);
  23600. //Reset values
  23601. $this->Reset();
  23602. }//end of (if isset(cells)...)
  23603. }// end of columns
  23604. $newpagestarted = false;
  23605. $this->tabletheadjustfinished = false;
  23606. /* -- COLUMNS -- */
  23607. if ($this->ColActive) {
  23608. if (!$this->table_keep_together && $i < $numrows - 1 && $level == 1) {
  23609. $this->breakpoints[$this->CurrCol][] = $y + $h;
  23610. } // mPDF 6
  23611. if (count($this->cellBorderBuffer)) {
  23612. $this->printcellbuffer();
  23613. }
  23614. }
  23615. /* -- END COLUMNS -- */
  23616. if ($i == $numrows - 1) {
  23617. $this->y = $y + $h;
  23618. } //last row jump (update this->y position)
  23619. if ($this->table_rotate && $level == 1) {
  23620. $this->tbrot_h += $h;
  23621. }
  23622. }// end of rows
  23623. if ($this->progressBar) {
  23624. $this->UpdateProgressBar(7, 70, ' ');
  23625. } // *PROGRESS-BAR*
  23626. if (count($this->cellBorderBuffer)) {
  23627. $this->printcellbuffer();
  23628. }
  23629. if ($this->tableClipPath) {
  23630. $this->_out("Q");
  23631. }
  23632. $this->tableClipPath = '';
  23633. // Advance down page by half width of bottom border
  23634. if ($table['borders_separate']) {
  23635. $this->y += $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  23636. } else {
  23637. $this->y += $table['max_cell_border_width']['B'] / 2;
  23638. }
  23639. if ($table['borders_separate'] && $level == 1) {
  23640. $this->tbrot_h += $table['margin']['B'] + $table['padding']['B'] + $table['border_details']['B']['w'] + $table['border_spacing_V'] / 2;
  23641. } elseif ($level == 1) {
  23642. $this->tbrot_h += $table['margin']['B'] + $table['max_cell_border_width']['B'] / 2;
  23643. }
  23644. $bx = $x0;
  23645. $by = $y0;
  23646. if ($table['borders_separate']) {
  23647. $bx -= ($table['padding']['L'] + $table['border_details']['L']['w'] + $table['border_spacing_H'] / 2);
  23648. if ($tablestartpageno != $this->page) { // IF broken across page
  23649. $by += $table['max_cell_border_width']['T'] / 2;
  23650. if (empty($tableheader)) {
  23651. $by -= ($table['border_spacing_V'] / 2);
  23652. }
  23653. } elseif ($split && $startrow > 0 && empty($tableheader)) {
  23654. $by -= ($table['border_spacing_V'] / 2);
  23655. } else {
  23656. $by -= ($table['padding']['T'] + $table['border_details']['T']['w'] + $table['border_spacing_V'] / 2);
  23657. }
  23658. } elseif ($tablestartpageno != $this->page && !empty($tableheader)) {
  23659. $by += $maxbwtop / 2;
  23660. }
  23661. $by -= $tableheaderadj;
  23662. $bh = $this->y - $by;
  23663. if (!$table['borders_separate']) {
  23664. $bh -= $table['max_cell_border_width']['B'] / 2;
  23665. }
  23666. if ($split) {
  23667. $bw = 0;
  23668. $finalSpread = true;
  23669. for ($t = $startcol; $t < $numcols; $t++) {
  23670. if ($table['colPg'][$t] == $splitpg) {
  23671. $bw += $table['wc'][$t];
  23672. }
  23673. if ($table['colPg'][$t] > $splitpg) {
  23674. $finalSpread = false;
  23675. break;
  23676. }
  23677. }
  23678. if ($startcol == 0) {
  23679. $firstSpread = true;
  23680. } else {
  23681. $firstSpread = false;
  23682. }
  23683. if ($table['borders_separate']) {
  23684. $bw += $table['border_spacing_H'];
  23685. if ($firstSpread) {
  23686. $bw += $table['padding']['L'] + $table['border_details']['L']['w'];
  23687. } else {
  23688. $bx += ($table['padding']['L'] + $table['border_details']['L']['w']);
  23689. }
  23690. if ($finalSpread) {
  23691. $bw += $table['padding']['R'] + $table['border_details']['R']['w'];
  23692. }
  23693. }
  23694. } else {
  23695. $bw = $table['w'] - ($table['max_cell_border_width']['L'] / 2) - ($table['max_cell_border_width']['R'] / 2) - $table['margin']['L'] - $table['margin']['R'];
  23696. }
  23697. if (!$this->ColActive) {
  23698. if (isset($table['bgcolor'][-1])) {
  23699. $color = $this->ConvertColor($table['bgcolor'][-1]);
  23700. if ($color) {
  23701. $this->tableBackgrounds[$level * 9][] = array('gradient' => false, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'col' => $color);
  23702. }
  23703. }
  23704. /* -- BACKGROUNDS -- */
  23705. if (isset($table['gradient'])) {
  23706. $g = $this->grad->parseBackgroundGradient($table['gradient']);
  23707. if ($g) {
  23708. $this->tableBackgrounds[$level * 9 + 1][] = array('gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  23709. }
  23710. }
  23711. if (isset($table['background-image'])) {
  23712. if (isset($table['background-image']['gradient']) && $table['background-image']['gradient'] && preg_match('/(-moz-)*(repeating-)*(linear|radial)-gradient/', $table['background-image']['gradient'])) {
  23713. $g = $this->grad->parseMozGradient($table['background-image']['gradient']);
  23714. if ($g) {
  23715. $this->tableBackgrounds[$level * 9 + 1][] = array('gradient' => true, 'x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'gradtype' => $g['type'], 'stops' => $g['stops'], 'colorspace' => $g['colorspace'], 'coords' => $g['coords'], 'extend' => $g['extend'], 'clippath' => '');
  23716. }
  23717. } else {
  23718. $image_id = $table['background-image']['image_id'];
  23719. $orig_w = $table['background-image']['orig_w'];
  23720. $orig_h = $table['background-image']['orig_h'];
  23721. $x_pos = $table['background-image']['x_pos'];
  23722. $y_pos = $table['background-image']['y_pos'];
  23723. $x_repeat = $table['background-image']['x_repeat'];
  23724. $y_repeat = $table['background-image']['y_repeat'];
  23725. $resize = $table['background-image']['resize'];
  23726. $opacity = $table['background-image']['opacity'];
  23727. $itype = $table['background-image']['itype'];
  23728. $this->tableBackgrounds[$level * 9 + 2][] = array('x' => $bx, 'y' => $by, 'w' => $bw, 'h' => $bh, 'image_id' => $image_id, 'orig_w' => $orig_w, 'orig_h' => $orig_h, 'x_pos' => $x_pos, 'y_pos' => $y_pos, 'x_repeat' => $x_repeat, 'y_repeat' => $y_repeat, 'clippath' => '', 'resize' => $resize, 'opacity' => $opacity, 'itype' => $itype);
  23729. }
  23730. }
  23731. /* -- END BACKGROUNDS -- */
  23732. }
  23733. if ($this->tableBackgrounds && $level == 1) {
  23734. $s = $this->PrintTableBackgrounds();
  23735. if ($this->table_rotate && !$this->processingHeader && !$this->processingFooter) {
  23736. $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->tablebuffer);
  23737. if ($level == 1) {
  23738. $this->tablebuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->tablebuffer);
  23739. }
  23740. } elseif ($this->bufferoutput) {
  23741. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->headerbuffer);
  23742. if ($level == 1) {
  23743. $this->headerbuffer = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->headerbuffer);
  23744. }
  23745. } else {
  23746. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', '\\1' . "\n" . $s . "\n", $this->pages[$this->page]);
  23747. if ($level == 1) {
  23748. $this->pages[$this->page] = preg_replace('/(___TABLE___BACKGROUNDS' . $this->uniqstr . ')/', " ", $this->pages[$this->page]);
  23749. }
  23750. }
  23751. $this->tableBackgrounds = array();
  23752. }
  23753. // TABLE BOTTOM MARGIN
  23754. if ($table['margin']['B']) {
  23755. if (!$this->table_rotate && $level == 1) {
  23756. $this->DivLn($table['margin']['B'], $this->blklvl, true); // collapsible
  23757. } else {
  23758. $this->y += ($table['margin']['B']);
  23759. }
  23760. }
  23761. if ($this->ColActive && $level == 1) {
  23762. $this->breakpoints[$this->CurrCol][] = $this->y;
  23763. } // *COLUMNS*
  23764. if ($split) {
  23765. // Are there more columns to print on a next page?
  23766. if ($lastCol < $numcols - 1) {
  23767. $splitpg++;
  23768. $startcol = $lastCol + 1;
  23769. return array(false, $startrow, $startcol, $splitpg, $returny, $y0);
  23770. } else {
  23771. return array(true, 0, 0, 0, false, false);
  23772. }
  23773. }
  23774. }
  23775. //END OF FUNCTION _tableWrite()
  23776. /////////////////////////END OF TABLE CODE//////////////////////////////////
  23777. /* -- END TABLES -- */
  23778. function _putextgstates()
  23779. {
  23780. for ($i = 1; $i <= count($this->extgstates); $i++) {
  23781. $this->_newobj();
  23782. $this->extgstates[$i]['n'] = $this->n;
  23783. $this->_out('<</Type /ExtGState');
  23784. foreach ($this->extgstates[$i]['parms'] as $k => $v)
  23785. $this->_out('/' . $k . ' ' . $v);
  23786. $this->_out('>>');
  23787. $this->_out('endobj');
  23788. }
  23789. }
  23790. function _putocg()
  23791. {
  23792. if ($this->hasOC) {
  23793. $this->_newobj();
  23794. $this->n_ocg_print = $this->n;
  23795. $this->_out('<</Type /OCG /Name ' . $this->_textstring('Print only'));
  23796. $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
  23797. $this->_out('endobj');
  23798. $this->_newobj();
  23799. $this->n_ocg_view = $this->n;
  23800. $this->_out('<</Type /OCG /Name ' . $this->_textstring('Screen only'));
  23801. $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
  23802. $this->_out('endobj');
  23803. $this->_newobj();
  23804. $this->n_ocg_hidden = $this->n;
  23805. $this->_out('<</Type /OCG /Name ' . $this->_textstring('Hidden'));
  23806. $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /OFF>>>>>>');
  23807. $this->_out('endobj');
  23808. }
  23809. if (count($this->layers)) {
  23810. ksort($this->layers);
  23811. foreach ($this->layers as $id => $layer) {
  23812. $this->_newobj();
  23813. $this->layers[$id]['n'] = $this->n;
  23814. if (isset($this->layerDetails[$id]['name']) && $this->layerDetails[$id]['name']) {
  23815. $name = $this->layerDetails[$id]['name'];
  23816. } else {
  23817. $name = $layer['name'];
  23818. }
  23819. $this->_out('<</Type /OCG /Name ' . $this->_UTF16BEtextstring($name) . '>>');
  23820. $this->_out('endobj');
  23821. }
  23822. }
  23823. }
  23824. /* -- IMPORTS -- */
  23825. // from mPDFI
  23826. function _putimportedobjects()
  23827. {
  23828. if (is_array($this->parsers) && count($this->parsers) > 0) {
  23829. foreach ($this->parsers AS $filename => $p) {
  23830. $this->current_parser = $this->parsers[$filename];
  23831. if (is_array($this->_obj_stack[$filename])) {
  23832. while ($n = key($this->_obj_stack[$filename])) {
  23833. $nObj = $this->current_parser->resolveObject($this->_obj_stack[$filename][$n][1]);
  23834. $this->_newobj($this->_obj_stack[$filename][$n][0]);
  23835. if ($nObj[0] == pdf_parser::TYPE_STREAM) {
  23836. $this->pdf_write_value($nObj);
  23837. } else {
  23838. $this->pdf_write_value($nObj[1]);
  23839. }
  23840. $this->_out('endobj');
  23841. $this->_obj_stack[$filename][$n] = null; // free memory
  23842. unset($this->_obj_stack[$filename][$n]);
  23843. reset($this->_obj_stack[$filename]);
  23844. }
  23845. }
  23846. }
  23847. }
  23848. }
  23849. function _putformxobjects()
  23850. {
  23851. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  23852. reset($this->tpls);
  23853. foreach ($this->tpls AS $tplidx => $tpl) {
  23854. $p = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
  23855. $this->_newobj();
  23856. $this->tpls[$tplidx]['n'] = $this->n;
  23857. $this->_out('<<' . $filter . '/Type /XObject');
  23858. $this->_out('/Subtype /Form');
  23859. $this->_out('/FormType 1');
  23860. // Left/Bottom/Right/Top
  23861. $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', $tpl['box']['x'] * _MPDFK, $tpl['box']['y'] * _MPDFK, ($tpl['box']['x'] + $tpl['box']['w']) * _MPDFK, ($tpl['box']['y'] + $tpl['box']['h']) * _MPDFK)
  23862. );
  23863. if (isset($tpl['box']))
  23864. $this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]', -$tpl['box']['x'] * _MPDFK, -$tpl['box']['y'] * _MPDFK));
  23865. $this->_out('/Resources ');
  23866. if (isset($tpl['resources'])) {
  23867. $this->current_parser = $tpl['parser'];
  23868. $this->pdf_write_value($tpl['resources']);
  23869. } else {
  23870. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  23871. if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
  23872. $this->_out('/Font <<');
  23873. foreach ($this->_res['tpl'][$tplidx]['fonts'] as $font)
  23874. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  23875. $this->_out('>>');
  23876. }
  23877. if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) ||
  23878. isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
  23879. $this->_out('/XObject <<');
  23880. if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
  23881. foreach ($this->_res['tpl'][$tplidx]['images'] as $image)
  23882. $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
  23883. }
  23884. if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
  23885. foreach ($this->_res['tpl'][$tplidx]['tpls'] as $i => $itpl)
  23886. $this->_out($this->tplprefix . $i . ' ' . $itpl['n'] . ' 0 R');
  23887. }
  23888. $this->_out('>>');
  23889. }
  23890. $this->_out('>>');
  23891. }
  23892. $this->_out('/Length ' . strlen($p) . ' >>');
  23893. $this->_putstream($p);
  23894. $this->_out('endobj');
  23895. }
  23896. }
  23897. /* -- END IMPORTS -- */
  23898. function _putpatterns()
  23899. {
  23900. for ($i = 1; $i <= count($this->patterns); $i++) {
  23901. $x = $this->patterns[$i]['x'];
  23902. $y = $this->patterns[$i]['y'];
  23903. $w = $this->patterns[$i]['w'];
  23904. $h = $this->patterns[$i]['h'];
  23905. $pgh = $this->patterns[$i]['pgh'];
  23906. $orig_w = $this->patterns[$i]['orig_w'];
  23907. $orig_h = $this->patterns[$i]['orig_h'];
  23908. $image_id = $this->patterns[$i]['image_id'];
  23909. $itype = $this->patterns[$i]['itype'];
  23910. if (isset($this->patterns[$i]['bpa'])) {
  23911. $bpa = $this->patterns[$i]['bpa'];
  23912. } // background positioning area
  23913. else {
  23914. $bpa = array();
  23915. }
  23916. if ($this->patterns[$i]['x_repeat']) {
  23917. $x_repeat = true;
  23918. } else {
  23919. $x_repeat = false;
  23920. }
  23921. if ($this->patterns[$i]['y_repeat']) {
  23922. $y_repeat = true;
  23923. } else {
  23924. $y_repeat = false;
  23925. }
  23926. $x_pos = $this->patterns[$i]['x_pos'];
  23927. if (stristr($x_pos, '%')) {
  23928. $x_pos += 0;
  23929. $x_pos /= 100;
  23930. if (isset($bpa['w']) && $bpa['w'])
  23931. $x_pos = ($bpa['w'] * $x_pos) - ($orig_w / _MPDFK * $x_pos);
  23932. else
  23933. $x_pos = ($w * $x_pos) - ($orig_w / _MPDFK * $x_pos);
  23934. }
  23935. $y_pos = $this->patterns[$i]['y_pos'];
  23936. if (stristr($y_pos, '%')) {
  23937. $y_pos += 0;
  23938. $y_pos /= 100;
  23939. if (isset($bpa['h']) && $bpa['h'])
  23940. $y_pos = ($bpa['h'] * $y_pos) - ($orig_h / _MPDFK * $y_pos);
  23941. else
  23942. $y_pos = ($h * $y_pos) - ($orig_h / _MPDFK * $y_pos);
  23943. }
  23944. if (isset($bpa['x']) && $bpa['x'])
  23945. $adj_x = ($x_pos + $bpa['x']) * _MPDFK;
  23946. else
  23947. $adj_x = ($x_pos + $x) * _MPDFK;
  23948. if (isset($bpa['y']) && $bpa['y'])
  23949. $adj_y = (($pgh - $y_pos - $bpa['y']) * _MPDFK) - $orig_h;
  23950. else
  23951. $adj_y = (($pgh - $y_pos - $y) * _MPDFK) - $orig_h;
  23952. $img_obj = false;
  23953. if ($itype == 'svg' || $itype == 'wmf') {
  23954. foreach ($this->formobjects AS $fo) {
  23955. if ($fo['i'] == $image_id) {
  23956. $img_obj = $fo['n'];
  23957. $fo_w = $fo['w'];
  23958. $fo_h = -$fo['h'];
  23959. $wmf_x = $fo['x'];
  23960. $wmf_y = $fo['y'];
  23961. break;
  23962. }
  23963. }
  23964. } else {
  23965. foreach ($this->images AS $img) {
  23966. if ($img['i'] == $image_id) {
  23967. $img_obj = $img['n'];
  23968. break;
  23969. }
  23970. }
  23971. }
  23972. if (!$img_obj) {
  23973. echo "Problem: Image object not found for background pattern " . $img['i'];
  23974. exit;
  23975. }
  23976. $this->_newobj();
  23977. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  23978. if ($itype == 'svg' || $itype == 'wmf') {
  23979. $this->_out('/XObject <</FO' . $image_id . ' ' . $img_obj . ' 0 R >>');
  23980. // ******* ADD ANY ExtGStates, Shading AND Fonts needed for the FormObject
  23981. // Set in classes/svg array['fo'] = true
  23982. // Required that _putshaders comes before _putpatterns in _putresources
  23983. // This adds any resources associated with any FormObject to every Formobject - overkill but works!
  23984. if (count($this->extgstates)) {
  23985. $this->_out('/ExtGState <<');
  23986. foreach ($this->extgstates as $k => $extgstate)
  23987. if (isset($extgstate['fo']) && $extgstate['fo']) {
  23988. if (isset($extgstate['trans']))
  23989. $this->_out('/' . $extgstate['trans'] . ' ' . $extgstate['n'] . ' 0 R');
  23990. else
  23991. $this->_out('/GS' . $k . ' ' . $extgstate['n'] . ' 0 R');
  23992. }
  23993. $this->_out('>>');
  23994. }
  23995. /* -- BACKGROUNDS -- */
  23996. if (isset($this->gradients) AND ( count($this->gradients) > 0)) {
  23997. $this->_out('/Shading <<');
  23998. foreach ($this->gradients as $id => $grad) {
  23999. if (isset($grad['fo']) && $grad['fo']) {
  24000. $this->_out('/Sh' . $id . ' ' . $grad['id'] . ' 0 R');
  24001. }
  24002. }
  24003. $this->_out('>>');
  24004. }
  24005. /* -- END BACKGROUNDS -- */
  24006. $this->_out('/Font <<');
  24007. foreach ($this->fonts as $font) {
  24008. if (!$font['used'] && $font['type'] == 'TTF') {
  24009. continue;
  24010. }
  24011. if (isset($font['fo']) && $font['fo']) {
  24012. if ($font['type'] == 'TTF' && ($font['sip'] || $font['smp'])) {
  24013. foreach ($font['n'] AS $k => $fid) {
  24014. $this->_out('/F' . $font['subsetfontids'][$k] . ' ' . $font['n'][$k] . ' 0 R');
  24015. }
  24016. } else {
  24017. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  24018. }
  24019. }
  24020. }
  24021. $this->_out('>>');
  24022. } else {
  24023. $this->_out('/XObject <</I' . $image_id . ' ' . $img_obj . ' 0 R >>');
  24024. }
  24025. $this->_out('>>');
  24026. $this->_out('endobj');
  24027. $this->_newobj();
  24028. $this->patterns[$i]['n'] = $this->n;
  24029. $this->_out('<< /Type /Pattern /PatternType 1 /PaintType 1 /TilingType 2');
  24030. $this->_out('/Resources ' . ($this->n - 1) . ' 0 R');
  24031. $this->_out(sprintf('/BBox [0 0 %.3F %.3F]', $orig_w, $orig_h));
  24032. if ($x_repeat) {
  24033. $this->_out(sprintf('/XStep %.3F', $orig_w));
  24034. } else {
  24035. $this->_out(sprintf('/XStep %d', 99999));
  24036. }
  24037. if ($y_repeat) {
  24038. $this->_out(sprintf('/YStep %.3F', $orig_h));
  24039. } else {
  24040. $this->_out(sprintf('/YStep %d', 99999));
  24041. }
  24042. if ($itype == 'svg' || $itype == 'wmf') {
  24043. $this->_out(sprintf('/Matrix [1 0 0 -1 %.3F %.3F]', $adj_x, ($adj_y + $orig_h)));
  24044. $s = sprintf("q %.3F 0 0 %.3F %.3F %.3F cm /FO%d Do Q", ($orig_w / $fo_w), (-$orig_h / $fo_h), -($orig_w / $fo_w) * $wmf_x, ($orig_w / $fo_w) * $wmf_y, $image_id);
  24045. } else {
  24046. $this->_out(sprintf('/Matrix [1 0 0 1 %.3F %.3F]', $adj_x, $adj_y));
  24047. $s = sprintf("q %.3F 0 0 %.3F 0 0 cm /I%d Do Q", $orig_w, $orig_h, $image_id);
  24048. }
  24049. if ($this->compress) {
  24050. $this->_out('/Filter /FlateDecode');
  24051. $s = gzcompress($s);
  24052. }
  24053. $this->_out('/Length ' . strlen($s) . '>>');
  24054. $this->_putstream($s);
  24055. $this->_out('endobj');
  24056. }
  24057. }
  24058. /* -- BACKGROUNDS -- */
  24059. function _putshaders()
  24060. {
  24061. $maxid = count($this->gradients); //index for transparency gradients
  24062. foreach ($this->gradients as $id => $grad) {
  24063. if (($grad['type'] == 2 || $grad['type'] == 3) && empty($grad['is_mask'])) {
  24064. $this->_newobj();
  24065. $this->_out('<<');
  24066. $this->_out('/FunctionType 3');
  24067. $this->_out('/Domain [0 1]');
  24068. $fn = array();
  24069. $bd = array();
  24070. $en = array();
  24071. for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
  24072. $fn[] = ($this->n + 1 + $i) . ' 0 R';
  24073. $en[] = '0 1';
  24074. if ($i > 0) {
  24075. $bd[] = sprintf('%.3F', $grad['stops'][$i]['offset']);
  24076. }
  24077. }
  24078. $this->_out('/Functions [' . implode(' ', $fn) . ']');
  24079. $this->_out('/Bounds [' . implode(' ', $bd) . ']');
  24080. $this->_out('/Encode [' . implode(' ', $en) . ']');
  24081. $this->_out('>>');
  24082. $this->_out('endobj');
  24083. $f1 = $this->n;
  24084. for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
  24085. $this->_newobj();
  24086. $this->_out('<<');
  24087. $this->_out('/FunctionType 2');
  24088. $this->_out('/Domain [0 1]');
  24089. $this->_out('/C0 [' . $grad['stops'][$i]['col'] . ']');
  24090. $this->_out('/C1 [' . $grad['stops'][$i + 1]['col'] . ']');
  24091. $this->_out('/N 1');
  24092. $this->_out('>>');
  24093. $this->_out('endobj');
  24094. }
  24095. }
  24096. if ($grad['type'] == 2 || $grad['type'] == 3) {
  24097. if (isset($grad['trans']) && $grad['trans']) {
  24098. $this->_newobj();
  24099. $this->_out('<<');
  24100. $this->_out('/FunctionType 3');
  24101. $this->_out('/Domain [0 1]');
  24102. $fn = array();
  24103. $bd = array();
  24104. $en = array();
  24105. for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
  24106. $fn[] = ($this->n + 1 + $i) . ' 0 R';
  24107. $en[] = '0 1';
  24108. if ($i > 0) {
  24109. $bd[] = sprintf('%.3F', $grad['stops'][$i]['offset']);
  24110. }
  24111. }
  24112. $this->_out('/Functions [' . implode(' ', $fn) . ']');
  24113. $this->_out('/Bounds [' . implode(' ', $bd) . ']');
  24114. $this->_out('/Encode [' . implode(' ', $en) . ']');
  24115. $this->_out('>>');
  24116. $this->_out('endobj');
  24117. $f2 = $this->n;
  24118. for ($i = 0; $i < (count($grad['stops']) - 1); $i++) {
  24119. $this->_newobj();
  24120. $this->_out('<<');
  24121. $this->_out('/FunctionType 2');
  24122. $this->_out('/Domain [0 1]');
  24123. $this->_out(sprintf('/C0 [%.3F]', $grad['stops'][$i]['opacity']));
  24124. $this->_out(sprintf('/C1 [%.3F]', $grad['stops'][$i + 1]['opacity']));
  24125. $this->_out('/N 1');
  24126. $this->_out('>>');
  24127. $this->_out('endobj');
  24128. }
  24129. }
  24130. }
  24131. if (empty($grad['is_mask'])) {
  24132. $this->_newobj();
  24133. $this->_out('<<');
  24134. $this->_out('/ShadingType ' . $grad['type']);
  24135. if (isset($grad['colorspace'])) {
  24136. $this->_out('/ColorSpace /Device' . $grad['colorspace']); // Can use CMYK if all C0 and C1 above have 4 values
  24137. } else {
  24138. $this->_out('/ColorSpace /DeviceRGB');
  24139. }
  24140. if ($grad['type'] == 2) {
  24141. $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
  24142. $this->_out('/Function ' . $f1 . ' 0 R');
  24143. $this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
  24144. $this->_out('>>');
  24145. } elseif ($grad['type'] == 3) {
  24146. //x0, y0, r0, x1, y1, r1
  24147. //at this this time radius of inner circle is 0
  24148. $ir = 0;
  24149. if (isset($grad['coords'][5]) && $grad['coords'][5]) {
  24150. $ir = $grad['coords'][5];
  24151. }
  24152. $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $ir, $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
  24153. $this->_out('/Function ' . $f1 . ' 0 R');
  24154. $this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
  24155. $this->_out('>>');
  24156. } elseif ($grad['type'] == 6) {
  24157. $this->_out('/BitsPerCoordinate 16');
  24158. $this->_out('/BitsPerComponent 8');
  24159. if ($grad['colorspace'] == 'CMYK') {
  24160. $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1 0 1]');
  24161. } elseif ($grad['colorspace'] == 'Gray') {
  24162. $this->_out('/Decode[0 1 0 1 0 1]');
  24163. } else {
  24164. $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
  24165. }
  24166. $this->_out('/BitsPerFlag 8');
  24167. $this->_out('/Length ' . strlen($grad['stream']));
  24168. $this->_out('>>');
  24169. $this->_putstream($grad['stream']);
  24170. }
  24171. $this->_out('endobj');
  24172. }
  24173. $this->gradients[$id]['id'] = $this->n;
  24174. // set pattern object
  24175. $this->_newobj();
  24176. $out = '<< /Type /Pattern /PatternType 2';
  24177. $out .= ' /Shading ' . $this->gradients[$id]['id'] . ' 0 R';
  24178. $out .= ' >>';
  24179. $out .= "\n" . 'endobj';
  24180. $this->_out($out);
  24181. $this->gradients[$id]['pattern'] = $this->n;
  24182. if (isset($grad['trans']) && $grad['trans']) {
  24183. // luminosity pattern
  24184. $transid = $id + $maxid;
  24185. $this->_newobj();
  24186. $this->_out('<<');
  24187. $this->_out('/ShadingType ' . $grad['type']);
  24188. $this->_out('/ColorSpace /DeviceGray');
  24189. if ($grad['type'] == 2) {
  24190. $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
  24191. $this->_out('/Function ' . $f2 . ' 0 R');
  24192. $this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
  24193. $this->_out('>>');
  24194. } elseif ($grad['type'] == 3) {
  24195. //x0, y0, r0, x1, y1, r1
  24196. //at this this time radius of inner circle is 0
  24197. $ir = 0;
  24198. if (isset($grad['coords'][5]) && $grad['coords'][5]) {
  24199. $ir = $grad['coords'][5];
  24200. }
  24201. $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $ir, $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
  24202. $this->_out('/Function ' . $f2 . ' 0 R');
  24203. $this->_out('/Extend [' . $grad['extend'][0] . ' ' . $grad['extend'][1] . '] ');
  24204. $this->_out('>>');
  24205. } elseif ($grad['type'] == 6) {
  24206. $this->_out('/BitsPerCoordinate 16');
  24207. $this->_out('/BitsPerComponent 8');
  24208. $this->_out('/Decode[0 1 0 1 0 1]');
  24209. $this->_out('/BitsPerFlag 8');
  24210. $this->_out('/Length ' . strlen($grad['stream_trans']));
  24211. $this->_out('>>');
  24212. $this->_putstream($grad['stream_trans']);
  24213. }
  24214. $this->_out('endobj');
  24215. $this->gradients[$transid]['id'] = $this->n;
  24216. $this->_newobj();
  24217. $this->_out('<< /Type /Pattern /PatternType 2');
  24218. $this->_out('/Shading ' . $this->gradients[$transid]['id'] . ' 0 R');
  24219. $this->_out('>>');
  24220. $this->_out('endobj');
  24221. $this->gradients[$transid]['pattern'] = $this->n;
  24222. $this->_newobj();
  24223. // Need to extend size of viewing box in case of transformations
  24224. $str = 'q /a0 gs /Pattern cs /p' . $transid . ' scn -' . ($this->wPt / 2) . ' -' . ($this->hPt / 2) . ' ' . (2 * $this->wPt) . ' ' . (2 * $this->hPt) . ' re f Q';
  24225. $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
  24226. $p = ($this->compress) ? gzcompress($str) : $str;
  24227. $this->_out('<< /Type /XObject /Subtype /Form /FormType 1 ' . $filter);
  24228. $this->_out('/Length ' . strlen($p));
  24229. $this->_out('/BBox [-' . ($this->wPt / 2) . ' -' . ($this->hPt / 2) . ' ' . (2 * $this->wPt) . ' ' . (2 * $this->hPt) . ']');
  24230. $this->_out('/Group << /Type /Group /S /Transparency /CS /DeviceGray >>');
  24231. $this->_out('/Resources <<');
  24232. $this->_out('/ExtGState << /a0 << /ca 1 /CA 1 >> >>');
  24233. $this->_out('/Pattern << /p' . $transid . ' ' . $this->gradients[$transid]['pattern'] . ' 0 R >>');
  24234. $this->_out('>>');
  24235. $this->_out('>>');
  24236. $this->_putstream($p);
  24237. $this->_out('endobj');
  24238. $this->_newobj();
  24239. $this->_out('<< /Type /Mask /S /Luminosity /G ' . ($this->n - 1) . ' 0 R >>' . "\n" . 'endobj');
  24240. $this->_newobj();
  24241. $this->_out('<< /Type /ExtGState /SMask ' . ($this->n - 1) . ' 0 R /AIS false >>' . "\n" . 'endobj');
  24242. if (isset($grad['fo']) && $grad['fo']) {
  24243. $this->extgstates[] = array('n' => $this->n, 'trans' => 'TGS' . $id, 'fo' => true);
  24244. } else {
  24245. $this->extgstates[] = array('n' => $this->n, 'trans' => 'TGS' . $id);
  24246. }
  24247. }
  24248. }
  24249. }
  24250. /* -- END BACKGROUNDS -- */
  24251. function _putspotcolors()
  24252. {
  24253. foreach ($this->spotColors as $name => $color) {
  24254. $this->_newobj();
  24255. $this->_out('[/Separation /' . str_replace(' ', '#20', $name));
  24256. $this->_out('/DeviceCMYK <<');
  24257. $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
  24258. $this->_out(sprintf('/C1 [%.3F %.3F %.3F %.3F] ', $color['c'] / 100, $color['m'] / 100, $color['y'] / 100, $color['k'] / 100));
  24259. $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
  24260. $this->_out('endobj');
  24261. $this->spotColors[$name]['n'] = $this->n;
  24262. }
  24263. }
  24264. function _putresources()
  24265. {
  24266. if ($this->hasOC || count($this->layers))
  24267. $this->_putocg();
  24268. $this->_putextgstates();
  24269. $this->_putspotcolors();
  24270. if ($this->progressBar) {
  24271. $this->UpdateProgressBar(2, '40', 'Compiling Fonts');
  24272. } // *PROGRESS-BAR*
  24273. $this->_putfonts();
  24274. if ($this->progressBar) {
  24275. $this->UpdateProgressBar(2, '50', 'Compiling Images');
  24276. } // *PROGRESS-BAR*
  24277. $this->_putimages();
  24278. $this->_putformobjects(); // *IMAGES-CORE*
  24279. /* -- IMPORTS -- */
  24280. if ($this->enableImports) {
  24281. $this->_putformxobjects();
  24282. $this->_putimportedobjects();
  24283. }
  24284. /* -- END IMPORTS -- */
  24285. /* -- BACKGROUNDS -- */
  24286. $this->_putshaders();
  24287. $this->_putpatterns();
  24288. /* -- END BACKGROUNDS -- */
  24289. //Resource dictionary
  24290. $this->offsets[2] = strlen($this->buffer);
  24291. $this->_out('2 0 obj');
  24292. $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
  24293. $this->_out('/Font <<');
  24294. foreach ($this->fonts as $font) {
  24295. if (isset($font['type']) && $font['type'] == 'TTF' && !$font['used']) {
  24296. continue;
  24297. }
  24298. if (isset($font['type']) && $font['type'] == 'TTF' && ($font['sip'] || $font['smp'])) {
  24299. foreach ($font['n'] AS $k => $fid) {
  24300. $this->_out('/F' . $font['subsetfontids'][$k] . ' ' . $font['n'][$k] . ' 0 R');
  24301. }
  24302. } else {
  24303. $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
  24304. }
  24305. }
  24306. $this->_out('>>');
  24307. if (count($this->spotColors)) {
  24308. $this->_out('/ColorSpace <<');
  24309. foreach ($this->spotColors as $color)
  24310. $this->_out('/CS' . $color['i'] . ' ' . $color['n'] . ' 0 R');
  24311. $this->_out('>>');
  24312. }
  24313. if (count($this->extgstates)) {
  24314. $this->_out('/ExtGState <<');
  24315. foreach ($this->extgstates as $k => $extgstate)
  24316. if (isset($extgstate['trans']))
  24317. $this->_out('/' . $extgstate['trans'] . ' ' . $extgstate['n'] . ' 0 R');
  24318. else
  24319. $this->_out('/GS' . $k . ' ' . $extgstate['n'] . ' 0 R');
  24320. $this->_out('>>');
  24321. }
  24322. /* -- BACKGROUNDS -- */
  24323. if ((isset($this->gradients) AND ( count($this->gradients) > 0)) || ($this->enableImports && count($this->tpls))) { // mPDF 5.7.3
  24324. $this->_out('/Shading <<');
  24325. foreach ($this->gradients as $id => $grad) {
  24326. $this->_out('/Sh' . $id . ' ' . $grad['id'] . ' 0 R');
  24327. }
  24328. // mPDF 5.7.3
  24329. // If a shading dictionary is in an object (tpl) imported from another PDF, it needs to be included
  24330. // in the document resources, as well as the object resources
  24331. // Otherwise get an error in some PDF viewers
  24332. if ($this->enableImports && count($this->tpls)) {
  24333. foreach ($this->tpls as $tplidx => $tpl) {
  24334. if (isset($tpl['resources'])) {
  24335. $this->current_parser = $tpl['parser'];
  24336. reset($tpl['resources'][1]);
  24337. while (list($k, $v) = each($tpl['resources'][1])) {
  24338. if ($k == '/Shading') {
  24339. while (list($k2, $v2) = each($v[1])) {
  24340. $this->_out($k2 . " ", false);
  24341. $this->pdf_write_value($v2);
  24342. }
  24343. }
  24344. }
  24345. }
  24346. }
  24347. }
  24348. $this->_out('>>');
  24349. /*
  24350. // ??? Not needed !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  24351. $this->_out('/Pattern <<');
  24352. foreach ($this->gradients as $id => $grad) {
  24353. $this->_out('/P'.$id.' '.$grad['pattern'].' 0 R');
  24354. }
  24355. $this->_out('>>');
  24356. */
  24357. }
  24358. /* -- END BACKGROUNDS -- */
  24359. if (count($this->images) || count($this->formobjects) || ($this->enableImports && count($this->tpls))) {
  24360. $this->_out('/XObject <<');
  24361. foreach ($this->images as $image)
  24362. $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
  24363. foreach ($this->formobjects as $formobject)
  24364. $this->_out('/FO' . $formobject['i'] . ' ' . $formobject['n'] . ' 0 R');
  24365. /* -- IMPORTS -- */
  24366. if ($this->enableImports && count($this->tpls)) {
  24367. foreach ($this->tpls as $tplidx => $tpl) {
  24368. $this->_out($this->tplprefix . $tplidx . ' ' . $tpl['n'] . ' 0 R');
  24369. }
  24370. }
  24371. /* -- END IMPORTS -- */
  24372. $this->_out('>>');
  24373. }
  24374. /* -- BACKGROUNDS -- */
  24375. if (count($this->patterns)) {
  24376. $this->_out('/Pattern <<');
  24377. foreach ($this->patterns as $k => $patterns)
  24378. $this->_out('/P' . $k . ' ' . $patterns['n'] . ' 0 R');
  24379. $this->_out('>>');
  24380. }
  24381. /* -- END BACKGROUNDS -- */
  24382. if ($this->hasOC || count($this->layers)) {
  24383. $this->_out('/Properties <<');
  24384. if ($this->hasOC) {
  24385. $this->_out('/OC1 ' . $this->n_ocg_print . ' 0 R /OC2 ' . $this->n_ocg_view . ' 0 R /OC3 ' . $this->n_ocg_hidden . ' 0 R ');
  24386. }
  24387. if (count($this->layers)) {
  24388. foreach ($this->layers as $id => $layer)
  24389. $this->_out('/ZI' . $id . ' ' . $layer['n'] . ' 0 R');
  24390. }
  24391. $this->_out('>>');
  24392. }
  24393. $this->_out('>>');
  24394. $this->_out('endobj'); // end resource dictionary
  24395. $this->_putbookmarks(); // *BOOKMARKS*
  24396. if (isset($this->js) && $this->js) {
  24397. $this->_putjavascript();
  24398. }
  24399. /* -- ENCRYPTION -- */
  24400. if ($this->encrypted) {
  24401. $this->_newobj();
  24402. $this->enc_obj_id = $this->n;
  24403. $this->_out('<<');
  24404. $this->_putencryption();
  24405. $this->_out('>>');
  24406. $this->_out('endobj');
  24407. }
  24408. /* -- END ENCRYPTION -- */
  24409. }
  24410. function _putjavascript()
  24411. {
  24412. $this->_newobj();
  24413. $this->n_js = $this->n;
  24414. $this->_out('<<');
  24415. $this->_out('/Names [(EmbeddedJS) ' . (1 + $this->n) . ' 0 R ]');
  24416. $this->_out('>>');
  24417. $this->_out('endobj');
  24418. $this->_newobj();
  24419. $this->_out('<<');
  24420. $this->_out('/S /JavaScript');
  24421. $this->_out('/JS ' . $this->_textstring($this->js));
  24422. $this->_out('>>');
  24423. $this->_out('endobj');
  24424. }
  24425. /* -- ENCRYPTION -- */
  24426. function _putencryption()
  24427. {
  24428. $this->_out('/Filter /Standard');
  24429. if ($this->useRC128encryption) {
  24430. $this->_out('/V 2');
  24431. $this->_out('/R 3');
  24432. $this->_out('/Length 128');
  24433. } else {
  24434. $this->_out('/V 1');
  24435. $this->_out('/R 2');
  24436. }
  24437. $this->_out('/O (' . $this->_escape($this->Ovalue) . ')');
  24438. $this->_out('/U (' . $this->_escape($this->Uvalue) . ')');
  24439. $this->_out('/P ' . $this->Pvalue);
  24440. }
  24441. /* -- END ENCRYPTION -- */
  24442. function _puttrailer()
  24443. {
  24444. $this->_out('/Size ' . ($this->n + 1));
  24445. $this->_out('/Root ' . $this->n . ' 0 R');
  24446. $this->_out('/Info ' . $this->InfoRoot . ' 0 R');
  24447. /* -- ENCRYPTION -- */
  24448. if ($this->encrypted) {
  24449. $this->_out('/Encrypt ' . $this->enc_obj_id . ' 0 R');
  24450. $this->_out('/ID [<' . $this->uniqid . '> <' . $this->uniqid . '>]');
  24451. } else {
  24452. /* -- END ENCRYPTION -- */
  24453. $uniqid = md5(time() . $this->buffer);
  24454. $this->_out('/ID [<' . $uniqid . '> <' . $uniqid . '>]');
  24455. /* -- ENCRYPTION -- */
  24456. }
  24457. /* -- END ENCRYPTION -- */
  24458. }
  24459. /* -- ENCRYPTION -- */
  24460. function SetProtection($permissions = array(), $user_pass = '', $owner_pass = null, $length = 40)
  24461. {
  24462. $this->encrypted = false;
  24463. if (is_string($permissions) && strlen($permissions) > 0) {
  24464. $permissions = array($permissions);
  24465. } elseif (!is_array($permissions)) {
  24466. return 0;
  24467. }
  24468. $this->last_rc4_key = '';
  24469. $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08" .
  24470. "\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
  24471. $options = array(
  24472. 'print' => 4, // bit 3
  24473. 'modify' => 8, // bit 4
  24474. 'copy' => 16, // bit 5
  24475. 'annot-forms' => 32, // bit 6
  24476. 'fill-forms' => 256, // bit 9
  24477. 'extract' => 512, // bit 10
  24478. 'assemble' => 1024, // bit 11
  24479. 'print-highres' => 2048 // bit 12
  24480. );
  24481. // bit 31 = 1073741824
  24482. // bit 32 = 2147483648
  24483. // bits 13-31 = 2147479552
  24484. // bits 13-32 = 4294963200 + 192 = 4294963392
  24485. $protection = 4294963392; // bits 7,8,13-32
  24486. foreach ($permissions as $permission) {
  24487. if (!isset($options[$permission]))
  24488. throw new MpdfException('Incorrect permission: ' . $permission);
  24489. if ($options[$permission] > 32) {
  24490. $this->useRC128encryption = true;
  24491. }
  24492. if (isset($options[$permission]))
  24493. $protection += $options[$permission];
  24494. }
  24495. if ($length == 128) {
  24496. $this->useRC128encryption = true;
  24497. }
  24498. if ($owner_pass === null)
  24499. $owner_pass = uniqid(rand());
  24500. $this->encrypted = true;
  24501. $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
  24502. }
  24503. // Compute key depending on object number where the encrypted data is stored
  24504. function _objectkey($n)
  24505. {
  24506. if ($this->useRC128encryption)
  24507. $len = 16;
  24508. else
  24509. $len = 10;
  24510. return substr($this->_md5_16($this->encryption_key . pack('VXxx', $n)), 0, $len);
  24511. }
  24512. // RC4 is the standard encryption algorithm used in PDF format
  24513. function _RC4($key, $text)
  24514. {
  24515. if ($this->last_rc4_key != $key) {
  24516. $k = str_repeat($key, 256 / strlen($key) + 1);
  24517. $rc4 = range(0, 255);
  24518. $j = 0;
  24519. for ($i = 0; $i < 256; $i++) {
  24520. $t = $rc4[$i];
  24521. $j = ($j + $t + ord($k[$i])) % 256;
  24522. $rc4[$i] = $rc4[$j];
  24523. $rc4[$j] = $t;
  24524. }
  24525. $this->last_rc4_key = $key;
  24526. $this->last_rc4_key_c = $rc4;
  24527. } else {
  24528. $rc4 = $this->last_rc4_key_c;
  24529. }
  24530. $len = strlen($text);
  24531. $a = 0;
  24532. $b = 0;
  24533. $out = '';
  24534. for ($i = 0; $i < $len; $i++) {
  24535. $a = ($a + 1) % 256;
  24536. $t = $rc4[$a];
  24537. $b = ($b + $t) % 256;
  24538. $rc4[$a] = $rc4[$b];
  24539. $rc4[$b] = $t;
  24540. $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
  24541. $out.= chr(ord($text[$i]) ^ $k);
  24542. }
  24543. return $out;
  24544. }
  24545. // Get MD5 as binary string
  24546. function _md5_16($string)
  24547. {
  24548. return pack('H*', md5($string));
  24549. }
  24550. // Compute O value
  24551. function _Ovalue($user_pass, $owner_pass)
  24552. {
  24553. $tmp = $this->_md5_16($owner_pass);
  24554. if ($this->useRC128encryption) {
  24555. for ($i = 0; $i < 50; ++$i) {
  24556. $tmp = $this->_md5_16($tmp);
  24557. }
  24558. }
  24559. if ($this->useRC128encryption)
  24560. $keybytelen = (128 / 8);
  24561. else
  24562. $keybytelen = (40 / 8);
  24563. $owner_RC4_key = substr($tmp, 0, $keybytelen);
  24564. $enc = $this->_RC4($owner_RC4_key, $user_pass);
  24565. if ($this->useRC128encryption) {
  24566. $len = strlen($owner_RC4_key);
  24567. for ($i = 1; $i <= 19; ++$i) {
  24568. $key = '';
  24569. for ($j = 0; $j < $len; ++$j) {
  24570. $key .= chr(ord($owner_RC4_key{$j}) ^ $i);
  24571. }
  24572. $enc = $this->_RC4($key, $enc);
  24573. }
  24574. }
  24575. return $enc;
  24576. }
  24577. // Compute U value
  24578. function _Uvalue()
  24579. {
  24580. if ($this->useRC128encryption) {
  24581. $tmp = $this->_md5_16($this->padding . $this->_hexToString($this->uniqid));
  24582. $enc = $this->_RC4($this->encryption_key, $tmp);
  24583. $len = strlen($tmp);
  24584. for ($i = 1; $i <= 19; ++$i) {
  24585. $key = '';
  24586. for ($j = 0; $j < $len; ++$j) {
  24587. $key .= chr(ord($this->encryption_key{$j}) ^ $i);
  24588. }
  24589. $enc = $this->_RC4($key, $enc);
  24590. }
  24591. $enc .= str_repeat("\x00", 16);
  24592. return substr($enc, 0, 32);
  24593. } else {
  24594. return $this->_RC4($this->encryption_key, $this->padding);
  24595. }
  24596. }
  24597. // Compute encryption key
  24598. function _generateencryptionkey($user_pass, $owner_pass, $protection)
  24599. {
  24600. // Pad passwords
  24601. $user_pass = substr($user_pass . $this->padding, 0, 32);
  24602. $owner_pass = substr($owner_pass . $this->padding, 0, 32);
  24603. $chars = 'ABCDEF1234567890';
  24604. $id = '';
  24605. for ($i = 0; $i < 32; $i++) {
  24606. $id .= $chars{rand(0, 15)};
  24607. }
  24608. $this->uniqid = md5($id);
  24609. // Compute O value
  24610. $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
  24611. // Compute encyption key
  24612. if ($this->useRC128encryption)
  24613. $keybytelen = (128 / 8);
  24614. else
  24615. $keybytelen = (40 / 8);
  24616. $prot = sprintf('%032b', $protection);
  24617. $perms = chr(bindec(substr($prot, 24, 8)));
  24618. $perms .= chr(bindec(substr($prot, 16, 8)));
  24619. $perms .= chr(bindec(substr($prot, 8, 8)));
  24620. $perms .= chr(bindec(substr($prot, 0, 8)));
  24621. $tmp = $this->_md5_16($user_pass . $this->Ovalue . $perms . $this->_hexToString($this->uniqid));
  24622. if ($this->useRC128encryption) {
  24623. for ($i = 0; $i < 50; ++$i) {
  24624. $tmp = $this->_md5_16(substr($tmp, 0, $keybytelen));
  24625. }
  24626. }
  24627. $this->encryption_key = substr($tmp, 0, $keybytelen);
  24628. // Compute U value
  24629. $this->Uvalue = $this->_Uvalue();
  24630. // Compute P value
  24631. $this->Pvalue = $protection;
  24632. }
  24633. function _hexToString($hs)
  24634. {
  24635. $s = '';
  24636. $len = strlen($hs);
  24637. if (($len % 2) != 0) {
  24638. $hs .= '0';
  24639. ++$len;
  24640. }
  24641. for ($i = 0; $i < $len; $i += 2) {
  24642. $s .= chr(hexdec($hs{$i} . $hs{($i + 1)}));
  24643. }
  24644. return $s;
  24645. }
  24646. /* -- END ENCRYPTION -- */
  24647. //=========================================
  24648. /* -- BOOKMARKS -- */
  24649. // FROM class PDF_Bookmark
  24650. function Bookmark($txt, $level = 0, $y = 0)
  24651. {
  24652. $txt = $this->purify_utf8_text($txt);
  24653. if ($this->text_input_as_HTML) {
  24654. $txt = $this->all_entities_to_utf8($txt);
  24655. }
  24656. if ($y == -1) {
  24657. if (!$this->ColActive) {
  24658. $y = $this->y;
  24659. } else {
  24660. $y = $this->y0;
  24661. } // If columns are on - mark top of columns
  24662. }
  24663. // else y is used as set, or =0 i.e. top of page
  24664. // DIRECTIONALITY RTL
  24665. $bmo = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->page);
  24666. if ($this->keep_block_together) {
  24667. // do nothing
  24668. }
  24669. /* -- TABLES -- */ elseif ($this->table_rotate) {
  24670. $this->tbrot_BMoutlines[] = $bmo;
  24671. } elseif ($this->kwt) {
  24672. $this->kwt_BMoutlines[] = $bmo;
  24673. }
  24674. /* -- END TABLES -- */ elseif ($this->ColActive) { // *COLUMNS*
  24675. $this->col_BMoutlines[] = $bmo; // *COLUMNS*
  24676. } // *COLUMNS*
  24677. else {
  24678. $this->BMoutlines[] = $bmo;
  24679. }
  24680. }
  24681. function _putbookmarks()
  24682. {
  24683. $nb = count($this->BMoutlines);
  24684. if ($nb == 0)
  24685. return;
  24686. $bmo = $this->BMoutlines;
  24687. $this->BMoutlines = array();
  24688. $lastlevel = -1;
  24689. for ($i = 0; $i < count($bmo); $i++) {
  24690. if ($bmo[$i]['l'] > 0) {
  24691. while ($bmo[$i]['l'] - $lastlevel > 1) { // If jump down more than one level, insert a new entry
  24692. $new = $bmo[$i];
  24693. $new['t'] = "[" . $new['t'] . "]"; // Put [] around text/title to highlight
  24694. $new['l'] = $lastlevel + 1;
  24695. $lastlevel++;
  24696. $this->BMoutlines[] = $new;
  24697. }
  24698. }
  24699. $this->BMoutlines[] = $bmo[$i];
  24700. $lastlevel = $bmo[$i]['l'];
  24701. }
  24702. $nb = count($this->BMoutlines);
  24703. $lru = array();
  24704. $level = 0;
  24705. foreach ($this->BMoutlines as $i => $o) {
  24706. if ($o['l'] > 0) {
  24707. $parent = $lru[$o['l'] - 1];
  24708. //Set parent and last pointers
  24709. $this->BMoutlines[$i]['parent'] = $parent;
  24710. $this->BMoutlines[$parent]['last'] = $i;
  24711. if ($o['l'] > $level) {
  24712. //Level increasing: set first pointer
  24713. $this->BMoutlines[$parent]['first'] = $i;
  24714. }
  24715. } else {
  24716. $this->BMoutlines[$i]['parent'] = $nb;
  24717. }
  24718. if ($o['l'] <= $level and $i > 0) {
  24719. //Set prev and next pointers
  24720. $prev = $lru[$o['l']];
  24721. $this->BMoutlines[$prev]['next'] = $i;
  24722. $this->BMoutlines[$i]['prev'] = $prev;
  24723. }
  24724. $lru[$o['l']] = $i;
  24725. $level = $o['l'];
  24726. }
  24727. //Outline items
  24728. $n = $this->n + 1;
  24729. foreach ($this->BMoutlines as $i => $o) {
  24730. $this->_newobj();
  24731. $this->_out('<</Title ' . $this->_UTF16BEtextstring($o['t']));
  24732. $this->_out('/Parent ' . ($n + $o['parent']) . ' 0 R');
  24733. if (isset($o['prev']))
  24734. $this->_out('/Prev ' . ($n + $o['prev']) . ' 0 R');
  24735. if (isset($o['next']))
  24736. $this->_out('/Next ' . ($n + $o['next']) . ' 0 R');
  24737. if (isset($o['first']))
  24738. $this->_out('/First ' . ($n + $o['first']) . ' 0 R');
  24739. if (isset($o['last']))
  24740. $this->_out('/Last ' . ($n + $o['last']) . ' 0 R');
  24741. if (isset($this->pageDim[$o['p']]['h'])) {
  24742. $h = $this->pageDim[$o['p']]['h'];
  24743. } else {
  24744. $h = 0;
  24745. }
  24746. $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.3F null]', 1 + 2 * ($o['p']), ($h - $o['y']) * _MPDFK));
  24747. if (isset($this->bookmarkStyles) && isset($this->bookmarkStyles[$o['l']])) {
  24748. // font style
  24749. $bms = $this->bookmarkStyles[$o['l']]['style'];
  24750. $style = 0;
  24751. if (strpos($bms, 'B') !== false) {
  24752. $style += 2;
  24753. }
  24754. if (strpos($bms, 'I') !== false) {
  24755. $style += 1;
  24756. }
  24757. $this->_out(sprintf('/F %d', $style));
  24758. // Colour
  24759. $col = $this->bookmarkStyles[$o['l']]['color'];
  24760. if (isset($col) && is_array($col) && count($col) == 3) {
  24761. $this->_out(sprintf('/C [%.3F %.3F %.3F]', ($col[0] / 255), ($col[1] / 255), ($col[2] / 255)));
  24762. }
  24763. }
  24764. $this->_out('/Count 0>>');
  24765. $this->_out('endobj');
  24766. }
  24767. //Outline root
  24768. $this->_newobj();
  24769. $this->OutlineRoot = $this->n;
  24770. $this->_out('<</Type /BMoutlines /First ' . $n . ' 0 R');
  24771. $this->_out('/Last ' . ($n + $lru[0]) . ' 0 R>>');
  24772. $this->_out('endobj');
  24773. }
  24774. /* -- END BOOKMARKS -- */
  24775. //======================================================
  24776. /* -- TOC -- */
  24777. // ToC TABLE OF CONTENTS
  24778. // Initiate, and Mark a place for the Table of Contents to be inserted
  24779. function TOC($tocfont = '', $tocfontsize = 0, $tocindent = 0, $resetpagenum = '', $pagenumstyle = '', $suppress = '', $toc_orientation = '', $TOCusePaging = true, $TOCuseLinking = false, $toc_id = 0, $tocoutdent = '')
  24780. {
  24781. if (!class_exists('tocontents', false)) {
  24782. include(_MPDF_PATH . 'classes/tocontents.php');
  24783. }
  24784. if (empty($this->tocontents)) {
  24785. $this->tocontents = new tocontents($this);
  24786. }
  24787. $this->tocontents->TOC($tocfont, $tocfontsize, $tocindent, $resetpagenum, $pagenumstyle, $suppress, $toc_orientation, $TOCusePaging, $TOCuseLinking, $toc_id, $tocoutdent);
  24788. }
  24789. function TOCpagebreakByArray($a)
  24790. {
  24791. if (!is_array($a)) {
  24792. $a = array();
  24793. }
  24794. if (!class_exists('tocontents', false)) {
  24795. include(_MPDF_PATH . 'classes/tocontents.php');
  24796. }
  24797. if (empty($this->tocontents)) {
  24798. $this->tocontents = new tocontents($this);
  24799. }
  24800. $tocoutdent = (isset($a['tocoutdent']) ? $a['tocoutdent'] : (isset($a['outdent']) ? $a['outdent'] : ''));
  24801. $TOCusePaging = (isset($a['TOCusePaging']) ? $a['TOCusePaging'] : (isset($a['paging']) ? $a['paging'] : true));
  24802. $TOCuseLinking = (isset($a['TOCuseLinking']) ? $a['TOCuseLinking'] : (isset($a['links']) ? $a['links'] : ''));
  24803. $toc_orientation = (isset($a['toc_orientation']) ? $a['toc_orientation'] : (isset($a['toc-orientation']) ? $a['toc-orientation'] : ''));
  24804. $toc_mgl = (isset($a['toc_mgl']) ? $a['toc_mgl'] : (isset($a['toc-margin-left']) ? $a['toc-margin-left'] : ''));
  24805. $toc_mgr = (isset($a['toc_mgr']) ? $a['toc_mgr'] : (isset($a['toc-margin-right']) ? $a['toc-margin-right'] : ''));
  24806. $toc_mgt = (isset($a['toc_mgt']) ? $a['toc_mgt'] : (isset($a['toc-margin-top']) ? $a['toc-margin-top'] : ''));
  24807. $toc_mgb = (isset($a['toc_mgb']) ? $a['toc_mgb'] : (isset($a['toc-margin-bottom']) ? $a['toc-margin-bottom'] : ''));
  24808. $toc_mgh = (isset($a['toc_mgh']) ? $a['toc_mgh'] : (isset($a['toc-margin-header']) ? $a['toc-margin-header'] : ''));
  24809. $toc_mgf = (isset($a['toc_mgf']) ? $a['toc_mgf'] : (isset($a['toc-margin-footer']) ? $a['toc-margin-footer'] : ''));
  24810. $toc_ohname = (isset($a['toc_ohname']) ? $a['toc_ohname'] : (isset($a['toc-odd-header-name']) ? $a['toc-odd-header-name'] : ''));
  24811. $toc_ehname = (isset($a['toc_ehname']) ? $a['toc_ehname'] : (isset($a['toc-even-header-name']) ? $a['toc-even-header-name'] : ''));
  24812. $toc_ofname = (isset($a['toc_ofname']) ? $a['toc_ofname'] : (isset($a['toc-odd-footer-name']) ? $a['toc-odd-footer-name'] : ''));
  24813. $toc_efname = (isset($a['toc_efname']) ? $a['toc_efname'] : (isset($a['toc-even-footer-name']) ? $a['toc-even-footer-name'] : ''));
  24814. $toc_ohvalue = (isset($a['toc_ohvalue']) ? $a['toc_ohvalue'] : (isset($a['toc-odd-header-value']) ? $a['toc-odd-header-value'] : 0));
  24815. $toc_ehvalue = (isset($a['toc_ehvalue']) ? $a['toc_ehvalue'] : (isset($a['toc-even-header-value']) ? $a['toc-even-header-value'] : 0));
  24816. $toc_ofvalue = (isset($a['toc_ofvalue']) ? $a['toc_ofvalue'] : (isset($a['toc-odd-footer-value']) ? $a['toc-odd-footer-value'] : 0));
  24817. $toc_efvalue = (isset($a['toc_efvalue']) ? $a['toc_efvalue'] : (isset($a['toc-even-footer-value']) ? $a['toc-even-footer-value'] : 0));
  24818. $toc_preHTML = (isset($a['toc_preHTML']) ? $a['toc_preHTML'] : (isset($a['toc-preHTML']) ? $a['toc-preHTML'] : ''));
  24819. $toc_postHTML = (isset($a['toc_postHTML']) ? $a['toc_postHTML'] : (isset($a['toc-postHTML']) ? $a['toc-postHTML'] : ''));
  24820. $toc_bookmarkText = (isset($a['toc_bookmarkText']) ? $a['toc_bookmarkText'] : (isset($a['toc-bookmarkText']) ? $a['toc-bookmarkText'] : ''));
  24821. $resetpagenum = (isset($a['resetpagenum']) ? $a['resetpagenum'] : '');
  24822. $pagenumstyle = (isset($a['pagenumstyle']) ? $a['pagenumstyle'] : '');
  24823. $suppress = (isset($a['suppress']) ? $a['suppress'] : '');
  24824. $orientation = (isset($a['orientation']) ? $a['orientation'] : '');
  24825. $mgl = (isset($a['mgl']) ? $a['mgl'] : (isset($a['margin-left']) ? $a['margin-left'] : ''));
  24826. $mgr = (isset($a['mgr']) ? $a['mgr'] : (isset($a['margin-right']) ? $a['margin-right'] : ''));
  24827. $mgt = (isset($a['mgt']) ? $a['mgt'] : (isset($a['margin-top']) ? $a['margin-top'] : ''));
  24828. $mgb = (isset($a['mgb']) ? $a['mgb'] : (isset($a['margin-bottom']) ? $a['margin-bottom'] : ''));
  24829. $mgh = (isset($a['mgh']) ? $a['mgh'] : (isset($a['margin-header']) ? $a['margin-header'] : ''));
  24830. $mgf = (isset($a['mgf']) ? $a['mgf'] : (isset($a['margin-footer']) ? $a['margin-footer'] : ''));
  24831. $ohname = (isset($a['ohname']) ? $a['ohname'] : (isset($a['odd-header-name']) ? $a['odd-header-name'] : ''));
  24832. $ehname = (isset($a['ehname']) ? $a['ehname'] : (isset($a['even-header-name']) ? $a['even-header-name'] : ''));
  24833. $ofname = (isset($a['ofname']) ? $a['ofname'] : (isset($a['odd-footer-name']) ? $a['odd-footer-name'] : ''));
  24834. $efname = (isset($a['efname']) ? $a['efname'] : (isset($a['even-footer-name']) ? $a['even-footer-name'] : ''));
  24835. $ohvalue = (isset($a['ohvalue']) ? $a['ohvalue'] : (isset($a['odd-header-value']) ? $a['odd-header-value'] : 0));
  24836. $ehvalue = (isset($a['ehvalue']) ? $a['ehvalue'] : (isset($a['even-header-value']) ? $a['even-header-value'] : 0));
  24837. $ofvalue = (isset($a['ofvalue']) ? $a['ofvalue'] : (isset($a['odd-footer-value']) ? $a['odd-footer-value'] : 0));
  24838. $efvalue = (isset($a['efvalue']) ? $a['efvalue'] : (isset($a['even-footer-value']) ? $a['even-footer-value'] : 0));
  24839. $toc_id = (isset($a['toc_id']) ? $a['toc_id'] : (isset($a['name']) ? $a['name'] : 0));
  24840. $pagesel = (isset($a['pagesel']) ? $a['pagesel'] : (isset($a['pageselector']) ? $a['pageselector'] : ''));
  24841. $toc_pagesel = (isset($a['toc_pagesel']) ? $a['toc_pagesel'] : (isset($a['toc-pageselector']) ? $a['toc-pageselector'] : ''));
  24842. $sheetsize = (isset($a['sheetsize']) ? $a['sheetsize'] : (isset($a['sheet-size']) ? $a['sheet-size'] : ''));
  24843. $toc_sheetsize = (isset($a['toc_sheetsize']) ? $a['toc_sheetsize'] : (isset($a['toc-sheet-size']) ? $a['toc-sheet-size'] : ''));
  24844. $this->TOCpagebreak($tocfont, $tocfontsize, $tocindent, $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent);
  24845. }
  24846. function TOCpagebreak($tocfont = '', $tocfontsize = '', $tocindent = '', $TOCusePaging = true, $TOCuseLinking = '', $toc_orientation = '', $toc_mgl = '', $toc_mgr = '', $toc_mgt = '', $toc_mgb = '', $toc_mgh = '', $toc_mgf = '', $toc_ohname = '', $toc_ehname = '', $toc_ofname = '', $toc_efname = '', $toc_ohvalue = 0, $toc_ehvalue = 0, $toc_ofvalue = 0, $toc_efvalue = 0, $toc_preHTML = '', $toc_postHTML = '', $toc_bookmarkText = '', $resetpagenum = '', $pagenumstyle = '', $suppress = '', $orientation = '', $mgl = '', $mgr = '', $mgt = '', $mgb = '', $mgh = '', $mgf = '', $ohname = '', $ehname = '', $ofname = '', $efname = '', $ohvalue = 0, $ehvalue = 0, $ofvalue = 0, $efvalue = 0, $toc_id = 0, $pagesel = '', $toc_pagesel = '', $sheetsize = '', $toc_sheetsize = '', $tocoutdent = '')
  24847. {
  24848. if (!class_exists('tocontents', false)) {
  24849. include(_MPDF_PATH . 'classes/tocontents.php');
  24850. }
  24851. if (empty($this->tocontents)) {
  24852. $this->tocontents = new tocontents($this);
  24853. }
  24854. if (!$resetpagenum) {
  24855. $resetpagenum = 1;
  24856. } // mPDF 6
  24857. //Start a new page
  24858. if ($this->state == 0)
  24859. $this->AddPage();
  24860. if ($this->y == $this->tMargin && (!$this->mirrorMargins || ($this->mirrorMargins && $this->page % 2 == 1))) {
  24861. // Don't add a page
  24862. if ($this->page == 1 && count($this->PageNumSubstitutions) == 0) {
  24863. if (!$suppress) {
  24864. $suppress = 'off';
  24865. }
  24866. //$this->PageNumSubstitutions[] = array('from'=>1, 'reset'=> $resetpagenum, 'type'=>$pagenumstyle, 'suppress'=> $suppress);
  24867. }
  24868. $this->PageNumSubstitutions[] = array('from' => $this->page, 'reset' => $resetpagenum, 'type' => $pagenumstyle, 'suppress' => $suppress);
  24869. } else {
  24870. $this->AddPage($orientation, 'NEXT-ODD', $resetpagenum, $pagenumstyle, $suppress, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $pagesel, $sheetsize);
  24871. }
  24872. $this->tocontents->TOCpagebreak($tocfont, $tocfontsize, $tocindent, $TOCusePaging, $TOCuseLinking, $toc_orientation, $toc_mgl, $toc_mgr, $toc_mgt, $toc_mgb, $toc_mgh, $toc_mgf, $toc_ohname, $toc_ehname, $toc_ofname, $toc_efname, $toc_ohvalue, $toc_ehvalue, $toc_ofvalue, $toc_efvalue, $toc_preHTML, $toc_postHTML, $toc_bookmarkText, $resetpagenum, $pagenumstyle, $suppress, $orientation, $mgl, $mgr, $mgt, $mgb, $mgh, $mgf, $ohname, $ehname, $ofname, $efname, $ohvalue, $ehvalue, $ofvalue, $efvalue, $toc_id, $pagesel, $toc_pagesel, $sheetsize, $toc_sheetsize, $tocoutdent);
  24873. }
  24874. function TOC_Entry($txt, $level = 0, $toc_id = 0)
  24875. {
  24876. if ($this->ColActive) {
  24877. $ily = $this->y0;
  24878. } else {
  24879. $ily = $this->y;
  24880. } // use top of columns
  24881. if (!class_exists('tocontents', false)) {
  24882. include(_MPDF_PATH . 'classes/tocontents.php');
  24883. }
  24884. if (empty($this->tocontents)) {
  24885. $this->tocontents = new tocontents($this);
  24886. }
  24887. $linkn = $this->AddLink();
  24888. $uid = '__mpdfinternallink_' . $linkn;
  24889. if ($this->table_rotate) {
  24890. $this->internallink[$uid] = array("Y" => $ily, "PAGE" => $this->page, "tbrot" => true);
  24891. } elseif ($this->kwt) {
  24892. $this->internallink[$uid] = array("Y" => $ily, "PAGE" => $this->page, "kwt" => true);
  24893. } elseif ($this->ColActive) {
  24894. $this->internallink[$uid] = array("Y" => $ily, "PAGE" => $this->page, "col" => $this->CurrCol);
  24895. } elseif (!$this->keep_block_together) {
  24896. $this->internallink[$uid] = array("Y" => $ily, "PAGE" => $this->page);
  24897. }
  24898. $this->internallink['#' . $uid] = $linkn;
  24899. $this->SetLink($linkn, $ily, $this->page);
  24900. if (strtoupper($toc_id) == 'ALL') {
  24901. $toc_id = '_mpdf_all';
  24902. } elseif (!$toc_id) {
  24903. $toc_id = 0;
  24904. } else {
  24905. $toc_id = strtolower($toc_id);
  24906. }
  24907. $btoc = array('t' => $txt, 'l' => $level, 'p' => $this->page, 'link' => $linkn, 'toc_id' => $toc_id);
  24908. if ($this->keep_block_together) {
  24909. // do nothing
  24910. }
  24911. /* -- TABLES -- */ elseif ($this->table_rotate) {
  24912. $this->tbrot_toc[] = $btoc;
  24913. } elseif ($this->kwt) {
  24914. $this->kwt_toc[] = $btoc;
  24915. }
  24916. /* -- END TABLES -- */ elseif ($this->ColActive) { // *COLUMNS*
  24917. $this->col_toc[] = $btoc; // *COLUMNS*
  24918. } // *COLUMNS*
  24919. else {
  24920. $this->tocontents->_toc[] = $btoc;
  24921. }
  24922. }
  24923. /* -- END TOC -- */
  24924. //======================================================
  24925. function MovePages($target_page, $start_page, $end_page = -1)
  24926. {
  24927. // move a page/pages EARLIER in the document
  24928. if ($end_page < 1) {
  24929. $end_page = $start_page;
  24930. }
  24931. $n_toc = $end_page - $start_page + 1;
  24932. // Set/Update PageNumSubstitutions changes before moving anything
  24933. if (count($this->PageNumSubstitutions)) {
  24934. $tp_present = false;
  24935. $sp_present = false;
  24936. $ep_present = false;
  24937. foreach ($this->PageNumSubstitutions AS $k => $v) {
  24938. if ($this->PageNumSubstitutions[$k]['from'] == $target_page) {
  24939. $tp_present = true;
  24940. if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
  24941. $this->PageNumSubstitutions[$k]['suppress'] = 'off';
  24942. }
  24943. }
  24944. if ($this->PageNumSubstitutions[$k]['from'] == $start_page) {
  24945. $sp_present = true;
  24946. if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
  24947. $this->PageNumSubstitutions[$k]['suppress'] = 'off';
  24948. }
  24949. }
  24950. if ($this->PageNumSubstitutions[$k]['from'] == ($end_page + 1)) {
  24951. $ep_present = true;
  24952. if ($this->PageNumSubstitutions[$k]['suppress'] != 'on' && $this->PageNumSubstitutions[$k]['suppress'] != 1) {
  24953. $this->PageNumSubstitutions[$k]['suppress'] = 'off';
  24954. }
  24955. }
  24956. }
  24957. if (!$tp_present) {
  24958. list($tp_type, $tp_suppress, $tp_reset) = $this->docPageSettings($target_page);
  24959. }
  24960. if (!$sp_present) {
  24961. list($sp_type, $sp_suppress, $sp_reset) = $this->docPageSettings($start_page);
  24962. }
  24963. if (!$ep_present) {
  24964. list($ep_type, $ep_suppress, $ep_reset) = $this->docPageSettings($start_page - 1);
  24965. }
  24966. }
  24967. $last = array();
  24968. //store pages
  24969. for ($i = $start_page; $i <= $end_page; $i++)
  24970. $last[] = $this->pages[$i];
  24971. //move pages
  24972. for ($i = $start_page - 1; $i >= ($target_page); $i--) {
  24973. $this->pages[$i + $n_toc] = $this->pages[$i];
  24974. }
  24975. //Put toc pages at insert point
  24976. for ($i = 0; $i < $n_toc; $i++) {
  24977. $this->pages[$target_page + $i] = $last[$i];
  24978. }
  24979. /* -- BOOKMARKS -- */
  24980. // Update Bookmarks
  24981. foreach ($this->BMoutlines as $i => $o) {
  24982. if ($o['p'] >= $target_page) {
  24983. $this->BMoutlines[$i]['p'] += $n_toc;
  24984. }
  24985. }
  24986. /* -- END BOOKMARKS -- */
  24987. // Update Page Links
  24988. if (count($this->PageLinks)) {
  24989. $newarr = array();
  24990. foreach ($this->PageLinks as $i => $o) {
  24991. foreach ($this->PageLinks[$i] as $key => $pl) {
  24992. if (strpos($pl[4], '@') === 0) {
  24993. $p = substr($pl[4], 1);
  24994. if ($p >= $start_page && $p <= $end_page) {
  24995. $this->PageLinks[$i][$key][4] = '@' . ($p + ($target_page - $start_page));
  24996. } elseif ($p >= $target_page && $p < $start_page) {
  24997. $this->PageLinks[$i][$key][4] = '@' . ($p + $n_toc);
  24998. }
  24999. }
  25000. }
  25001. if ($i >= $start_page && $i <= $end_page) {
  25002. $newarr[($i + ($target_page - $start_page))] = $this->PageLinks[$i];
  25003. } elseif ($i >= $target_page && $i < $start_page) {
  25004. $newarr[($i + $n_toc)] = $this->PageLinks[$i];
  25005. } else {
  25006. $newarr[$i] = $this->PageLinks[$i];
  25007. }
  25008. }
  25009. $this->PageLinks = $newarr;
  25010. }
  25011. // OrientationChanges
  25012. if (count($this->OrientationChanges)) {
  25013. $newarr = array();
  25014. foreach ($this->OrientationChanges AS $p => $v) {
  25015. if ($p >= $start_page && $p <= $end_page) {
  25016. $newarr[($p + ($target_page - $start_page))] = $this->OrientationChanges[$p];
  25017. } elseif ($p >= $target_page && $p < $start_page) {
  25018. $newarr[$p + $n_toc] = $this->OrientationChanges[$p];
  25019. } else {
  25020. $newarr[$p] = $this->OrientationChanges[$p];
  25021. }
  25022. }
  25023. ksort($newarr);
  25024. $this->OrientationChanges = $newarr;
  25025. }
  25026. // Page Dimensions
  25027. if (count($this->pageDim)) {
  25028. $newarr = array();
  25029. foreach ($this->pageDim AS $p => $v) {
  25030. if ($p >= $start_page && $p <= $end_page) {
  25031. $newarr[($p + ($target_page - $start_page))] = $this->pageDim[$p];
  25032. } elseif ($p >= $target_page && $p < $start_page) {
  25033. $newarr[$p + $n_toc] = $this->pageDim[$p];
  25034. } else {
  25035. $newarr[$p] = $this->pageDim[$p];
  25036. }
  25037. }
  25038. ksort($newarr);
  25039. $this->pageDim = $newarr;
  25040. }
  25041. // HTML Headers & Footers
  25042. if (count($this->saveHTMLHeader)) {
  25043. $newarr = array();
  25044. foreach ($this->saveHTMLHeader AS $p => $v) {
  25045. if ($p >= $start_page && $p <= $end_page) {
  25046. $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLHeader[$p];
  25047. } elseif ($p >= $target_page && $p < $start_page) {
  25048. $newarr[$p + $n_toc] = $this->saveHTMLHeader[$p];
  25049. } else {
  25050. $newarr[$p] = $this->saveHTMLHeader[$p];
  25051. }
  25052. }
  25053. ksort($newarr);
  25054. $this->saveHTMLHeader = $newarr;
  25055. }
  25056. if (count($this->saveHTMLFooter)) {
  25057. $newarr = array();
  25058. foreach ($this->saveHTMLFooter AS $p => $v) {
  25059. if ($p >= $start_page && $p <= $end_page) {
  25060. $newarr[($p + ($target_page - $start_page))] = $this->saveHTMLFooter[$p];
  25061. } elseif ($p >= $target_page && $p < $start_page) {
  25062. $newarr[$p + $n_toc] = $this->saveHTMLFooter[$p];
  25063. } else {
  25064. $newarr[$p] = $this->saveHTMLFooter[$p];
  25065. }
  25066. }
  25067. ksort($newarr);
  25068. $this->saveHTMLFooter = $newarr;
  25069. }
  25070. // Update Internal Links
  25071. if (count($this->internallink)) {
  25072. foreach ($this->internallink as $key => $o) {
  25073. if ($o['PAGE'] >= $start_page && $o['PAGE'] <= $end_page) {
  25074. $this->internallink[$key]['PAGE'] += ($target_page - $start_page);
  25075. } elseif ($o['PAGE'] >= $target_page && $o['PAGE'] < $start_page) {
  25076. $this->internallink[$key]['PAGE'] += $n_toc;
  25077. }
  25078. }
  25079. }
  25080. // Update Links
  25081. if (count($this->links)) {
  25082. foreach ($this->links as $key => $o) {
  25083. if ($o[0] >= $start_page && $o[0] <= $end_page) {
  25084. $this->links[$key][0] += ($target_page - $start_page);
  25085. }
  25086. if ($o[0] >= $target_page && $o[0] < $start_page) {
  25087. $this->links[$key][0] += $n_toc;
  25088. }
  25089. }
  25090. }
  25091. // Update Form fields
  25092. if (count($this->mpdfform->forms)) {
  25093. foreach ($this->mpdfform->forms as $key => $f) {
  25094. if ($f['page'] >= $start_page && $f['page'] <= $end_page) {
  25095. $this->mpdfform->forms[$key]['page'] += ($target_page - $start_page);
  25096. }
  25097. if ($f['page'] >= $target_page && $f['page'] < $start_page) {
  25098. $this->mpdfform->forms[$key]['page'] += $n_toc;
  25099. }
  25100. }
  25101. }
  25102. /* -- ANNOTATIONS -- */
  25103. // Update Annotations
  25104. if (count($this->PageAnnots)) {
  25105. $newarr = array();
  25106. foreach ($this->PageAnnots as $p => $anno) {
  25107. if ($p >= $start_page && $p <= $end_page) {
  25108. $np = $p + ($target_page - $start_page);
  25109. foreach ($anno as $o) {
  25110. $newarr[$np][] = $o;
  25111. }
  25112. } elseif ($p >= $target_page && $p < $start_page) {
  25113. $np = $p + $n_toc;
  25114. foreach ($anno as $o) {
  25115. $newarr[$np][] = $o;
  25116. }
  25117. } else {
  25118. $newarr[$p] = $this->PageAnnots[$p];
  25119. }
  25120. }
  25121. $this->PageAnnots = $newarr;
  25122. unset($newarr);
  25123. }
  25124. /* -- END ANNOTATIONS -- */
  25125. // Update PageNumSubstitutions
  25126. if (count($this->PageNumSubstitutions)) {
  25127. $newarr = array();
  25128. foreach ($this->PageNumSubstitutions AS $k => $v) {
  25129. if ($this->PageNumSubstitutions[$k]['from'] >= $start_page && $this->PageNumSubstitutions[$k]['from'] <= $end_page) {
  25130. $this->PageNumSubstitutions[$k]['from'] += ($target_page - $start_page);
  25131. $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
  25132. } elseif ($this->PageNumSubstitutions[$k]['from'] >= $target_page && $this->PageNumSubstitutions[$k]['from'] < $start_page) {
  25133. $this->PageNumSubstitutions[$k]['from'] += $n_toc;
  25134. $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
  25135. } else {
  25136. $newarr[$this->PageNumSubstitutions[$k]['from']] = $this->PageNumSubstitutions[$k];
  25137. }
  25138. }
  25139. if (!$sp_present) {
  25140. $newarr[$target_page] = array('from' => $target_page, 'suppress' => $sp_suppress, 'reset' => $sp_reset, 'type' => $sp_type);
  25141. }
  25142. if (!$tp_present) {
  25143. $newarr[($target_page + $n_toc)] = array('from' => ($target_page + $n_toc), 'suppress' => $tp_suppress, 'reset' => $tp_reset, 'type' => $tp_type);
  25144. }
  25145. if (!$ep_present && $end_page > count($this->pages)) {
  25146. $newarr[($end_page + 1)] = array('from' => ($end_page + 1), 'suppress' => $ep_suppress, 'reset' => $ep_reset, 'type' => $ep_type);
  25147. }
  25148. ksort($newarr);
  25149. $this->PageNumSubstitutions = array();
  25150. foreach ($newarr as $v) {
  25151. $this->PageNumSubstitutions[] = $v;
  25152. }
  25153. }
  25154. }
  25155. //======================================================
  25156. function DeletePages($start_page, $end_page = -1)
  25157. {
  25158. // move a page/pages EARLIER in the document
  25159. if ($end_page < 1) {
  25160. $end_page = $start_page;
  25161. }
  25162. $n_tod = $end_page - $start_page + 1;
  25163. $last_page = count($this->pages);
  25164. $n_atend = $last_page - $end_page + 1;
  25165. //move pages
  25166. for ($i = 0; $i < $n_atend; $i++) {
  25167. $this->pages[$start_page + $i] = $this->pages[$end_page + 1 + $i];
  25168. }
  25169. //delete pages
  25170. for ($i = 0; $i < $n_tod; $i++)
  25171. unset($this->pages[$last_page - $i]);
  25172. /* -- BOOKMARKS -- */
  25173. // Update Bookmarks
  25174. foreach ($this->BMoutlines as $i => $o) {
  25175. if ($o['p'] >= $end_page) {
  25176. $this->BMoutlines[$i]['p'] -= $n_tod;
  25177. } elseif ($p < $start_page) {
  25178. unset($this->BMoutlines[$i]);
  25179. }
  25180. }
  25181. /* -- END BOOKMARKS -- */
  25182. // Update Page Links
  25183. if (count($this->PageLinks)) {
  25184. $newarr = array();
  25185. foreach ($this->PageLinks as $i => $o) {
  25186. foreach ($this->PageLinks[$i] as $key => $pl) {
  25187. if (strpos($pl[4], '@') === 0) {
  25188. $p = substr($pl[4], 1);
  25189. if ($p > $end_page) {
  25190. $this->PageLinks[$i][$key][4] = '@' . ($p - $n_tod);
  25191. } elseif ($p < $start_page) {
  25192. unset($this->PageLinks[$i][$key]);
  25193. }
  25194. }
  25195. }
  25196. if ($i > $end_page) {
  25197. $newarr[($i - $n_tod)] = $this->PageLinks[$i];
  25198. } elseif ($p < $start_page) {
  25199. $newarr[$i] = $this->PageLinks[$i];
  25200. }
  25201. }
  25202. $this->PageLinks = $newarr;
  25203. }
  25204. // OrientationChanges
  25205. if (count($this->OrientationChanges)) {
  25206. $newarr = array();
  25207. foreach ($this->OrientationChanges AS $p => $v) {
  25208. if ($p > $end_page) {
  25209. $newarr[($p - $t_tod)] = $this->OrientationChanges[$p];
  25210. } elseif ($p < $start_page) {
  25211. $newarr[$p] = $this->OrientationChanges[$p];
  25212. }
  25213. }
  25214. ksort($newarr);
  25215. $this->OrientationChanges = $newarr;
  25216. }
  25217. // Page Dimensions
  25218. if (count($this->pageDim)) {
  25219. $newarr = array();
  25220. foreach ($this->pageDim AS $p => $v) {
  25221. if ($p > $end_page) {
  25222. $newarr[($p - $n_tod)] = $this->pageDim[$p];
  25223. } elseif ($p < $start_page) {
  25224. $newarr[$p] = $this->pageDim[$p];
  25225. }
  25226. }
  25227. ksort($newarr);
  25228. $this->pageDim = $newarr;
  25229. }
  25230. // HTML Headers & Footers
  25231. if (count($this->saveHTMLHeader)) {
  25232. foreach ($this->saveHTMLHeader AS $p => $v) {
  25233. if ($p > $end_page) {
  25234. $newarr[($p - $n_tod)] = $this->saveHTMLHeader[$p];
  25235. } // mPDF 5.7.3
  25236. elseif ($p < $start_page) {
  25237. $newarr[$p] = $this->saveHTMLHeader[$p];
  25238. }
  25239. }
  25240. ksort($newarr);
  25241. $this->saveHTMLHeader = $newarr;
  25242. }
  25243. if (count($this->saveHTMLFooter)) {
  25244. $newarr = array();
  25245. foreach ($this->saveHTMLFooter AS $p => $v) {
  25246. if ($p > $end_page) {
  25247. $newarr[($p - $n_tod)] = $this->saveHTMLFooter[$p];
  25248. } elseif ($p < $start_page) {
  25249. $newarr[$p] = $this->saveHTMLFooter[$p];
  25250. }
  25251. }
  25252. ksort($newarr);
  25253. $this->saveHTMLFooter = $newarr;
  25254. }
  25255. // Update Internal Links
  25256. foreach ($this->internallink as $key => $o) {
  25257. if ($o['PAGE'] > $end_page) {
  25258. $this->internallink[$key]['PAGE'] -= $n_tod;
  25259. } elseif ($o['PAGE'] < $start_page) {
  25260. unset($this->internallink[$key]);
  25261. }
  25262. }
  25263. // Update Links
  25264. foreach ($this->links as $key => $o) {
  25265. if ($o[0] > $end_page) {
  25266. $this->links[$key][0] -= $n_tod;
  25267. } elseif ($o[0] < $start_page) {
  25268. unset($this->links[$key]);
  25269. }
  25270. }
  25271. // Update Form fields
  25272. foreach ($this->mpdfform->forms as $key => $f) {
  25273. if ($f['page'] > $end_page) {
  25274. $this->mpdfform->forms[$key]['page'] -= $n_tod;
  25275. } elseif ($f['page'] < $start_page) {
  25276. unset($this->mpdfform->forms[$key]);
  25277. }
  25278. }
  25279. /* -- ANNOTATIONS -- */
  25280. // Update Annotations
  25281. if (count($this->PageAnnots)) {
  25282. $newarr = array();
  25283. foreach ($this->PageAnnots as $p => $anno) {
  25284. if ($p > $end_page) {
  25285. foreach ($anno as $o) {
  25286. $newarr[($p - $n_tod)][] = $o;
  25287. }
  25288. } elseif ($p < $start_page) {
  25289. $newarr[$p] = $this->PageAnnots[$p];
  25290. }
  25291. }
  25292. ksort($newarr);
  25293. $this->PageAnnots = $newarr;
  25294. }
  25295. /* -- END ANNOTATIONS -- */
  25296. // Update PageNumSubstitutions
  25297. foreach ($this->PageNumSubstitutions AS $k => $v) {
  25298. if ($this->PageNumSubstitutions[$k]['from'] > $end_page) {
  25299. $this->PageNumSubstitutions[$k]['from'] -= $n_tod;
  25300. } elseif ($this->PageNumSubstitutions[$k]['from'] < $start_page) {
  25301. unset($this->PageNumSubstitutions[$k]);
  25302. }
  25303. }
  25304. unset($newarr);
  25305. $this->page = count($this->pages);
  25306. }
  25307. //======================================================
  25308. /* -- INDEX -- */
  25309. // FROM class PDF_Ref == INDEX
  25310. function IndexEntry($txt, $xref = '')
  25311. {
  25312. if ($xref) {
  25313. $this->IndexEntrySee($txt, $xref);
  25314. return;
  25315. }
  25316. //Search the reference (AND Ref/PageNo) in the array
  25317. $Present = false;
  25318. if ($this->keep_block_together) {
  25319. // do nothing
  25320. }
  25321. /* -- TABLES -- */ elseif ($this->kwt) {
  25322. $size = count($this->kwt_Reference);
  25323. for ($i = 0; $i < $size; $i++) {
  25324. if (isset($this->kwt_Reference[$i]['t']) && $this->kwt_Reference[$i]['t'] == $txt) {
  25325. $Present = true;
  25326. if ($this->page != $this->kwt_Reference[$i]['op']) {
  25327. $this->kwt_Reference[$i]['op'] = $this->page;
  25328. }
  25329. }
  25330. }
  25331. if (!$Present) { //If not found, add it
  25332. $this->kwt_Reference[] = array('t' => $txt, 'op' => $this->page);
  25333. }
  25334. }
  25335. /* -- END TABLES -- */ else {
  25336. $size = count($this->Reference);
  25337. for ($i = 0; $i < $size; $i++) {
  25338. if (isset($this->Reference[$i]['t']) && $this->Reference[$i]['t'] == $txt) {
  25339. $Present = true;
  25340. if (!in_array($this->page, $this->Reference[$i]['p'])) {
  25341. $this->Reference[$i]['p'][] = $this->page;
  25342. }
  25343. }
  25344. }
  25345. if (!$Present) { //If not found, add it
  25346. $this->Reference[] = array('t' => $txt, 'p' => array($this->page));
  25347. }
  25348. }
  25349. }
  25350. // Added function to add a reference "Elephants. See Chickens"
  25351. function IndexEntrySee($txta, $txtb)
  25352. {
  25353. if ($this->directionality == 'rtl') { // *OTL*
  25354. // ONLY DO THIS IF NOT IN TAGS
  25355. if ($txta == strip_tags($txta))
  25356. $txta = str_replace(':', ' - ', $txta); // *OTL*
  25357. if ($txtb == strip_tags($txtb))
  25358. $txtb = str_replace(':', ' - ', $txtb); // *OTL*
  25359. } // *OTL*
  25360. else { // *OTL*
  25361. if ($txta == strip_tags($txta))
  25362. $txta = str_replace(':', ', ', $txta);
  25363. if ($txtb == strip_tags($txtb))
  25364. $txtb = str_replace(':', ', ', $txtb);
  25365. } // *OTL*
  25366. $this->Reference[] = array('t' => $txta . ' - see ' . $txtb, 'p' => array());
  25367. }
  25368. function InsertIndex($usedivletters = 1, $useLinking = false, $indexCollationLocale = '', $indexCollationGroup = '')
  25369. {
  25370. $size = count($this->Reference);
  25371. if ($size == 0) {
  25372. return false;
  25373. }
  25374. // $spacer used after named entry
  25375. // $sep separates number [groups], $joiner joins numbers in range
  25376. // e.g. "elephant 73, 97-99" = elephant[$spacer]73[$sep]97[$joiner]99
  25377. // $subEntrySeparator separates main and subentry (if $this->indexUseSubentries == false;) e.g.
  25378. // Mammal:elephant => Mammal[$subEntrySeparator]elephant
  25379. // $subEntryInset specifies what precedes a subentry (if $this->indexUseSubentries == true;) e.g.
  25380. // Mammal:elephant => [$subEntryInset]elephant
  25381. $spacer = "\xc2\xa0 ";
  25382. if ($this->directionality == 'rtl') {
  25383. $sep = '&#x060c; ';
  25384. $joiner = '-';
  25385. $subEntrySeparator = '&#x060c; ';
  25386. $subEntryInset = ' - ';
  25387. } else {
  25388. $sep = ', ';
  25389. $joiner = '-';
  25390. $subEntrySeparator = ', ';
  25391. $subEntryInset = ' - ';
  25392. }
  25393. for ($i = 0; $i < $size; $i++) {
  25394. $txt = $this->Reference[$i]['t'];
  25395. $txt = strip_tags($txt); // mPDF 6
  25396. $txt = $this->purify_utf8($txt);
  25397. $this->Reference[$i]['uf'] = $txt; // Unformatted e.g. pure utf-8 encoded characters, no mark-up/tags
  25398. // Used for ordering and collation
  25399. }
  25400. if ($usedivletters) {
  25401. if ($indexCollationGroup) {
  25402. require_once(_MPDF_PATH . 'collations/' . $indexCollationGroup . '.php');
  25403. } else {
  25404. $collation = array();
  25405. }
  25406. for ($i = 0; $i < $size; $i++) {
  25407. if ($this->Reference[$i]['uf']) {
  25408. $l = mb_substr($this->Reference[$i]['uf'], 0, 1, 'UTF-8');
  25409. if (isset($this->indexCollationGroup) && $this->indexCollationGroup) {
  25410. $uni = $this->UTF8StringToArray($l);
  25411. $ucode = $uni[0];
  25412. if (isset($collation[$ucode])) {
  25413. $this->Reference[$i]['d'] = code2utf($collation[$ucode]);
  25414. } else {
  25415. $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
  25416. }
  25417. } else {
  25418. $this->Reference[$i]['d'] = mb_strtolower($l, 'UTF-8');
  25419. }
  25420. }
  25421. }
  25422. }
  25423. if (!function_exists('cmp')) {
  25424. function cmp($a, $b)
  25425. {
  25426. return strcoll(strtolower($a['uf']), strtolower($b['uf']));
  25427. }
  25428. }
  25429. //Alphabetic sort of the references
  25430. $originalLocale = setlocale(LC_COLLATE, 0);
  25431. if ($indexCollationLocale) {
  25432. setlocale(LC_COLLATE, $indexCollationLocale);
  25433. }
  25434. usort($this->Reference, 'cmp');
  25435. if ($indexCollationLocale) {
  25436. setlocale(LC_COLLATE, $originalLocale);
  25437. }
  25438. $html = '<div class="mpdf_index_main">';
  25439. $lett = '';
  25440. $last_lett = '';
  25441. $mainentry = '';
  25442. for ($i = 0; $i < $size; $i++) {
  25443. if ($this->Reference[$i]['t']) {
  25444. if ($usedivletters) {
  25445. $lett = $this->Reference[$i]['d'];
  25446. if ($lett != $last_lett) {
  25447. $html .= '<div class="mpdf_index_letter">' . $lett . '</div>';
  25448. }
  25449. }
  25450. $txt = $this->Reference[$i]['t'];
  25451. // Sub-entries e.g. Mammals:elephant
  25452. // But allow for tags e.g. <b>Mammal</b>:elephants
  25453. $a = preg_split('/(<.*?>)/', $txt, -1, PREG_SPLIT_DELIM_CAPTURE);
  25454. $txt = '';
  25455. $marker = false;
  25456. foreach ($a as $k => $e) {
  25457. if ($k % 2 == 0 && !$marker) {
  25458. if (strpos($e, ':') !== false) { // == SubEntry
  25459. if ($this->indexUseSubentries) {
  25460. // If the Main entry does not have any page numbers associated with it
  25461. // create and insert an entry
  25462. list($txtmain, $sub) = preg_split('/[:]/', $e, 2);
  25463. if (strip_tags($txt . $txtmain) != $mainentry) {
  25464. $html .= '<div class="mpdf_index_entry">' . $txt . $txtmain . '</div>';
  25465. $mainentry = strip_tags($txt . $txtmain);
  25466. }
  25467. $txt = $subEntryInset;
  25468. $e = $sub; // Only replace first one
  25469. } else {
  25470. $e = preg_replace('/[:]/', $subEntrySeparator, $e, 1); // Only replace first one
  25471. }
  25472. $marker = true; // Don't replace any more once the subentry marker has been found
  25473. }
  25474. }
  25475. $txt .= $e;
  25476. }
  25477. if (!$marker) {
  25478. $mainentry = strip_tags($txt);
  25479. }
  25480. $html .= '<div class="mpdf_index_entry">';
  25481. $html .= $txt;
  25482. $ppp = $this->Reference[$i]['p']; // = array of page numbers to point to
  25483. if (count($ppp)) {
  25484. sort($ppp);
  25485. $newarr = array();
  25486. $range_start = $ppp[0];
  25487. $range_end = 0;
  25488. $html .= $spacer;
  25489. for ($zi = 1; $zi < count($ppp); $zi++) {
  25490. if ($ppp[$zi] == ($ppp[($zi - 1)] + 1)) {
  25491. $range_end = $ppp[$zi];
  25492. } else {
  25493. if ($range_end) {
  25494. if ($range_end == $range_start + 1) {
  25495. if ($useLinking) {
  25496. $html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
  25497. }
  25498. $html .= $this->docPageNum($range_start);
  25499. if ($useLinking) {
  25500. $html .= '</a>';
  25501. }
  25502. $html .= $sep;
  25503. if ($useLinking) {
  25504. $html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
  25505. }
  25506. $html .= $this->docPageNum($ppp[$zi - 1]);
  25507. if ($useLinking) {
  25508. $html .= '</a>';
  25509. }
  25510. $html .= $sep;
  25511. }
  25512. } else {
  25513. if ($useLinking) {
  25514. $html .= '<a class="mpdf_index_link" href="@' . $ppp[$zi - 1] . '">';
  25515. }
  25516. $html .= $this->docPageNum($ppp[$zi - 1]);
  25517. if ($useLinking) {
  25518. $html .= '</a>';
  25519. }
  25520. $html .= $sep;
  25521. }
  25522. $range_start = $ppp[$zi];
  25523. $range_end = 0;
  25524. }
  25525. }
  25526. if ($range_end) {
  25527. if ($useLinking) {
  25528. $html .= '<a class="mpdf_index_link" href="@' . $range_start . '">';
  25529. }
  25530. $html .= $this->docPageNum($range_start);
  25531. if ($range_end == $range_start + 1) {
  25532. if ($useLinking) {
  25533. $html .= '</a>';
  25534. }
  25535. $html .= $sep;
  25536. if ($useLinking) {
  25537. $html .= '<a class="mpdf_index_link" href="@' . $range_end . '">';
  25538. }
  25539. $html .= $this->docPageNum($range_end);
  25540. if ($useLinking) {
  25541. $html .= '</a>';
  25542. }
  25543. } else {
  25544. $html .= $joiner;
  25545. $html .= $this->docPageNum($range_end);
  25546. if ($useLinking) {
  25547. $html .= '</a>';
  25548. }
  25549. }
  25550. } else {
  25551. if ($useLinking) {
  25552. $html .= '<a class="mpdf_index_link" href="@' . $ppp[(count($ppp) - 1)] . '">';
  25553. }
  25554. $html .= $this->docPageNum($ppp[(count($ppp) - 1)]);
  25555. if ($useLinking) {
  25556. $html .= '</a>';
  25557. }
  25558. }
  25559. }
  25560. }
  25561. $html .= '</div>';
  25562. $last_lett = $lett;
  25563. }
  25564. $html .= '</div>';
  25565. $save_fpb = $this->fixedPosBlockSave;
  25566. $this->WriteHTML($html);
  25567. $this->fixedPosBlockSave = $save_fpb;
  25568. $this->breakpoints[$this->CurrCol][] = $this->y; // *COLUMNS*
  25569. }
  25570. /* -- END INDEX -- */
  25571. function AcceptPageBreak()
  25572. {
  25573. if (count($this->cellBorderBuffer)) {
  25574. $this->printcellbuffer();
  25575. } // *TABLES*
  25576. /* -- COLUMNS -- */
  25577. if ($this->ColActive == 1) {
  25578. if ($this->CurrCol < $this->NbCol - 1) {
  25579. //Go to the next column
  25580. $this->CurrCol++;
  25581. $this->SetCol($this->CurrCol);
  25582. $this->y = $this->y0;
  25583. $this->ChangeColumn = 1; // Number (and direction) of columns changed +1, +2, -2 etc.
  25584. // DIRECTIONALITY RTL
  25585. if ($this->directionality == 'rtl') {
  25586. $this->ChangeColumn = -($this->ChangeColumn);
  25587. } // *OTL*
  25588. //Stay on the page
  25589. return false;
  25590. } else {
  25591. //Go back to the first column - NEW PAGE
  25592. if (count($this->columnbuffer)) {
  25593. $this->printcolumnbuffer();
  25594. }
  25595. $this->SetCol(0);
  25596. $this->y0 = $this->tMargin;
  25597. $this->ChangeColumn = -($this->NbCol - 1);
  25598. // DIRECTIONALITY RTL
  25599. if ($this->directionality == 'rtl') {
  25600. $this->ChangeColumn = -($this->ChangeColumn);
  25601. } // *OTL*
  25602. //Page break
  25603. return true;
  25604. }
  25605. }
  25606. /* -- END COLUMNS -- */
  25607. /* -- TABLES -- */ elseif ($this->table_rotate) {
  25608. if ($this->tablebuffer) {
  25609. $this->printtablebuffer();
  25610. }
  25611. return true;
  25612. }
  25613. /* -- END TABLES -- */ else { // *COLUMNS*
  25614. $this->ChangeColumn = 0;
  25615. return $this->autoPageBreak;
  25616. } // *COLUMNS*
  25617. return $this->autoPageBreak;
  25618. }
  25619. //----------- COLUMNS ---------------------
  25620. /* -- COLUMNS -- */
  25621. function SetColumns($NbCol, $vAlign = '', $gap = 5)
  25622. {
  25623. // NbCol = number of columns
  25624. // Anything less than 2 turns columns off
  25625. if ($NbCol < 2) { // SET COLUMNS OFF
  25626. if ($this->ColActive) {
  25627. $this->ColActive = 0;
  25628. if (count($this->columnbuffer)) {
  25629. $this->printcolumnbuffer();
  25630. }
  25631. $this->NbCol = 1;
  25632. $this->ResetMargins();
  25633. $this->pgwidth = $this->w - $this->lMargin - $this->rMargin;
  25634. $this->divwidth = 0;
  25635. $this->Ln();
  25636. }
  25637. $this->ColActive = 0;
  25638. $this->columnbuffer = array();
  25639. $this->ColDetails = array();
  25640. $this->columnLinks = array();
  25641. $this->columnAnnots = array();
  25642. $this->columnForms = array();
  25643. $this->col_BMoutlines = array();
  25644. $this->col_toc = array();
  25645. $this->breakpoints = array();
  25646. } else { // SET COLUMNS ON
  25647. if ($this->ColActive) {
  25648. $this->ColActive = 0;
  25649. if (count($this->columnbuffer)) {
  25650. $this->printcolumnbuffer();
  25651. }
  25652. $this->ResetMargins();
  25653. }
  25654. if (isset($this->y) && $this->y > $this->tMargin)
  25655. $this->Ln();
  25656. $this->NbCol = $NbCol;
  25657. $this->ColGap = $gap;
  25658. $this->divwidth = 0;
  25659. $this->ColActive = 1;
  25660. $this->ColumnAdjust = true; // enables column height adjustment for the page
  25661. $this->columnbuffer = array();
  25662. $this->ColDetails = array();
  25663. $this->columnLinks = array();
  25664. $this->columnAnnots = array();
  25665. $this->columnForms = array();
  25666. $this->col_BMoutlines = array();
  25667. $this->col_toc = array();
  25668. $this->breakpoints = array();
  25669. if ((strtoupper($vAlign) == 'J') || (strtoupper($vAlign) == 'JUSTIFY')) {
  25670. $vAlign = 'J';
  25671. } else {
  25672. $vAlign = '';
  25673. }
  25674. $this->colvAlign = $vAlign;
  25675. //Save the ordinate
  25676. $absL = $this->DeflMargin - ($gap / 2);
  25677. $absR = $this->DefrMargin - ($gap / 2);
  25678. $PageWidth = $this->w - $absL - $absR; // virtual pagewidth for calculation only
  25679. $ColWidth = (($PageWidth - ($gap * ($NbCol))) / $NbCol);
  25680. $this->ColWidth = $ColWidth;
  25681. /* -- OTL -- */
  25682. if ($this->directionality == 'rtl') {
  25683. for ($i = 0; $i < $this->NbCol; $i++) {
  25684. $this->ColL[$i] = $absL + ($gap / 2) + (($NbCol - ($i + 1)) * ($PageWidth / $NbCol));
  25685. $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
  25686. }
  25687. } else {
  25688. /* -- END OTL -- */
  25689. for ($i = 0; $i < $this->NbCol; $i++) {
  25690. $this->ColL[$i] = $absL + ($gap / 2) + ($i * ($PageWidth / $NbCol) );
  25691. $this->ColR[$i] = $this->ColL[$i] + $ColWidth; // NB This is not R margin -> R pos
  25692. }
  25693. } // *OTL*
  25694. $this->pgwidth = $ColWidth;
  25695. $this->SetCol(0);
  25696. $this->y0 = $this->y;
  25697. }
  25698. $this->x = $this->lMargin;
  25699. }
  25700. function SetCol($CurrCol)
  25701. {
  25702. // Used internally to set column by number: 0 is 1st column
  25703. //Set position on a column
  25704. $this->CurrCol = $CurrCol;
  25705. $x = $this->ColL[$CurrCol];
  25706. $xR = $this->ColR[$CurrCol]; // NB This is not R margin -> R pos
  25707. if (($this->mirrorMargins) && (($this->page) % 2 == 0)) { // EVEN
  25708. $x += $this->MarginCorrection;
  25709. $xR += $this->MarginCorrection;
  25710. }
  25711. $this->SetMargins($x, ($this->w - $xR), $this->tMargin);
  25712. }
  25713. function AddColumn()
  25714. {
  25715. $this->NewColumn();
  25716. $this->ColumnAdjust = false; // disables all column height adjustment for the page.
  25717. }
  25718. function NewColumn()
  25719. {
  25720. if ($this->ColActive == 1) {
  25721. if ($this->CurrCol < $this->NbCol - 1) {
  25722. //Go to the next column
  25723. $this->CurrCol++;
  25724. $this->SetCol($this->CurrCol);
  25725. $this->y = $this->y0;
  25726. $this->ChangeColumn = 1;
  25727. // DIRECTIONALITY RTL
  25728. if ($this->directionality == 'rtl') {
  25729. $this->ChangeColumn = -($this->ChangeColumn);
  25730. } // *OTL*
  25731. //Stay on the page
  25732. } else {
  25733. //Go back to the first column
  25734. //Page break
  25735. if (count($this->columnbuffer)) {
  25736. $this->printcolumnbuffer();
  25737. }
  25738. $this->AddPage($this->CurOrientation);
  25739. $this->SetCol(0);
  25740. $this->y0 = $this->tMargin;
  25741. $this->ChangeColumn = -($this->NbCol - 1);
  25742. // DIRECTIONALITY RTL
  25743. if ($this->directionality == 'rtl') {
  25744. $this->ChangeColumn = -($this->ChangeColumn);
  25745. } // *OTL*
  25746. }
  25747. $this->x = $this->lMargin;
  25748. } else {
  25749. $this->AddPage($this->CurOrientation);
  25750. }
  25751. }
  25752. function printcolumnbuffer()
  25753. {
  25754. // Columns ended (but page not ended) -> try to match all columns - unless disabled by using a custom column-break
  25755. if (!$this->ColActive && $this->ColumnAdjust && !$this->keepColumns) {
  25756. // Calculate adjustment to add to each column to calculate rel_y value
  25757. $this->ColDetails[0]['add_y'] = 0;
  25758. $last_col = 0;
  25759. // Recursively add previous column's height
  25760. for ($i = 1; $i < $this->NbCol; $i++) {
  25761. if (isset($this->ColDetails[$i]['bottom_margin']) && $this->ColDetails[$i]['bottom_margin']) { // If any entries in the column
  25762. $this->ColDetails[$i]['add_y'] = ($this->ColDetails[$i - 1]['bottom_margin'] - $this->y0) + $this->ColDetails[$i - 1]['add_y'];
  25763. $last_col = $i; // Last column actually printed
  25764. }
  25765. }
  25766. // Calculate value for each position sensitive entry as though for one column
  25767. foreach ($this->columnbuffer AS $key => $s) {
  25768. $t = $s['s'];
  25769. if ($t == 'ACROFORM') {
  25770. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25771. $this->columnbuffer[$key]['s'] = '';
  25772. } elseif (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) {
  25773. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25774. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) {
  25775. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25776. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) {
  25777. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25778. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) {
  25779. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25780. } elseif (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) {
  25781. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25782. } elseif (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t)) {
  25783. $this->columnbuffer[$key]['rel_y'] = $s['y'] + $this->ColDetails[$s['col']]['add_y'] - $this->y0;
  25784. }
  25785. }
  25786. foreach ($this->internallink AS $key => $f) {
  25787. if (is_array($f) && isset($f['col'])) {
  25788. $this->internallink[$key]['rel_y'] = $f['Y'] + $this->ColDetails[$f['col']]['add_y'] - $this->y0;
  25789. }
  25790. }
  25791. $breaks = array();
  25792. foreach ($this->breakpoints AS $c => $bpa) {
  25793. foreach ($bpa AS $rely) {
  25794. $breaks[] = $rely + $this->ColDetails[$c]['add_y'] - $this->y0;
  25795. }
  25796. }
  25797. if (isset($this->ColDetails[$last_col]['bottom_margin'])) {
  25798. $lcbm = $this->ColDetails[$last_col]['bottom_margin'];
  25799. } else {
  25800. $lcbm = 0;
  25801. }
  25802. $sum_h = $this->ColDetails[$last_col]['add_y'] + $lcbm - $this->y0;
  25803. //$sum_h = max($this->ColDetails[$last_col]['add_y'] + $this->ColDetails[$last_col]['bottom_margin'] - $this->y0, end($breaks));
  25804. $target_h = ($sum_h / $this->NbCol);
  25805. $cbr = array();
  25806. for ($i = 1; $i < $this->NbCol; $i++) {
  25807. $th = ($sum_h * $i / $this->NbCol);
  25808. foreach ($breaks AS $bk => $val) {
  25809. if ($val > $th) {
  25810. if (($val - $th) < ($th - $breaks[$bk - 1])) {
  25811. $cbr[$i - 1] = $val;
  25812. } else {
  25813. $cbr[$i - 1] = $breaks[$bk - 1];
  25814. }
  25815. break;
  25816. }
  25817. }
  25818. }
  25819. $cbr[($this->NbCol - 1)] = $sum_h;
  25820. // mPDF 6
  25821. // Avoid outputing with 1st column empty
  25822. if (isset($cbr[0]) && $cbr[0] == 0) {
  25823. for ($i = 0; $i < $this->NbCol - 1; $i++) {
  25824. $cbr[$i] = $cbr[$i + 1];
  25825. }
  25826. }
  25827. // Now update the columns - divide into columns of approximately equal value
  25828. $last_new_col = 0;
  25829. $yadj = 0; // mm
  25830. $xadj = 0;
  25831. $last_col_bottom = 0;
  25832. $lowest_bottom_y = 0;
  25833. $block_bottom = 0;
  25834. $newcolumn = 0;
  25835. foreach ($this->columnbuffer AS $key => $s) {
  25836. if (isset($s['rel_y'])) { // only process position sensitive data
  25837. if ($s['rel_y'] >= $cbr[$newcolumn]) {
  25838. $newcolumn++;
  25839. } else {
  25840. $newcolumn = $last_new_col;
  25841. }
  25842. $block_bottom = max($block_bottom, ($s['rel_y'] + $s['h']));
  25843. if ($this->directionality == 'rtl') { // *OTL*
  25844. $xadj = -(($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap)); // *OTL*
  25845. } // *OTL*
  25846. else { // *OTL*
  25847. $xadj = ($newcolumn - $s['col']) * ($this->ColWidth + $this->ColGap);
  25848. } // *OTL*
  25849. if ($last_new_col != $newcolumn) { // Added new column
  25850. $last_col_bottom = $this->columnbuffer[$key]['rel_y'];
  25851. $block_bottom = 0;
  25852. }
  25853. $yadj = ($s['rel_y'] - $s['y']) - ($last_col_bottom) + $this->y0;
  25854. // callback function
  25855. $t = $s['s'];
  25856. // mPDF 5.7+
  25857. $t = $this->columnAdjustPregReplace('Td', $xadj, $yadj, '/BT (\d+\.\d\d+) (\d+\.\d\d+) Td/', $t);
  25858. $t = $this->columnAdjustPregReplace('re', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) ([\-]{0,1}\d+\.\d\d+) re/', $t);
  25859. $t = $this->columnAdjustPregReplace('l', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) l/', $t);
  25860. $t = $this->columnAdjustPregReplace('img', $xadj, $yadj, '/q (\d+\.\d\d+) 0 0 (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) cm \/(I|FO)/', $t);
  25861. $t = $this->columnAdjustPregReplace('draw', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) m/', $t);
  25862. $t = $this->columnAdjustPregReplace('bezier', $xadj, $yadj, '/(\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) (\d+\.\d\d+) c/', $t);
  25863. $this->columnbuffer[$key]['s'] = $t;
  25864. $this->columnbuffer[$key]['newcol'] = $newcolumn;
  25865. $this->columnbuffer[$key]['newy'] = $s['y'] + $yadj;
  25866. $last_new_col = $newcolumn;
  25867. $clb = $s['y'] + $yadj + $s['h']; // bottom_margin of current
  25868. if ((isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb > $this->ColDetails[$newcolumn]['max_bottom']) || (!isset($this->ColDetails[$newcolumn]['max_bottom']) && $clb)) {
  25869. $this->ColDetails[$newcolumn]['max_bottom'] = $clb;
  25870. }
  25871. if ($clb > $lowest_bottom_y) {
  25872. $lowest_bottom_y = $clb;
  25873. }
  25874. // Adjust LINKS
  25875. if (isset($this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  25876. $ref = $this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  25877. $this->PageLinks[$this->page][$ref][0] += ($xadj * _MPDFK);
  25878. $this->PageLinks[$this->page][$ref][1] -= ($yadj * _MPDFK);
  25879. unset($this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  25880. }
  25881. // Adjust FORM FIELDS
  25882. if (isset($this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  25883. $ref = $this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  25884. $this->mpdfform->forms[$ref]['x'] += ($xadj);
  25885. $this->mpdfform->forms[$ref]['y'] += ($yadj);
  25886. unset($this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  25887. }
  25888. /* -- ANNOTATIONS -- */
  25889. if (isset($this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  25890. $ref = $this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  25891. if ($this->PageAnnots[$this->page][$ref]['x'] < 0) {
  25892. $this->PageAnnots[$this->page][$ref]['x'] -= ($xadj);
  25893. } else {
  25894. $this->PageAnnots[$this->page][$ref]['x'] += ($xadj);
  25895. }
  25896. $this->PageAnnots[$this->page][$ref]['y'] += ($yadj); // unlike PageLinks, Page annots has y values from top in mm
  25897. unset($this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  25898. }
  25899. /* -- END ANNOTATIONS -- */
  25900. }
  25901. }
  25902. /* -- BOOKMARKS -- */
  25903. // Adjust Bookmarks
  25904. foreach ($this->col_BMoutlines AS $v) {
  25905. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']);
  25906. }
  25907. /* -- END BOOKMARKS -- */
  25908. /* -- TOC -- */
  25909. // Adjust ToC
  25910. foreach ($this->col_toc AS $v) {
  25911. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  25912. $this->links[$v['link']][1] = $this->y0;
  25913. }
  25914. /* -- END TOC -- */
  25915. // Adjust column length to be equal
  25916. if ($this->colvAlign == 'J') {
  25917. foreach ($this->columnbuffer AS $key => $s) {
  25918. if (isset($s['rel_y'])) { // only process position sensitive data
  25919. // Set ratio to expand y values or heights
  25920. if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
  25921. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
  25922. } else {
  25923. $ratio = 1;
  25924. }
  25925. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  25926. $yadj = ($s['newy'] - $this->y0) * ($ratio - 1);
  25927. // Adjust LINKS
  25928. if (isset($this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  25929. $ref = $this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  25930. $this->PageLinks[$this->page][$ref][1] -= ($yadj * _MPDFK); // y value
  25931. $this->PageLinks[$this->page][$ref][3] *= $ratio; // height
  25932. unset($this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  25933. }
  25934. // Adjust FORM FIELDS
  25935. if (isset($this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  25936. $ref = $this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  25937. $this->mpdfform->forms[$ref]['x'] += ($xadj);
  25938. $this->mpdfform->forms[$ref]['y'] += ($yadj);
  25939. unset($this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  25940. }
  25941. /* -- ANNOTATIONS -- */
  25942. if (isset($this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  25943. $ref = $this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  25944. $this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
  25945. unset($this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  25946. }
  25947. /* -- END ANNOTATIONS -- */
  25948. }
  25949. }
  25950. }
  25951. foreach ($this->internallink AS $key => $f) {
  25952. if (is_array($f) && isset($f['col'])) {
  25953. $last_col_bottom = 0;
  25954. for ($nbc = 0; $nbc < $this->NbCol; $nbc++) {
  25955. if ($f['rel_y'] >= $cbr[$nbc]) {
  25956. $last_col_bottom = $cbr[$nbc];
  25957. }
  25958. }
  25959. $yadj = ($f['rel_y'] - $f['Y']) - $last_col_bottom + $this->y0;
  25960. $f['Y'] += $yadj;
  25961. unset($f['col']);
  25962. unset($f['rel_y']);
  25963. $this->internallink[$key] = $f;
  25964. }
  25965. }
  25966. $last_col = -1;
  25967. $trans_on = false;
  25968. foreach ($this->columnbuffer AS $key => $s) {
  25969. if (isset($s['rel_y'])) { // only process position sensitive data
  25970. // Set ratio to expand y values or heights
  25971. if (isset($this->ColDetails[$s['newcol']]['max_bottom']) && $this->ColDetails[$s['newcol']]['max_bottom'] && $this->ColDetails[$s['newcol']]['max_bottom'] != $this->y0) {
  25972. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['newcol']]['max_bottom'] - ($this->y0));
  25973. } else {
  25974. $ratio = 1;
  25975. }
  25976. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  25977. //Start Transformation
  25978. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  25979. $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
  25980. $trans_on = true;
  25981. }
  25982. }
  25983. // Now output the adjusted values
  25984. $this->pages[$this->page] .= $s['s'] . "\n";
  25985. if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) { // only process position sensitive data
  25986. //Stop Transformation
  25987. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  25988. $trans_on = false;
  25989. }
  25990. }
  25991. if ($trans_on) {
  25992. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  25993. }
  25994. } else { // if NOT $this->colvAlign == 'J'
  25995. // Now output the adjusted values
  25996. foreach ($this->columnbuffer AS $s) {
  25997. $this->pages[$this->page] .= $s['s'] . "\n";
  25998. }
  25999. }
  26000. if ($lowest_bottom_y > 0) {
  26001. $this->y = $lowest_bottom_y;
  26002. }
  26003. }
  26004. // Columns not ended but new page -> align columns (can leave the columns alone - just tidy up the height)
  26005. elseif ($this->colvAlign == 'J' && $this->ColumnAdjust && !$this->keepColumns) {
  26006. // calculate the lowest bottom margin
  26007. $lowest_bottom_y = 0;
  26008. foreach ($this->columnbuffer AS $key => $s) {
  26009. // Only process output data
  26010. $t = $s['s'];
  26011. if ($t == 'ACROFORM' || (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) ||
  26012. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) ||
  26013. (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) ||
  26014. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) ||
  26015. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
  26016. $clb = $s['y'] + $s['h'];
  26017. if ((isset($this->ColDetails[$s['col']]['max_bottom']) && $clb > $this->ColDetails[$s['col']]['max_bottom']) || !isset($this->ColDetails[$s['col']]['max_bottom'])) {
  26018. $this->ColDetails[$s['col']]['max_bottom'] = $clb;
  26019. }
  26020. if ($clb > $lowest_bottom_y) {
  26021. $lowest_bottom_y = $clb;
  26022. }
  26023. $this->columnbuffer[$key]['rel_y'] = $s['y']; // Marks position sensitive data to process later
  26024. if ($t == 'ACROFORM') {
  26025. $this->columnbuffer[$key]['s'] = '';
  26026. }
  26027. }
  26028. }
  26029. // Adjust column length equal
  26030. foreach ($this->columnbuffer AS $key => $s) {
  26031. // Set ratio to expand y values or heights
  26032. if (isset($this->ColDetails[$s['col']]['max_bottom']) && $this->ColDetails[$s['col']]['max_bottom']) {
  26033. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
  26034. } else {
  26035. $ratio = 1;
  26036. }
  26037. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  26038. $yadj = ($s['y'] - $this->y0) * ($ratio - 1);
  26039. // Adjust LINKS
  26040. if (isset($s['rel_y'])) { // only process position sensitive data
  26041. // otherwise triggers for all entries in column buffer (.e.g. formatting) and makes below adjustments more than once
  26042. if (isset($this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  26043. $ref = $this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  26044. $this->PageLinks[$this->page][$ref][1] -= ($yadj * _MPDFK); // y value
  26045. $this->PageLinks[$this->page][$ref][3] *= $ratio; // height
  26046. unset($this->columnLinks[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  26047. }
  26048. // Adjust FORM FIELDS
  26049. if (isset($this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  26050. $ref = $this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  26051. $this->mpdfform->forms[$ref]['x'] += ($xadj);
  26052. $this->mpdfform->forms[$ref]['y'] += ($yadj);
  26053. unset($this->columnForms[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  26054. }
  26055. /* -- ANNOTATIONS -- */
  26056. if (isset($this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])])) {
  26057. $ref = $this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])];
  26058. $this->PageAnnots[$this->page][$ref]['y'] += ($yadj);
  26059. unset($this->columnAnnots[$s['col']][INTVAL($s['x'])][INTVAL($s['y'])]);
  26060. }
  26061. /* -- END ANNOTATIONS -- */
  26062. }
  26063. }
  26064. }
  26065. /* -- BOOKMARKS -- */
  26066. // Adjust Bookmarks
  26067. foreach ($this->col_BMoutlines AS $v) {
  26068. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $this->y0, 'p' => $v['p']);
  26069. }
  26070. /* -- END BOOKMARKS -- */
  26071. /* -- TOC -- */
  26072. // Adjust ToC
  26073. foreach ($this->col_toc AS $v) {
  26074. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  26075. $this->links[$v['link']][1] = $this->y0;
  26076. }
  26077. /* -- END TOC -- */
  26078. $trans_on = false;
  26079. foreach ($this->columnbuffer AS $key => $s) {
  26080. if (isset($s['rel_y'])) { // only process position sensitive data
  26081. // Set ratio to expand y values or heights
  26082. if ($this->ColDetails[$s['col']]['max_bottom']) {
  26083. $ratio = ($lowest_bottom_y - ($this->y0)) / ($this->ColDetails[$s['col']]['max_bottom'] - ($this->y0));
  26084. } else {
  26085. $ratio = 1;
  26086. }
  26087. if (($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  26088. //Start Transformation
  26089. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  26090. $this->pages[$this->page] .= $this->transformScale(100, $ratio * 100, $x = '', $this->y0, true) . "\n";
  26091. $trans_on = true;
  26092. }
  26093. }
  26094. // Now output the adjusted values
  26095. $this->pages[$this->page] .= $s['s'] . "\n";
  26096. if (isset($s['rel_y']) && ($ratio > 1) && ($ratio <= $this->max_colH_correction)) {
  26097. //Stop Transformation
  26098. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  26099. $trans_on = false;
  26100. }
  26101. }
  26102. if ($trans_on) {
  26103. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  26104. }
  26105. if ($lowest_bottom_y > 0) {
  26106. $this->y = $lowest_bottom_y;
  26107. }
  26108. }
  26109. // Just reproduce the page as it was
  26110. else {
  26111. // If page has not ended but height adjustment was disabled by custom column-break - adjust y
  26112. $lowest_bottom_y = 0;
  26113. if (!$this->ColActive && (!$this->ColumnAdjust || $this->keepColumns)) {
  26114. // calculate the lowest bottom margin
  26115. foreach ($this->columnbuffer AS $key => $s) {
  26116. // Only process output data
  26117. $t = $s['s'];
  26118. if ($t == 'ACROFORM' || (preg_match('/BT \d+\.\d\d+ (\d+\.\d\d+) Td/', $t)) || (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ [\-]{0,1}\d+\.\d\d+ re/', $t)) ||
  26119. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) l/', $t)) ||
  26120. (preg_match('/q \d+\.\d\d+ 0 0 \d+\.\d\d+ \d+\.\d\d+ (\d+\.\d\d+) cm \/(I|FO)\d+ Do Q/', $t)) ||
  26121. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) m/', $t)) ||
  26122. (preg_match('/\d+\.\d\d+ (\d+\.\d\d+) \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ \d+\.\d\d+ c/', $t))) {
  26123. $clb = $s['y'] + $s['h'];
  26124. if ($clb > $this->ColDetails[$s['col']]['max_bottom']) {
  26125. $this->ColDetails[$s['col']]['max_bottom'] = $clb;
  26126. }
  26127. if ($clb > $lowest_bottom_y) {
  26128. $lowest_bottom_y = $clb;
  26129. }
  26130. }
  26131. }
  26132. }
  26133. foreach ($this->columnbuffer AS $key => $s) {
  26134. if ($s['s'] != 'ACROFORM')
  26135. $this->pages[$this->page] .= $s['s'] . "\n";
  26136. }
  26137. if ($lowest_bottom_y > 0) {
  26138. $this->y = $lowest_bottom_y;
  26139. }
  26140. /* -- BOOKMARKS -- */
  26141. // Output Bookmarks
  26142. foreach ($this->col_BMoutlines AS $v) {
  26143. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']);
  26144. }
  26145. /* -- END BOOKMARKS -- */
  26146. /* -- TOC -- */
  26147. // Output ToC
  26148. foreach ($this->col_toc AS $v) {
  26149. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  26150. }
  26151. /* -- END TOC -- */
  26152. }
  26153. foreach ($this->internallink AS $key => $f) {
  26154. if (isset($this->internallink[$key]['col']))
  26155. unset($this->internallink[$key]['col']);
  26156. if (isset($this->internallink[$key]['rel_y']))
  26157. unset($this->internallink[$key]['rel_y']);
  26158. }
  26159. $this->columnbuffer = array();
  26160. $this->ColDetails = array();
  26161. $this->columnLinks = array();
  26162. $this->columnAnnots = array();
  26163. $this->columnForms = array();
  26164. $this->col_BMoutlines = array();
  26165. $this->col_toc = array();
  26166. $this->breakpoints = array();
  26167. }
  26168. // mPDF 5.7+
  26169. function columnAdjustPregReplace($type, $xadj, $yadj, $pattern, $subject)
  26170. {
  26171. preg_match($pattern, $subject, $matches);
  26172. if (!count($matches)) {
  26173. return $subject;
  26174. }
  26175. if (!isset($matches[3])) {
  26176. $matches[3] = 0;
  26177. }
  26178. if (!isset($matches[4])) {
  26179. $matches[4] = 0;
  26180. }
  26181. if (!isset($matches[5])) {
  26182. $matches[5] = 0;
  26183. }
  26184. if (!isset($matches[6])) {
  26185. $matches[6] = 0;
  26186. }
  26187. return str_replace($matches[0], $this->columnAdjustAdd($type, _MPDFK, $xadj, $yadj, $matches[1], $matches[2], $matches[3], $matches[4], $matches[5], $matches[6]), $subject);
  26188. }
  26189. /* -- END COLUMNS -- */
  26190. //==================================================================
  26191. /* -- TABLES -- */
  26192. function printcellbuffer()
  26193. {
  26194. if (count($this->cellBorderBuffer)) {
  26195. sort($this->cellBorderBuffer);
  26196. foreach ($this->cellBorderBuffer AS $cbb) {
  26197. $cba = unpack("A16dom/nbord/A1side/ns/dbw/a6ca/A10style/dx/dy/dw/dh/dmbl/dmbr/dmrt/dmrb/dmtl/dmtr/dmlt/dmlb/dcpd/dover/", $cbb);
  26198. $side = $cba['side'];
  26199. $color = str_pad($cba['ca'], 6, "\x00");
  26200. $details = array();
  26201. $details[$side]['dom'] = (float) $cba['dom'];
  26202. $details[$side]['s'] = $cba['s'];
  26203. $details[$side]['w'] = $cba['bw'];
  26204. $details[$side]['c'] = $color;
  26205. $details[$side]['style'] = trim($cba['style']);
  26206. $details['mbw']['BL'] = $cba['mbl'];
  26207. $details['mbw']['BR'] = $cba['mbr'];
  26208. $details['mbw']['RT'] = $cba['mrt'];
  26209. $details['mbw']['RB'] = $cba['mrb'];
  26210. $details['mbw']['TL'] = $cba['mtl'];
  26211. $details['mbw']['TR'] = $cba['mtr'];
  26212. $details['mbw']['LT'] = $cba['mlt'];
  26213. $details['mbw']['LB'] = $cba['mlb'];
  26214. $details['cellposdom'] = $cba['cpd'];
  26215. $details['p'] = $side;
  26216. if ($cba['over'] == 1) {
  26217. $details[$side]['overlay'] = true;
  26218. } else {
  26219. $details[$side]['overlay'] = false;
  26220. }
  26221. $this->_tableRect($cba['x'], $cba['y'], $cba['w'], $cba['h'], $cba['bord'], $details, false, false);
  26222. }
  26223. $this->cellBorderBuffer = array();
  26224. }
  26225. }
  26226. //==================================================================
  26227. function printtablebuffer()
  26228. {
  26229. if (!$this->table_rotate) {
  26230. $this->pages[$this->page] .= $this->tablebuffer;
  26231. foreach ($this->tbrot_Links AS $p => $l) {
  26232. foreach ($l AS $v) {
  26233. $this->PageLinks[$p][] = $v;
  26234. }
  26235. }
  26236. $this->tbrot_Links = array();
  26237. /* -- ANNOTATIONS -- */
  26238. foreach ($this->tbrot_Annots AS $p => $l) {
  26239. foreach ($l AS $v) {
  26240. $this->PageAnnots[$p][] = $v;
  26241. }
  26242. }
  26243. $this->tbrot_Annots = array();
  26244. /* -- END ANNOTATIONS -- */
  26245. /* -- BOOKMARKS -- */
  26246. // Output Bookmarks
  26247. foreach ($this->tbrot_BMoutlines AS $v) {
  26248. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']);
  26249. }
  26250. $this->tbrot_BMoutlines = array();
  26251. /* -- END BOOKMARKS -- */
  26252. /* -- TOC -- */
  26253. // Output ToC
  26254. foreach ($this->tbrot_toc AS $v) {
  26255. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  26256. }
  26257. $this->tbrot_toc = array();
  26258. /* -- END TOC -- */
  26259. return;
  26260. }
  26261. // elseif rotated
  26262. $lm = $this->lMargin + $this->blk[$this->blklvl]['outer_left_margin'] + $this->blk[$this->blklvl]['border_left']['w'] + $this->blk[$this->blklvl]['padding_left'];
  26263. $pw = $this->blk[$this->blklvl]['inner_width'];
  26264. //Start Transformation
  26265. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  26266. if ($this->table_rotate > 1) { // clockwise
  26267. if ($this->tbrot_align == 'L') {
  26268. $xadj = $this->tbrot_h; // align L (as is)
  26269. } elseif ($this->tbrot_align == 'R') {
  26270. $xadj = $lm - $this->tbrot_x0 + ($pw); // align R
  26271. } else {
  26272. $xadj = $lm - $this->tbrot_x0 + (($pw + $this->tbrot_h) / 2); // align C
  26273. }
  26274. $yadj = 0;
  26275. } else { // anti-clockwise
  26276. if ($this->tbrot_align == 'L') {
  26277. $xadj = 0; // align L (as is)
  26278. } elseif ($this->tbrot_align == 'R') {
  26279. $xadj = $lm - $this->tbrot_x0 + ($pw - $this->tbrot_h); // align R
  26280. } else {
  26281. $xadj = $lm - $this->tbrot_x0 + (($pw - $this->tbrot_h) / 2); // align C
  26282. }
  26283. $yadj = $this->tbrot_w;
  26284. }
  26285. $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
  26286. $this->pages[$this->page] .= $this->transformRotate($this->table_rotate, $this->tbrot_x0, $this->tbrot_y0, true) . "\n";
  26287. // Now output the adjusted values
  26288. $this->pages[$this->page] .= $this->tablebuffer;
  26289. foreach ($this->tbrot_Links AS $p => $l) {
  26290. foreach ($l AS $v) {
  26291. $w = $v[2] / _MPDFK;
  26292. $h = $v[3] / _MPDFK;
  26293. $ax = ($v[0] / _MPDFK) - $this->tbrot_x0;
  26294. $ay = (($this->hPt - $v[1]) / _MPDFK) - $this->tbrot_y0;
  26295. if ($this->table_rotate > 1) { // clockwise
  26296. $bx = $this->tbrot_x0 + $xadj - $ay - $h;
  26297. $by = $this->tbrot_y0 + $yadj + $ax;
  26298. } else {
  26299. $bx = $this->tbrot_x0 + $xadj + $ay;
  26300. $by = $this->tbrot_y0 + $yadj - $ax - $w;
  26301. }
  26302. $v[0] = $bx * _MPDFK;
  26303. $v[1] = ($this->h - $by) * _MPDFK;
  26304. $v[2] = $h * _MPDFK; // swap width and height
  26305. $v[3] = $w * _MPDFK;
  26306. $this->PageLinks[$p][] = $v;
  26307. }
  26308. }
  26309. $this->tbrot_Links = array();
  26310. foreach ($this->internallink AS $key => $f) {
  26311. if (is_array($f) && isset($f['tbrot'])) {
  26312. $f['Y'] = $this->tbrot_y0;
  26313. $f['PAGE'] = $this->page;
  26314. unset($f['tbrot']);
  26315. $this->internallink[$key] = $f;
  26316. }
  26317. }
  26318. /* -- ANNOTATIONS -- */
  26319. foreach ($this->tbrot_Annots AS $p => $l) {
  26320. foreach ($l AS $v) {
  26321. $ax = abs($v['x']) - $this->tbrot_x0; // abs because -ve values are internally set and held for reference if annotMargin set
  26322. $ay = $v['y'] - $this->tbrot_y0;
  26323. if ($this->table_rotate > 1) { // clockwise
  26324. $bx = $this->tbrot_x0 + $xadj - $ay;
  26325. $by = $this->tbrot_y0 + $yadj + $ax;
  26326. } else {
  26327. $bx = $this->tbrot_x0 + $xadj + $ay;
  26328. $by = $this->tbrot_y0 + $yadj - $ax;
  26329. }
  26330. if ($v['x'] < 0) {
  26331. $v['x'] = -$bx;
  26332. } else {
  26333. $v['x'] = $bx;
  26334. }
  26335. $v['y'] = ($by);
  26336. $this->PageAnnots[$p][] = $v;
  26337. }
  26338. }
  26339. $this->tbrot_Annots = array();
  26340. /* -- END ANNOTATIONS -- */
  26341. /* -- BOOKMARKS -- */
  26342. // Adjust Bookmarks
  26343. foreach ($this->tbrot_BMoutlines AS $v) {
  26344. $v['y'] = $this->tbrot_y0;
  26345. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page);
  26346. }
  26347. /* -- END BOOKMARKS -- */
  26348. /* -- TOC -- */
  26349. // Adjust ToC - uses document page number
  26350. foreach ($this->tbrot_toc AS $v) {
  26351. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  26352. $this->links[$v['link']][1] = $this->tbrot_y0;
  26353. }
  26354. /* -- END TOC -- */
  26355. $this->tbrot_BMoutlines = array();
  26356. $this->tbrot_toc = array();
  26357. //Stop Transformation
  26358. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  26359. $this->y = $this->tbrot_y0 + $this->tbrot_w;
  26360. $this->x = $this->lMargin;
  26361. $this->tablebuffer = '';
  26362. }
  26363. //==================================================================
  26364. // Keep-with-table This buffers contents of h1-6 to keep on page with table
  26365. function printkwtbuffer()
  26366. {
  26367. if (!$this->kwt_moved) {
  26368. foreach ($this->kwt_buffer AS $s) {
  26369. $this->pages[$this->page] .= $s['s'] . "\n";
  26370. }
  26371. foreach ($this->kwt_Links AS $p => $l) {
  26372. foreach ($l AS $v) {
  26373. $this->PageLinks[$p][] = $v;
  26374. }
  26375. }
  26376. $this->kwt_Links = array();
  26377. /* -- ANNOTATIONS -- */
  26378. foreach ($this->kwt_Annots AS $p => $l) {
  26379. foreach ($l AS $v) {
  26380. $this->PageAnnots[$p][] = $v;
  26381. }
  26382. }
  26383. $this->kwt_Annots = array();
  26384. /* -- END ANNOTATIONS -- */
  26385. /* -- INDEX -- */
  26386. // Output Reference (index)
  26387. foreach ($this->kwt_Reference AS $v) {
  26388. $Present = 0;
  26389. for ($i = 0; $i < count($this->Reference); $i++) {
  26390. if ($this->Reference[$i]['t'] == $v['t']) {
  26391. $Present = 1;
  26392. if (!in_array($v['op'], $this->Reference[$i]['p'])) {
  26393. $this->Reference[$i]['p'][] = $v['op'];
  26394. }
  26395. }
  26396. }
  26397. if ($Present == 0) {
  26398. $this->Reference[] = array('t' => $v['t'], 'p' => array($v['op']));
  26399. }
  26400. }
  26401. $this->kwt_Reference = array();
  26402. /* -- END INDEX -- */
  26403. /* -- BOOKMARKS -- */
  26404. // Output Bookmarks
  26405. foreach ($this->kwt_BMoutlines AS $v) {
  26406. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $v['p']);
  26407. }
  26408. $this->kwt_BMoutlines = array();
  26409. /* -- END BOOKMARKS -- */
  26410. /* -- TOC -- */
  26411. // Output ToC
  26412. foreach ($this->kwt_toc AS $v) {
  26413. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $v['p'], 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  26414. }
  26415. $this->kwt_toc = array();
  26416. /* -- END TOC -- */
  26417. $this->pageoutput[$this->page] = array(); // mPDF 6
  26418. return;
  26419. }
  26420. //Start Transformation
  26421. $this->pages[$this->page] .= $this->StartTransform(true) . "\n";
  26422. $xadj = $this->lMargin - $this->kwt_x0;
  26423. //$yadj = $this->y - $this->kwt_y0 ;
  26424. $yadj = $this->tMargin - $this->kwt_y0;
  26425. $this->pages[$this->page] .= $this->transformTranslate($xadj, $yadj, true) . "\n";
  26426. // Now output the adjusted values
  26427. foreach ($this->kwt_buffer AS $s) {
  26428. $this->pages[$this->page] .= $s['s'] . "\n";
  26429. }
  26430. // Adjust hyperLinks
  26431. foreach ($this->kwt_Links AS $p => $l) {
  26432. foreach ($l AS $v) {
  26433. $bx = $this->kwt_x0 + $xadj;
  26434. $by = $this->kwt_y0 + $yadj;
  26435. $v[0] = $bx * _MPDFK;
  26436. $v[1] = ($this->h - $by) * _MPDFK;
  26437. $this->PageLinks[$p][] = $v;
  26438. }
  26439. }
  26440. foreach ($this->internallink AS $key => $f) {
  26441. if (is_array($f) && isset($f['kwt'])) {
  26442. $f['Y'] += $yadj;
  26443. $f['PAGE'] = $this->page;
  26444. unset($f['kwt']);
  26445. $this->internallink[$key] = $f;
  26446. }
  26447. }
  26448. /* -- ANNOTATIONS -- */
  26449. foreach ($this->kwt_Annots AS $p => $l) {
  26450. foreach ($l AS $v) {
  26451. $bx = $this->kwt_x0 + $xadj;
  26452. $by = $this->kwt_y0 + $yadj;
  26453. if ($v['x'] < 0) {
  26454. $v['x'] = -$bx;
  26455. } else {
  26456. $v['x'] = $bx;
  26457. }
  26458. $v['y'] = $by;
  26459. $this->PageAnnots[$p][] = $v;
  26460. }
  26461. }
  26462. /* -- END ANNOTATIONS -- */
  26463. /* -- BOOKMARKS -- */
  26464. // Adjust Bookmarks
  26465. foreach ($this->kwt_BMoutlines AS $v) {
  26466. if ($v['y'] != 0) {
  26467. $v['y'] += $yadj;
  26468. }
  26469. $this->BMoutlines[] = array('t' => $v['t'], 'l' => $v['l'], 'y' => $v['y'], 'p' => $this->page);
  26470. }
  26471. /* -- END BOOKMARKS -- */
  26472. /* -- INDEX -- */
  26473. // Adjust Reference (index)
  26474. foreach ($this->kwt_Reference AS $v) {
  26475. $Present = 0;
  26476. //Search the reference (AND Ref/PageNo) in the array
  26477. for ($i = 0; $i < count($this->Reference); $i++) {
  26478. if ($this->Reference[$i]['t'] == $v['t']) {
  26479. $Present = 1;
  26480. if (!in_array($this->page, $this->Reference[$i]['p'])) {
  26481. $this->Reference[$i]['p'][] = $this->page;
  26482. }
  26483. }
  26484. }
  26485. if ($Present == 0) {
  26486. $this->Reference[] = array('t' => $v['t'], 'p' => array($this->page));
  26487. }
  26488. }
  26489. /* -- END INDEX -- */
  26490. /* -- TOC -- */
  26491. // Adjust ToC
  26492. foreach ($this->kwt_toc AS $v) {
  26493. $this->tocontents->_toc[] = array('t' => $v['t'], 'l' => $v['l'], 'p' => $this->page, 'link' => $v['link'], 'toc_id' => $v['toc_id']);
  26494. $this->links[$v['link']][0] = $this->page;
  26495. $this->links[$v['link']][1] += $yadj;
  26496. }
  26497. /* -- END TOC -- */
  26498. $this->kwt_Links = array();
  26499. $this->kwt_Annots = array();
  26500. $this->kwt_Reference = array();
  26501. $this->kwt_BMoutlines = array();
  26502. $this->kwt_toc = array();
  26503. //Stop Transformation
  26504. $this->pages[$this->page] .= $this->StopTransform(true) . "\n";
  26505. $this->kwt_buffer = array();
  26506. $this->y += $this->kwt_height;
  26507. $this->pageoutput[$this->page] = array(); // mPDF 6
  26508. }
  26509. /* -- END TABLES -- */
  26510. //==================================================================
  26511. function printfloatbuffer()
  26512. {
  26513. if (count($this->floatbuffer)) {
  26514. $this->objectbuffer = $this->floatbuffer;
  26515. $this->printobjectbuffer(false);
  26516. $this->objectbuffer = array();
  26517. $this->floatbuffer = array();
  26518. $this->floatmargins = array();
  26519. }
  26520. }
  26521. //==================================================================
  26522. //==================================================================
  26523. // Added ELLIPSES and CIRCLES
  26524. function Circle($x, $y, $r, $style = 'S')
  26525. {
  26526. $this->Ellipse($x, $y, $r, $r, $style);
  26527. }
  26528. function Ellipse($x, $y, $rx, $ry, $style = 'S')
  26529. {
  26530. if ($style == 'F') {
  26531. $op = 'f';
  26532. } elseif ($style == 'FD' or $style == 'DF') {
  26533. $op = 'B';
  26534. } else {
  26535. $op = 'S';
  26536. }
  26537. $lx = 4 / 3 * (M_SQRT2 - 1) * $rx;
  26538. $ly = 4 / 3 * (M_SQRT2 - 1) * $ry;
  26539. $h = $this->h;
  26540. $this->_out(sprintf('%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c', ($x + $rx) * _MPDFK, ($h - $y) * _MPDFK, ($x + $rx) * _MPDFK, ($h - ($y - $ly)) * _MPDFK, ($x + $lx) * _MPDFK, ($h - ($y - $ry)) * _MPDFK, $x * _MPDFK, ($h - ($y - $ry)) * _MPDFK));
  26541. $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $lx) * _MPDFK, ($h - ($y - $ry)) * _MPDFK, ($x - $rx) * _MPDFK, ($h - ($y - $ly)) * _MPDFK, ($x - $rx) * _MPDFK, ($h - $y) * _MPDFK));
  26542. $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c', ($x - $rx) * _MPDFK, ($h - ($y + $ly)) * _MPDFK, ($x - $lx) * _MPDFK, ($h - ($y + $ry)) * _MPDFK, $x * _MPDFK, ($h - ($y + $ry)) * _MPDFK));
  26543. $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F c %s', ($x + $lx) * _MPDFK, ($h - ($y + $ry)) * _MPDFK, ($x + $rx) * _MPDFK, ($h - ($y + $ly)) * _MPDFK, ($x + $rx) * _MPDFK, ($h - $y) * _MPDFK, $op));
  26544. }
  26545. /* -- DIRECTW -- */
  26546. // Added adaptation of shaded_box = AUTOSIZE-TEXT
  26547. function AutosizeText($text, $w, $font, $style, $szfont = 72)
  26548. {
  26549. $text = ' ' . $text . ' ';
  26550. $this->SetFont($font, $style, $szfont, false);
  26551. $text = $this->purify_utf8_text($text);
  26552. if ($this->text_input_as_HTML) {
  26553. $text = $this->all_entities_to_utf8($text);
  26554. }
  26555. if ($this->usingCoreFont) {
  26556. $text = mb_convert_encoding($text, $this->mb_enc, 'UTF-8');
  26557. }
  26558. // DIRECTIONALITY
  26559. if (preg_match("/([" . $this->pregRTLchars . "])/u", $text)) {
  26560. $this->biDirectional = true;
  26561. } // *OTL*
  26562. $textvar = 0;
  26563. $save_OTLtags = $this->OTLtags;
  26564. $this->OTLtags = array();
  26565. if ($this->useKerning) {
  26566. if ($this->CurrentFont['haskernGPOS']) {
  26567. $this->OTLtags['Plus'] .= ' kern';
  26568. } else {
  26569. $textvar = ($textvar | FC_KERNING);
  26570. }
  26571. }
  26572. /* -- OTL -- */
  26573. // Use OTL OpenType Table Layout - GSUB & GPOS
  26574. if (isset($this->CurrentFont['useOTL']) && $this->CurrentFont['useOTL']) {
  26575. $text = $this->otl->applyOTL($text, $this->CurrentFont['useOTL']);
  26576. $OTLdata = $this->otl->OTLdata;
  26577. }
  26578. /* -- END OTL -- */
  26579. $this->OTLtags = $save_OTLtags;
  26580. $this->magic_reverse_dir($text, $this->directionality, $OTLdata);
  26581. $width = $this->ConvertSize($w);
  26582. $loop = 0;
  26583. while ($loop == 0) {
  26584. $this->SetFont($font, $style, $szfont, false);
  26585. $sz = $this->GetStringWidth($text, true, $OTLdata, $textvar);
  26586. if ($sz > $w) {
  26587. $szfont --;
  26588. } else {
  26589. $loop ++;
  26590. }
  26591. }
  26592. $this->SetFont($font, $style, $szfont, true, true);
  26593. $this->Cell($w, 0, $text, 0, 0, "C", 0, '', 0, 0, 0, 'M', 0, false, $OTLdata, $textvar);
  26594. }
  26595. /* -- END DIRECTW -- */
  26596. // ====================================================
  26597. // ====================================================
  26598. function magic_reverse_dir(&$chunk, $dir, &$chunkOTLdata)
  26599. {
  26600. /* -- OTL -- */
  26601. if ($this->usingCoreFont) {
  26602. return 0;
  26603. }
  26604. if ($chunk == '') {
  26605. return 0;
  26606. }
  26607. if ($this->biDirectional || $dir == 'rtl') {
  26608. // check if string contains RTL text
  26609. // including any added from OTL tables (in PUA)
  26610. $pregRTLchars = $this->pregRTLchars;
  26611. if (isset($this->CurrentFont['rtlPUAstr']) && $this->CurrentFont['rtlPUAstr']) {
  26612. $pregRTLchars .= $this->CurrentFont['rtlPUAstr'];
  26613. }
  26614. if (!preg_match("/[" . $pregRTLchars . "]/u", $chunk) && $dir != 'rtl') {
  26615. return 0;
  26616. } // Chunk doesn't contain RTL characters
  26617. $unicode = $this->UTF8StringToArray($chunk, false);
  26618. $is_strong = false;
  26619. if (empty($chunkOTLdata)) {
  26620. $this->getBasicOTLdata($chunkOTLdata, $unicode, $is_strong);
  26621. }
  26622. if (isset($this->CurrentFont['useOTL']) && ($this->CurrentFont['useOTL'] & 0x80)) {
  26623. $useGPOS = true;
  26624. } else {
  26625. $useGPOS = false;
  26626. }
  26627. // NB Returned $chunk may be a shorter string (with adjusted $cOTLdata) by removal of LRE, RLE etc embedding codes.
  26628. list($chunk, $rtl_content) = $this->otl->_bidiSort($unicode, $chunk, $dir, $chunkOTLdata, $useGPOS);
  26629. return $rtl_content;
  26630. }
  26631. /* -- END OTL -- */
  26632. return 0;
  26633. }
  26634. /* -- OTL -- */
  26635. function getBasicOTLdata(&$chunkOTLdata, $unicode, &$is_strong)
  26636. {
  26637. if (!class_exists('otl', false)) {
  26638. include(_MPDF_PATH . 'classes/otl.php');
  26639. }
  26640. if (empty($this->otl)) {
  26641. $this->otl = new otl($this);
  26642. }
  26643. $chunkOTLdata['group'] = '';
  26644. $chunkOTLdata['GPOSinfo'] = array();
  26645. $chunkOTLdata['char_data'] = array();
  26646. foreach ($unicode as $char) {
  26647. $ucd_record = UCDN::get_ucd_record($char);
  26648. $chunkOTLdata['char_data'][] = array('bidi_class' => $ucd_record[2], 'uni' => $char);
  26649. if ($ucd_record[2] == 0 || $ucd_record[2] == 3 || $ucd_record[2] == 4) {
  26650. $is_strong = true;
  26651. } // contains strong character
  26652. if ($ucd_record[0] == UCDN::UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
  26653. $chunkOTLdata['group'] .= 'M';
  26654. } elseif ($char == 32 || $char == 12288) {
  26655. $chunkOTLdata['group'] .= 'S';
  26656. } else {
  26657. $chunkOTLdata['group'] .= 'C';
  26658. }
  26659. }
  26660. }
  26661. function _setBidiCodes($mode = 'start', $bdf)
  26662. {
  26663. $s = '';
  26664. if ($mode == 'end') {
  26665. // PDF comes before PDI to close isolate-override (e.g. "LRILROPDFPDI")
  26666. if (strpos($bdf, 'PDF') !== false) {
  26667. $s .= code2utf(0x202C);
  26668. } // POP DIRECTIONAL FORMATTING
  26669. if (strpos($bdf, 'PDI') !== false) {
  26670. $s .= code2utf(0x2069);
  26671. } // POP DIRECTIONAL ISOLATE
  26672. } elseif ($mode == 'start') {
  26673. // LRI comes before LRO to open isolate-override (e.g. "LRILROPDFPDI")
  26674. if (strpos($bdf, 'LRI') !== false) {
  26675. $s .= code2utf(0x2066);
  26676. } // U+2066 LRI
  26677. elseif (strpos($bdf, 'RLI') !== false) {
  26678. $s .= code2utf(0x2067);
  26679. } // U+2067 RLI
  26680. elseif (strpos($bdf, 'FSI') !== false) {
  26681. $s .= code2utf(0x2068);
  26682. } // U+2068 FSI
  26683. if (strpos($bdf, 'LRO') !== false) {
  26684. $s .= code2utf(0x202D);
  26685. } // U+202D LRO
  26686. elseif (strpos($bdf, 'RLO') !== false) {
  26687. $s .= code2utf(0x202E);
  26688. } // U+202E RLO
  26689. elseif (strpos($bdf, 'LRE') !== false) {
  26690. $s .= code2utf(0x202A);
  26691. } // U+202A LRE
  26692. elseif (strpos($bdf, 'RLE') !== false) {
  26693. $s .= code2utf(0x202B);
  26694. } // U+202B RLE
  26695. }
  26696. return $s;
  26697. }
  26698. /* -- END OTL -- */
  26699. //
  26700. // ****************************
  26701. // ****************************
  26702. function SetSubstitutions()
  26703. {
  26704. $subsarray = array();
  26705. @include(_MPDF_PATH . 'includes/subs_win-1252.php');
  26706. $this->substitute = array();
  26707. foreach ($subsarray AS $key => $val) {
  26708. $this->substitute[code2utf($key)] = $val;
  26709. }
  26710. }
  26711. function SubstituteChars($html)
  26712. {
  26713. // only substitute characters between tags
  26714. if (count($this->substitute)) {
  26715. $a = preg_split('/(<.*?>)/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  26716. $html = '';
  26717. foreach ($a as $i => $e) {
  26718. if ($i % 2 == 0) {
  26719. $e = strtr($e, $this->substitute);
  26720. }
  26721. $html .= $e;
  26722. }
  26723. }
  26724. return $html;
  26725. }
  26726. function SubstituteCharsSIP(&$writehtml_a, &$writehtml_i, &$writehtml_e)
  26727. {
  26728. if (preg_match("/^(.*?)([\x{20000}-\x{2FFFF}]+)(.*)/u", $writehtml_e, $m)) {
  26729. if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
  26730. $font = $this->CurrentFont['sipext'];
  26731. if (!in_array($font, $this->available_unifonts)) {
  26732. return 0;
  26733. }
  26734. $writehtml_a[$writehtml_i] = $writehtml_e = $m[1];
  26735. array_splice($writehtml_a, $writehtml_i + 1, 0, array('span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]));
  26736. $this->subPos = $writehtml_i;
  26737. return 4;
  26738. }
  26739. }
  26740. return 0;
  26741. }
  26742. // If core font is selected in document which is not onlyCoreFonts - substitute with non-core font
  26743. function SubstituteCharsNonCore(&$writehtml_a, &$writehtml_i, &$writehtml_e)
  26744. {
  26745. // Ignore if in Textarea
  26746. if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
  26747. return 0;
  26748. }
  26749. if (mb_convert_encoding(mb_convert_encoding($writehtml_e, $this->mb_enc, "UTF-8"), "UTF-8", $this->mb_enc) == $writehtml_e) {
  26750. return 0;
  26751. }
  26752. $cw = &$this->CurrentFont['cw'];
  26753. $unicode = $this->UTF8StringToArray($writehtml_e, false);
  26754. $start = -1;
  26755. $end = 0;
  26756. $flag = 0;
  26757. $ftype = '';
  26758. $u = array();
  26759. if (!$this->subArrMB) {
  26760. include(_MPDF_PATH . 'includes/subs_core.php');
  26761. $this->subArrMB['a'] = $aarr;
  26762. $this->subArrMB['s'] = $sarr;
  26763. $this->subArrMB['z'] = $zarr;
  26764. }
  26765. foreach ($unicode AS $c => $char) {
  26766. if (($char > 127 || ($flag == 1 && $char == 32)) && $char != 173 && (!isset($this->subArrMB['a'][$char]) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {
  26767. if ($flag == 0) {
  26768. $start = $c;
  26769. }
  26770. $flag = 1;
  26771. $u[] = $char;
  26772. } elseif ($flag > 0) {
  26773. $end = $c - 1;
  26774. break;
  26775. }
  26776. }
  26777. if ($flag > 0 && !$end) {
  26778. $end = count($unicode) - 1;
  26779. }
  26780. if ($start == -1) {
  26781. return 0;
  26782. }
  26783. // TRY IN BACKUP SUBS FONT
  26784. if (!is_array($this->backupSubsFont)) {
  26785. $this->backupSubsFont = array("$this->backupSubsFont");
  26786. }
  26787. foreach ($this->backupSubsFont AS $bsfctr => $bsf) {
  26788. if ($this->fonttrans[$bsf] == 'chelvetica' || $this->fonttrans[$bsf] == 'ctimes' || $this->fonttrans[$bsf] == 'ccourier') {
  26789. continue;
  26790. }
  26791. $font = $bsf;
  26792. unset($cw);
  26793. $cw = '';
  26794. if (isset($this->fonts[$font])) {
  26795. $cw = &$this->fonts[$font]['cw'];
  26796. } elseif (file_exists(_MPDF_TTFONTDATAPATH . $font . '.cw.dat')) {
  26797. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  26798. } else {
  26799. $prevFontFamily = $this->FontFamily;
  26800. $prevFontStyle = $this->currentfontstyle;
  26801. $prevFontSizePt = $this->FontSizePt;
  26802. $this->SetFont($bsf, '', '', false);
  26803. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  26804. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  26805. }
  26806. if (!$cw) {
  26807. continue;
  26808. }
  26809. $l = 0;
  26810. foreach ($u AS $char) {
  26811. if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) {
  26812. $l++;
  26813. } else {
  26814. if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
  26815. $cont = mb_substr($writehtml_e, $start + 1);
  26816. $writehtml_e = mb_substr($writehtml_e, 0, $start + 1, 'UTF-8');
  26817. array_splice($writehtml_a, $writehtml_i + 1, 0, array('', $cont));
  26818. $this->subPos = $writehtml_i + 1;
  26819. return 2;
  26820. } else {
  26821. break;
  26822. }
  26823. }
  26824. }
  26825. if ($l > 0) {
  26826. $patt = mb_substr($writehtml_e, $start, $l, 'UTF-8');
  26827. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  26828. $writehtml_e = $m[1];
  26829. array_splice($writehtml_a, $writehtml_i + 1, 0, array('span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]));
  26830. $this->subPos = $writehtml_i + 3;
  26831. return 4;
  26832. }
  26833. }
  26834. }
  26835. unset($cw);
  26836. return 0;
  26837. }
  26838. function SubstituteCharsMB(&$writehtml_a, &$writehtml_i, &$writehtml_e)
  26839. {
  26840. // Ignore if in Textarea
  26841. if ($writehtml_i > 0 && strtolower(substr($writehtml_a[$writehtml_i - 1], 0, 8)) == 'textarea') {
  26842. return 0;
  26843. }
  26844. $cw = &$this->CurrentFont['cw'];
  26845. $unicode = $this->UTF8StringToArray($writehtml_e, false);
  26846. $start = -1;
  26847. $end = 0;
  26848. $flag = 0;
  26849. $ftype = '';
  26850. $u = array();
  26851. foreach ($unicode AS $c => $char) {
  26852. if (($flag == 0 || $flag == 2) && (!$this->_charDefined($cw, $char) || ($flag == 2 && $char == 32)) && $this->checkSIP && $char > 131071) { // Unicode Plane 2 (SIP)
  26853. if (in_array($this->FontFamily, $this->available_CJK_fonts)) {
  26854. return 0;
  26855. }
  26856. if ($flag == 0) {
  26857. $start = $c;
  26858. }
  26859. $flag = 2;
  26860. $u[] = $char;
  26861. }
  26862. //elseif (($flag == 0 || $flag==1) && $char != 173 && !$this->_charDefined($cw,$char) && ($char<1423 || ($char>3583 && $char < 11263))) {
  26863. elseif (($flag == 0 || $flag == 1) && $char != 173 && (!$this->_charDefined($cw, $char) || ($flag == 1 && $char == 32)) && ($char < 1536 || ($char > 1791 && $char < 2304) || $char > 3455)) {
  26864. if ($flag == 0) {
  26865. $start = $c;
  26866. }
  26867. $flag = 1;
  26868. $u[] = $char;
  26869. } elseif ($flag > 0) {
  26870. $end = $c - 1;
  26871. break;
  26872. }
  26873. }
  26874. if ($flag > 0 && !$end) {
  26875. $end = count($unicode) - 1;
  26876. }
  26877. if ($start == -1) {
  26878. return 0;
  26879. }
  26880. if ($flag == 2) { // SIP
  26881. // Check if current CJK font has a ext-B related font
  26882. if (isset($this->CurrentFont['sipext']) && $this->CurrentFont['sipext']) {
  26883. $font = $this->CurrentFont['sipext'];
  26884. unset($cw);
  26885. $cw = '';
  26886. if (isset($this->fonts[$font])) {
  26887. $cw = &$this->fonts[$font]['cw'];
  26888. } elseif (file_exists(_MPDF_TTFONTDATAPATH . $font . '.cw.dat')) {
  26889. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  26890. } else {
  26891. $prevFontFamily = $this->FontFamily;
  26892. $prevFontStyle = $this->currentfontstyle;
  26893. $prevFontSizePt = $this->FontSizePt;
  26894. $this->SetFont($font, '', '', false);
  26895. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  26896. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  26897. }
  26898. if (!$cw) {
  26899. return 0;
  26900. }
  26901. $l = 0;
  26902. foreach ($u AS $char) {
  26903. if ($this->_charDefined($cw, $char) || $char > 131071) {
  26904. $l++;
  26905. } else {
  26906. break;
  26907. }
  26908. }
  26909. if ($l > 0) {
  26910. $patt = mb_substr($writehtml_e, $start, $l);
  26911. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  26912. $writehtml_e = $m[1];
  26913. array_splice($writehtml_a, $writehtml_i + 1, 0, array('span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]));
  26914. $this->subPos = $writehtml_i + 3;
  26915. return 4;
  26916. }
  26917. }
  26918. }
  26919. // Check Backup SIP font (defined in config_fonts.php)
  26920. if (isset($this->backupSIPFont) && $this->backupSIPFont) {
  26921. if ($this->currentfontfamily != $this->backupSIPFont) {
  26922. $font = $this->backupSIPFont;
  26923. } else {
  26924. unset($cw);
  26925. return 0;
  26926. }
  26927. unset($cw);
  26928. $cw = '';
  26929. if (isset($this->fonts[$font])) {
  26930. $cw = &$this->fonts[$font]['cw'];
  26931. } elseif (file_exists(_MPDF_TTFONTDATAPATH . $font . '.cw.dat')) {
  26932. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  26933. } else {
  26934. $prevFontFamily = $this->FontFamily;
  26935. $prevFontStyle = $this->currentfontstyle;
  26936. $prevFontSizePt = $this->FontSizePt;
  26937. $this->SetFont($this->backupSIPFont, '', '', false);
  26938. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  26939. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  26940. }
  26941. if (!$cw) {
  26942. return 0;
  26943. }
  26944. $l = 0;
  26945. foreach ($u AS $char) {
  26946. if ($this->_charDefined($cw, $char) || $char > 131071) {
  26947. $l++;
  26948. } else {
  26949. break;
  26950. }
  26951. }
  26952. if ($l > 0) {
  26953. $patt = mb_substr($writehtml_e, $start, $l);
  26954. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  26955. $writehtml_e = $m[1];
  26956. array_splice($writehtml_a, $writehtml_i + 1, 0, array('span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]));
  26957. $this->subPos = $writehtml_i + 3;
  26958. return 4;
  26959. }
  26960. }
  26961. }
  26962. return 0;
  26963. }
  26964. // FIRST TRY CORE FONTS (when appropriate)
  26965. if (!$this->PDFA && !$this->PDFX && !$this->biDirectional) { // mPDF 6
  26966. $repl = array();
  26967. if (!$this->subArrMB) {
  26968. include(_MPDF_PATH . 'includes/subs_core.php');
  26969. $this->subArrMB['a'] = $aarr;
  26970. $this->subArrMB['s'] = $sarr;
  26971. $this->subArrMB['z'] = $zarr;
  26972. }
  26973. if (isset($this->subArrMB['a'][$u[0]])) {
  26974. $font = 'tta';
  26975. $ftype = 'C';
  26976. foreach ($u AS $char) {
  26977. if (isset($this->subArrMB['a'][$char])) {
  26978. $repl[] = $this->subArrMB['a'][$char];
  26979. } else {
  26980. break;
  26981. }
  26982. }
  26983. } elseif (isset($this->subArrMB['z'][$u[0]])) {
  26984. $font = 'ttz';
  26985. $ftype = 'C';
  26986. foreach ($u AS $char) {
  26987. if (isset($this->subArrMB['z'][$char])) {
  26988. $repl[] = $this->subArrMB['z'][$char];
  26989. } else {
  26990. break;
  26991. }
  26992. }
  26993. } elseif (isset($this->subArrMB['s'][$u[0]])) {
  26994. $font = 'tts';
  26995. $ftype = 'C';
  26996. foreach ($u AS $char) {
  26997. if (isset($this->subArrMB['s'][$char])) {
  26998. $repl[] = $this->subArrMB['s'][$char];
  26999. } else {
  27000. break;
  27001. }
  27002. }
  27003. }
  27004. if ($ftype == 'C') {
  27005. $patt = mb_substr($writehtml_e, $start, count($repl));
  27006. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  27007. $writehtml_e = $m[1];
  27008. array_splice($writehtml_a, $writehtml_i + 1, 0, array($font, implode('|', $repl), '/' . $font, $m[3])); // e.g. <tts>
  27009. $this->subPos = $writehtml_i + 3;
  27010. return 4;
  27011. }
  27012. return 0;
  27013. }
  27014. }
  27015. // LASTLY TRY IN BACKUP SUBS FONT
  27016. if (!is_array($this->backupSubsFont)) {
  27017. $this->backupSubsFont = array("$this->backupSubsFont");
  27018. }
  27019. foreach ($this->backupSubsFont AS $bsfctr => $bsf) {
  27020. if ($this->currentfontfamily != $bsf) {
  27021. $font = $bsf;
  27022. } else {
  27023. continue;
  27024. }
  27025. unset($cw);
  27026. $cw = '';
  27027. if (isset($this->fonts[$font])) {
  27028. $cw = &$this->fonts[$font]['cw'];
  27029. } elseif (file_exists(_MPDF_TTFONTDATAPATH . $font . '.cw.dat')) {
  27030. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  27031. } else {
  27032. $prevFontFamily = $this->FontFamily;
  27033. $prevFontStyle = $this->currentfontstyle;
  27034. $prevFontSizePt = $this->FontSizePt;
  27035. $this->SetFont($bsf, '', '', false);
  27036. $cw = @file_get_contents(_MPDF_TTFONTDATAPATH . $font . '.cw.dat');
  27037. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt, false);
  27038. }
  27039. if (!$cw) {
  27040. continue;
  27041. }
  27042. $l = 0;
  27043. foreach ($u AS $char) {
  27044. if ($char == 173 || $this->_charDefined($cw, $char) || ($char > 1536 && $char < 1791) || ($char > 2304 && $char < 3455 )) { // Arabic and Indic
  27045. $l++;
  27046. } else {
  27047. if ($l == 0 && $bsfctr == (count($this->backupSubsFont) - 1)) { // Not found even in last backup font
  27048. $cont = mb_substr($writehtml_e, $start + 1);
  27049. $writehtml_e = mb_substr($writehtml_e, 0, $start + 1);
  27050. array_splice($writehtml_a, $writehtml_i + 1, 0, array('', $cont));
  27051. $this->subPos = $writehtml_i + 1;
  27052. return 2;
  27053. } else {
  27054. break;
  27055. }
  27056. }
  27057. }
  27058. if ($l > 0) {
  27059. $patt = mb_substr($writehtml_e, $start, $l);
  27060. if (preg_match("/(.*?)(" . preg_quote($patt, '/') . ")(.*)/u", $writehtml_e, $m)) {
  27061. $writehtml_e = $m[1];
  27062. array_splice($writehtml_a, $writehtml_i + 1, 0, array('span style="font-family: ' . $font . '"', $m[2], '/span', $m[3]));
  27063. $this->subPos = $writehtml_i + 3;
  27064. return 4;
  27065. }
  27066. }
  27067. }
  27068. unset($cw);
  27069. return 0;
  27070. }
  27071. function setHiEntitySubstitutions()
  27072. {
  27073. $entarr = array(
  27074. 'nbsp' => '160', 'iexcl' => '161', 'cent' => '162', 'pound' => '163', 'curren' => '164', 'yen' => '165', 'brvbar' => '166', 'sect' => '167',
  27075. 'uml' => '168', 'copy' => '169', 'ordf' => '170', 'laquo' => '171', 'not' => '172', 'shy' => '173', 'reg' => '174', 'macr' => '175',
  27076. 'deg' => '176', 'plusmn' => '177', 'sup2' => '178', 'sup3' => '179', 'acute' => '180', 'micro' => '181', 'para' => '182', 'middot' => '183',
  27077. 'cedil' => '184', 'sup1' => '185', 'ordm' => '186', 'raquo' => '187', 'frac14' => '188', 'frac12' => '189', 'frac34' => '190',
  27078. 'iquest' => '191', 'Agrave' => '192', 'Aacute' => '193', 'Acirc' => '194', 'Atilde' => '195', 'Auml' => '196', 'Aring' => '197',
  27079. 'AElig' => '198', 'Ccedil' => '199', 'Egrave' => '200', 'Eacute' => '201', 'Ecirc' => '202', 'Euml' => '203', 'Igrave' => '204',
  27080. 'Iacute' => '205', 'Icirc' => '206', 'Iuml' => '207', 'ETH' => '208', 'Ntilde' => '209', 'Ograve' => '210', 'Oacute' => '211',
  27081. 'Ocirc' => '212', 'Otilde' => '213', 'Ouml' => '214', 'times' => '215', 'Oslash' => '216', 'Ugrave' => '217', 'Uacute' => '218',
  27082. 'Ucirc' => '219', 'Uuml' => '220', 'Yacute' => '221', 'THORN' => '222', 'szlig' => '223', 'agrave' => '224', 'aacute' => '225',
  27083. 'acirc' => '226', 'atilde' => '227', 'auml' => '228', 'aring' => '229', 'aelig' => '230', 'ccedil' => '231', 'egrave' => '232',
  27084. 'eacute' => '233', 'ecirc' => '234', 'euml' => '235', 'igrave' => '236', 'iacute' => '237', 'icirc' => '238', 'iuml' => '239',
  27085. 'eth' => '240', 'ntilde' => '241', 'ograve' => '242', 'oacute' => '243', 'ocirc' => '244', 'otilde' => '245', 'ouml' => '246',
  27086. 'divide' => '247', 'oslash' => '248', 'ugrave' => '249', 'uacute' => '250', 'ucirc' => '251', 'uuml' => '252', 'yacute' => '253',
  27087. 'thorn' => '254', 'yuml' => '255', 'OElig' => '338', 'oelig' => '339', 'Scaron' => '352', 'scaron' => '353', 'Yuml' => '376',
  27088. 'fnof' => '402', 'circ' => '710', 'tilde' => '732', 'Alpha' => '913', 'Beta' => '914', 'Gamma' => '915', 'Delta' => '916',
  27089. 'Epsilon' => '917', 'Zeta' => '918', 'Eta' => '919', 'Theta' => '920', 'Iota' => '921', 'Kappa' => '922', 'Lambda' => '923',
  27090. 'Mu' => '924', 'Nu' => '925', 'Xi' => '926', 'Omicron' => '927', 'Pi' => '928', 'Rho' => '929', 'Sigma' => '931', 'Tau' => '932',
  27091. 'Upsilon' => '933', 'Phi' => '934', 'Chi' => '935', 'Psi' => '936', 'Omega' => '937', 'alpha' => '945', 'beta' => '946', 'gamma' => '947',
  27092. 'delta' => '948', 'epsilon' => '949', 'zeta' => '950', 'eta' => '951', 'theta' => '952', 'iota' => '953', 'kappa' => '954',
  27093. 'lambda' => '955', 'mu' => '956', 'nu' => '957', 'xi' => '958', 'omicron' => '959', 'pi' => '960', 'rho' => '961', 'sigmaf' => '962',
  27094. 'sigma' => '963', 'tau' => '964', 'upsilon' => '965', 'phi' => '966', 'chi' => '967', 'psi' => '968', 'omega' => '969',
  27095. 'thetasym' => '977', 'upsih' => '978', 'piv' => '982', 'ensp' => '8194', 'emsp' => '8195', 'thinsp' => '8201', 'zwnj' => '8204',
  27096. 'zwj' => '8205', 'lrm' => '8206', 'rlm' => '8207', 'ndash' => '8211', 'mdash' => '8212', 'lsquo' => '8216', 'rsquo' => '8217',
  27097. 'sbquo' => '8218', 'ldquo' => '8220', 'rdquo' => '8221', 'bdquo' => '8222', 'dagger' => '8224', 'Dagger' => '8225', 'bull' => '8226',
  27098. 'hellip' => '8230', 'permil' => '8240', 'prime' => '8242', 'Prime' => '8243', 'lsaquo' => '8249', 'rsaquo' => '8250', 'oline' => '8254',
  27099. 'frasl' => '8260', 'euro' => '8364', 'image' => '8465', 'weierp' => '8472', 'real' => '8476', 'trade' => '8482', 'alefsym' => '8501',
  27100. 'larr' => '8592', 'uarr' => '8593', 'rarr' => '8594', 'darr' => '8595', 'harr' => '8596', 'crarr' => '8629', 'lArr' => '8656',
  27101. 'uArr' => '8657', 'rArr' => '8658', 'dArr' => '8659', 'hArr' => '8660', 'forall' => '8704', 'part' => '8706', 'exist' => '8707',
  27102. 'empty' => '8709', 'nabla' => '8711', 'isin' => '8712', 'notin' => '8713', 'ni' => '8715', 'prod' => '8719', 'sum' => '8721',
  27103. 'minus' => '8722', 'lowast' => '8727', 'radic' => '8730', 'prop' => '8733', 'infin' => '8734', 'ang' => '8736', 'and' => '8743',
  27104. 'or' => '8744', 'cap' => '8745', 'cup' => '8746', 'int' => '8747', 'there4' => '8756', 'sim' => '8764', 'cong' => '8773',
  27105. 'asymp' => '8776', 'ne' => '8800', 'equiv' => '8801', 'le' => '8804', 'ge' => '8805', 'sub' => '8834', 'sup' => '8835', 'nsub' => '8836',
  27106. 'sube' => '8838', 'supe' => '8839', 'oplus' => '8853', 'otimes' => '8855', 'perp' => '8869', 'sdot' => '8901', 'lceil' => '8968',
  27107. 'rceil' => '8969', 'lfloor' => '8970', 'rfloor' => '8971', 'lang' => '9001', 'rang' => '9002', 'loz' => '9674', 'spades' => '9824',
  27108. 'clubs' => '9827', 'hearts' => '9829', 'diams' => '9830',
  27109. );
  27110. foreach ($entarr AS $key => $val) {
  27111. $this->entsearch[] = '&' . $key . ';';
  27112. $this->entsubstitute[] = code2utf($val);
  27113. }
  27114. }
  27115. function SubstituteHiEntities($html)
  27116. {
  27117. // converts html_entities > ASCII 127 to unicode
  27118. // Leaves in particular &lt; to distinguish from tag marker
  27119. if (count($this->entsearch)) {
  27120. $html = str_replace($this->entsearch, $this->entsubstitute, $html);
  27121. }
  27122. return $html;
  27123. }
  27124. // Edited v1.2 Pass by reference; option to continue if invalid UTF-8 chars
  27125. function is_utf8(&$string)
  27126. {
  27127. if ($string === mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32")) {
  27128. return true;
  27129. } else {
  27130. if ($this->ignore_invalid_utf8) {
  27131. $string = mb_convert_encoding(mb_convert_encoding($string, "UTF-32", "UTF-8"), "UTF-8", "UTF-32");
  27132. return true;
  27133. } else {
  27134. return false;
  27135. }
  27136. }
  27137. }
  27138. function purify_utf8($html, $lo = true)
  27139. {
  27140. // For HTML
  27141. // Checks string is valid UTF-8 encoded
  27142. // converts html_entities > ASCII 127 to UTF-8
  27143. // Only exception - leaves low ASCII entities e.g. &lt; &amp; etc.
  27144. // Leaves in particular &lt; to distinguish from tag marker
  27145. if (!$this->is_utf8($html)) {
  27146. echo "<p><b>HTML contains invalid UTF-8 character(s)</b></p>";
  27147. while (mb_convert_encoding(mb_convert_encoding($html, "UTF-32", "UTF-8"), "UTF-8", "UTF-32") != $html) {
  27148. $a = iconv('UTF-8', 'UTF-8', $html);
  27149. echo ($a);
  27150. $pos = $start = strlen($a);
  27151. $err = '';
  27152. while (ord(substr($html, $pos, 1)) > 128) {
  27153. $err .= '[[#' . ord(substr($html, $pos, 1)) . ']]';
  27154. $pos++;
  27155. }
  27156. echo '<span style="color:red; font-weight:bold">' . $err . '</span>';
  27157. $html = substr($html, $pos);
  27158. }
  27159. echo $html;
  27160. throw new MpdfException("");
  27161. }
  27162. $html = preg_replace("/\r/", "", $html);
  27163. // converts html_entities > ASCII 127 to UTF-8
  27164. // Leaves in particular &lt; to distinguish from tag marker
  27165. $html = $this->SubstituteHiEntities($html);
  27166. // converts all &#nnn; or &#xHHH; to UTF-8 multibyte
  27167. // If $lo==true then includes ASCII < 128
  27168. $html = strcode2utf($html, $lo);
  27169. return ($html);
  27170. }
  27171. function purify_utf8_text($txt)
  27172. {
  27173. // For TEXT
  27174. // Make sure UTF-8 string of characters
  27175. if (!$this->is_utf8($txt)) {
  27176. throw new MpdfException("Text contains invalid UTF-8 character(s)");
  27177. }
  27178. $txt = preg_replace("/\r/", "", $txt);
  27179. return ($txt);
  27180. }
  27181. function all_entities_to_utf8($txt)
  27182. {
  27183. // converts txt_entities > ASCII 127 to UTF-8
  27184. // Leaves in particular &lt; to distinguish from tag marker
  27185. $txt = $this->SubstituteHiEntities($txt);
  27186. // converts all &#nnn; or &#xHHH; to UTF-8 multibyte
  27187. $txt = strcode2utf($txt);
  27188. $txt = $this->lesser_entity_decode($txt);
  27189. return ($txt);
  27190. }
  27191. // ====================================================
  27192. /* -- BARCODES -- */
  27193. // UPC/EAN barcode
  27194. // EAN13, EAN8, UPCA, UPCE, ISBN, ISSN
  27195. // Accepts 12 or 13 digits with or without - hyphens
  27196. function WriteBarcode($code, $showtext = 1, $x = '', $y = '', $size = 1, $border = 0, $paddingL = 1, $paddingR = 1, $paddingT = 2, $paddingB = 2, $height = 1, $bgcol = false, $col = false, $btype = 'ISBN', $supplement = '0', $supplement_code = '', $k = 1)
  27197. {
  27198. if (empty($code)) {
  27199. return;
  27200. }
  27201. $codestr = $code;
  27202. $code = preg_replace('/\-/', '', $code);
  27203. if (!class_exists('PDFBarcode', false)) {
  27204. include(_MPDF_PATH . 'classes/barcode.php');
  27205. }
  27206. $this->barcode = new PDFBarcode();
  27207. if ($btype == 'ISSN' || $btype == 'ISBN') {
  27208. $arrcode = $this->barcode->getBarcodeArray($code, 'EAN13');
  27209. } else {
  27210. $arrcode = $this->barcode->getBarcodeArray($code, $btype);
  27211. }
  27212. if ($arrcode === false) {
  27213. throw new MpdfException('Error in barcode string: ' . $codestr);
  27214. }
  27215. if ((($btype == 'EAN13' || $btype == 'ISBN' || $btype == 'ISSN') && strlen($code) == 12) || ($btype == 'UPCA' && strlen($code) == 11) || ($btype == 'UPCE' && strlen($code) == 11) || ($btype == 'EAN8' && strlen($code) == 7)) {
  27216. $code .= $arrcode['checkdigit'];
  27217. if (stristr($codestr, '-')) {
  27218. $codestr .= '-' . $arrcode['checkdigit'];
  27219. } else {
  27220. $codestr .= $arrcode['checkdigit'];
  27221. }
  27222. }
  27223. if ($btype == 'ISBN') {
  27224. $codestr = 'ISBN ' . $codestr;
  27225. }
  27226. if ($btype == 'ISSN') {
  27227. $codestr = 'ISSN ' . $codestr;
  27228. }
  27229. if (empty($x)) {
  27230. $x = $this->x;
  27231. }
  27232. if (empty($y)) {
  27233. $y = $this->y;
  27234. }
  27235. // set foreground color
  27236. $prevDrawColor = $this->DrawColor;
  27237. $prevTextColor = $this->TextColor;
  27238. $prevFillColor = $this->FillColor;
  27239. $lw = $this->LineWidth;
  27240. $this->SetLineWidth(0.01);
  27241. $size /= $k; // in case resized in a table
  27242. $xres = $arrcode['nom-X'] * $size;
  27243. $llm = $arrcode['lightmL'] * $arrcode['nom-X'] * $size; // Left Light margin
  27244. $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
  27245. $bcw = ($arrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
  27246. $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
  27247. $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
  27248. $fbwi = $fbw - 2; // Full barcode width incl. light margins - 2mm - for isbn string
  27249. // cf. http://www.gs1uk.org/downloads/bar_code/Bar coding getting it right.pdf
  27250. $num_height = 3 * $size; // Height of numerals
  27251. $fbh = $arrcode['nom-H'] * $size * $height; // Full barcode height incl. numerals
  27252. $bch = $fbh - (1.5 * $size); // Barcode height of bars (3mm for numerals)
  27253. if (($btype == 'EAN13' && $showtext) || $btype == 'ISSN' || $btype == 'ISBN') { // Add height for ISBN string + margin from top of bars
  27254. $tisbnm = 1.5 * $size; // Top margin between isbn (if shown) & bars
  27255. $codestr_fontsize = 2.1 * $size;
  27256. $paddingT += $codestr_fontsize + $tisbnm;
  27257. }
  27258. $oh = $fbh + $paddingT + $paddingB; // Full overall height incl. user-defined padding
  27259. // PRINT border background color
  27260. $xpos = $x;
  27261. $ypos = $y;
  27262. if ($col) {
  27263. $this->SetDColor($col);
  27264. $this->SetTColor($col);
  27265. } else {
  27266. $this->SetDColor($this->ConvertColor(0));
  27267. $this->SetTColor($this->ConvertColor(0));
  27268. }
  27269. if ($bgcol) {
  27270. $this->SetFColor($bgcol);
  27271. } else {
  27272. $this->SetFColor($this->ConvertColor(255));
  27273. }
  27274. if (!$bgcol && !$col) { // fn. called directly - not via HTML
  27275. if ($border) {
  27276. $fillb = 'DF';
  27277. } else {
  27278. $fillb = 'F';
  27279. }
  27280. $this->Rect($xpos, $ypos, $ow, $oh, $fillb);
  27281. }
  27282. // PRINT BARS
  27283. $xpos = $x + $paddingL + $llm;
  27284. $ypos = $y + $paddingT;
  27285. if ($col) {
  27286. $this->SetFColor($col);
  27287. } else {
  27288. $this->SetFColor($this->ConvertColor(0));
  27289. }
  27290. if ($arrcode !== false) {
  27291. foreach ($arrcode["bcode"] AS $v) {
  27292. $bw = ($v["w"] * $xres);
  27293. if ($v["t"]) {
  27294. // draw a vertical bar
  27295. $this->Rect($xpos, $ypos, $bw, $bch, 'F');
  27296. }
  27297. $xpos += $bw;
  27298. }
  27299. }
  27300. // print text
  27301. $prevFontFamily = $this->FontFamily;
  27302. $prevFontStyle = $this->FontStyle;
  27303. $prevFontSizePt = $this->FontSizePt;
  27304. // ISBN string
  27305. if (($btype == 'EAN13' && $showtext) || $btype == 'ISBN' || $btype == 'ISSN') {
  27306. if ($this->onlyCoreFonts) {
  27307. $this->SetFont('chelvetica');
  27308. } else {
  27309. $this->SetFont('sans');
  27310. }
  27311. if ($bgcol) {
  27312. $this->SetFColor($bgcol);
  27313. } else {
  27314. $this->SetFColor($this->ConvertColor(255));
  27315. }
  27316. $this->x = $x + $paddingL + 1; // 1mm left margin (cf. $fbwi above)
  27317. // max width is $fbwi
  27318. $loop = 0;
  27319. while ($loop == 0) {
  27320. $this->SetFontSize($codestr_fontsize * 1.4 * _MPDFK, false); // don't write
  27321. $sz = $this->GetStringWidth($codestr);
  27322. if ($sz > $fbwi)
  27323. $codestr_fontsize -= 0.1;
  27324. else
  27325. $loop ++;
  27326. }
  27327. $this->SetFont('', '', $codestr_fontsize * 1.4 * _MPDFK, true, true); // * 1.4 because font height is only 7/10 of given mm
  27328. // WORD SPACING
  27329. if ($fbwi > $sz) {
  27330. $xtra = $fbwi - $sz;
  27331. $charspacing = $xtra / (strlen($codestr) - 1);
  27332. if ($charspacing) {
  27333. $this->_out(sprintf('BT %.3F Tc ET', $charspacing * _MPDFK));
  27334. }
  27335. }
  27336. $this->y = $y + $paddingT - ($codestr_fontsize ) - $tisbnm;
  27337. $this->Cell($fbw, $codestr_fontsize, $codestr);
  27338. if ($charspacing) {
  27339. $this->_out('BT 0 Tc ET');
  27340. }
  27341. }
  27342. // Bottom NUMERALS
  27343. // mPDF 5.7.4
  27344. if ($this->onlyCoreFonts) {
  27345. $this->SetFont('ccourier');
  27346. $fh = 1.3;
  27347. } else {
  27348. $this->SetFont('ocrb');
  27349. $fh = 1.06;
  27350. }
  27351. $charRO = '';
  27352. if ($btype == 'EAN13' || $btype == 'ISBN' || $btype == 'ISSN') {
  27353. $outerfontsize = 3; // Inner fontsize = 3
  27354. $outerp = $xres * 4;
  27355. $innerp = $xres * 2.5;
  27356. $textw = ($bcw * 0.5) - $outerp - $innerp;
  27357. $chars = 6; // number of numerals in each half
  27358. $charLO = substr($code, 0, 1); // Left Outer
  27359. $charLI = substr($code, 1, 6); // Left Inner
  27360. $charRI = substr($code, 7, 6); // Right Inner
  27361. if (!$supplement)
  27362. $charRO = '>'; // Right Outer
  27363. }
  27364. elseif ($btype == 'UPCA') {
  27365. $outerfontsize = 2.3; // Inner fontsize = 3
  27366. $outerp = $xres * 10;
  27367. $innerp = $xres * 2.5;
  27368. $textw = ($bcw * 0.5) - $outerp - $innerp;
  27369. $chars = 5;
  27370. $charLO = substr($code, 0, 1); // Left Outer
  27371. $charLI = substr($code, 1, 5); // Left Inner
  27372. $charRI = substr($code, 6, 5); // Right Inner
  27373. $charRO = substr($code, 11, 1); // Right Outer
  27374. } elseif ($btype == 'UPCE') {
  27375. $outerfontsize = 2.3; // Inner fontsize = 3
  27376. $outerp = $xres * 4;
  27377. $innerp = 0;
  27378. $textw = ($bcw * 0.5) - $outerp - $innerp;
  27379. $chars = 3;
  27380. $upce_code = $arrcode['code'];
  27381. $charLO = substr($code, 0, 1); // Left Outer
  27382. $charLI = substr($upce_code, 0, 3); // Left Inner
  27383. $charRI = substr($upce_code, 3, 3); // Right Inner
  27384. $charRO = substr($code, 11, 1); // Right Outer
  27385. } elseif ($btype == 'EAN8') {
  27386. $outerfontsize = 3; // Inner fontsize = 3
  27387. $outerp = $xres * 4;
  27388. $innerp = $xres * 2.5;
  27389. $textw = ($bcw * 0.5) - $outerp - $innerp;
  27390. $chars = 4;
  27391. $charLO = '<'; // Left Outer
  27392. $charLI = substr($code, 0, 4); // Left Inner
  27393. $charRI = substr($code, 4, 4); // Right Inner
  27394. if (!$supplement)
  27395. $charRO = '>'; // Right Outer
  27396. }
  27397. $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * _MPDFK); // 3mm numerals (FontSize is larger to account for space above/below characters)
  27398. if (!$this->usingCoreFont) {
  27399. $cw = $this->_getCharWidth($this->CurrentFont['cw'], 32) * 3 * $fh * $size / 1000;
  27400. } // character width at 3mm
  27401. else {
  27402. $cw = 600 * 3 * $fh * $size / 1000;
  27403. } // mPDF 5.7.4
  27404. // Outer left character
  27405. $y_text = $y + $paddingT + $bch - ($num_height / 2);
  27406. $y_text_outer = $y + $paddingT + $bch - ($num_height * ($outerfontsize / 3) / 2);
  27407. $this->x = $x + $paddingL - ($cw * ($outerfontsize / 3) * 0.1); // 0.1 is correction as char does not fill full width;
  27408. $this->y = $y_text_outer;
  27409. $this->Cell($cw, $num_height, $charLO);
  27410. // WORD SPACING for inner chars
  27411. $xtra = $textw - ($cw * $chars);
  27412. $charspacing = $xtra / ($chars - 1);
  27413. if ($charspacing) {
  27414. $this->_out(sprintf('BT %.3F Tc ET', $charspacing * _MPDFK));
  27415. }
  27416. if ($bgcol) {
  27417. $this->SetFColor($bgcol);
  27418. } else {
  27419. $this->SetFColor($this->ConvertColor(255));
  27420. }
  27421. $this->SetFontSize(3 * $fh * $size * _MPDFK); // 3mm numerals (FontSize is larger to account for space above/below characters)
  27422. // Inner left half characters
  27423. $this->x = $x + $paddingL + $llm + $outerp;
  27424. $this->y = $y_text;
  27425. $this->Cell($textw, $num_height, $charLI, 0, 0, '', 1);
  27426. // Inner right half characters
  27427. $this->x = $x + $paddingL + $llm + ($bcw * 0.5) + $innerp;
  27428. $this->y = $y_text;
  27429. $this->Cell($textw, $num_height, $charRI, 0, 0, '', 1);
  27430. if ($charspacing) {
  27431. $this->_out('BT 0 Tc ET');
  27432. }
  27433. // Outer Right character
  27434. $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * _MPDFK); // 3mm numerals (FontSize is larger to account for space above/below characters)
  27435. $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * ($outerfontsize / 3) * 0.9); // 0.9 is correction as char does not fill full width
  27436. $this->y = $y_text_outer;
  27437. $this->Cell($cw * ($outerfontsize / 3), $num_height, $charRO, 0, 0, 'R');
  27438. if ($supplement) { // EAN-2 or -5 Supplement
  27439. // PRINT BARS
  27440. $supparrcode = $this->barcode->getBarcodeArray($supplement_code, 'EAN' . $supplement);
  27441. if ($supparrcode === false) {
  27442. throw new MpdfException('Error in barcode string (supplement): ' . $codestr . ' ' . $supplement_code);
  27443. }
  27444. if (strlen($supplement_code) != $supplement) {
  27445. throw new MpdfException('Barcode supplement incorrect: ' . $supplement_code);
  27446. }
  27447. $llm = $fbw - (($arrcode['lightmR'] - $supparrcode['sepM']) * $arrcode['nom-X'] * $size); // Left Light margin
  27448. $rlm = $arrcode['lightmR'] * $arrcode['nom-X'] * $size; // Right Light margin
  27449. $bcw = ($supparrcode["maxw"] * $xres); // Barcode width = Should always be 31.35mm * $size
  27450. $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
  27451. $ow = $fbw + $paddingL + $paddingR; // Full overall width incl. user-defined padding
  27452. $bch = $fbh - (1.5 * $size) - ($num_height + 0.5); // Barcode height of bars (3mm for numerals)
  27453. $xpos = $x + $paddingL + $llm;
  27454. $ypos = $y + $paddingT + $num_height + 0.5;
  27455. if ($col) {
  27456. $this->SetFColor($col);
  27457. } else {
  27458. $this->SetFColor($this->ConvertColor(0));
  27459. }
  27460. if ($supparrcode !== false) {
  27461. foreach ($supparrcode["bcode"] AS $v) {
  27462. $bw = ($v["w"] * $xres);
  27463. if ($v["t"]) {
  27464. // draw a vertical bar
  27465. $this->Rect($xpos, $ypos, $bw, $bch, 'F');
  27466. }
  27467. $xpos += $bw;
  27468. }
  27469. }
  27470. // Characters
  27471. if ($bgcol) {
  27472. $this->SetFColor($bgcol);
  27473. } else {
  27474. $this->SetFColor($this->ConvertColor(255));
  27475. }
  27476. $this->SetFontSize(3 * $fh * $size * _MPDFK); // 3mm numerals (FontSize is larger to account for space above/below characters)
  27477. $this->x = $x + $paddingL + $llm;
  27478. $this->y = $y + $paddingT;
  27479. $this->Cell($bcw, $num_height, $supplement_code, 0, 0, 'C');
  27480. // Outer Right character (light margin)
  27481. $this->SetFontSize(($outerfontsize / 3) * 3 * $fh * $size * _MPDFK); // 3mm numerals (FontSize is larger to account for space above/below characters)
  27482. $this->x = $x + $paddingL + $llm + $bcw + $rlm - ($cw * 0.9); // 0.9 is correction as char does not fill full width
  27483. $this->y = $y + $paddingT;
  27484. $this->Cell($cw * ($outerfontsize / 3), $num_height, '>', 0, 0, 'R');
  27485. }
  27486. // Restore **************
  27487. $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
  27488. $this->DrawColor = $prevDrawColor;
  27489. $this->TextColor = $prevTextColor;
  27490. $this->FillColor = $prevFillColor;
  27491. $this->SetLineWidth($lw);
  27492. $this->SetY($y);
  27493. }
  27494. // ====================================================
  27495. // POSTAL and OTHER barcodes
  27496. function WriteBarcode2($code, $x = '', $y = '', $size = 1, $height = 1, $bgcol = false, $col = false, $btype = 'IMB', $print_ratio = '', $k = 1)
  27497. {
  27498. if (empty($code)) {
  27499. return;
  27500. }
  27501. if (!class_exists('PDFBarcode', false)) {
  27502. include(_MPDF_PATH . 'classes/barcode.php');
  27503. }
  27504. $this->barcode = new PDFBarcode();
  27505. $arrcode = $this->barcode->getBarcodeArray($code, $btype, $print_ratio);
  27506. if ($arrcode === false) {
  27507. throw new MpdfException('Error in barcode string: ' . $code);
  27508. }
  27509. if (empty($x)) {
  27510. $x = $this->x;
  27511. }
  27512. if (empty($y)) {
  27513. $y = $this->y;
  27514. }
  27515. $prevDrawColor = $this->DrawColor;
  27516. $prevTextColor = $this->TextColor;
  27517. $prevFillColor = $this->FillColor;
  27518. $lw = $this->LineWidth;
  27519. $this->SetLineWidth(0.01);
  27520. $size /= $k; // in case resized in a table
  27521. $xres = $arrcode['nom-X'] * $size;
  27522. if ($btype == 'IMB' || $btype == 'RM4SCC' || $btype == 'KIX' || $btype == 'POSTNET' || $btype == 'PLANET') {
  27523. $llm = $arrcode['quietL'] / $k; // Left Quiet margin
  27524. $rlm = $arrcode['quietR'] / $k; // Right Quiet margin
  27525. $tlm = $blm = $arrcode['quietTB'] / $k;
  27526. $height = 1; // Overrides
  27527. } elseif (in_array($btype, array('C128A', 'C128B', 'C128C', 'EAN128A', 'EAN128B', 'EAN128C', 'C39', 'C39+', 'C39E', 'C39E+', 'S25', 'S25+', 'I25', 'I25+', 'I25B', 'I25B+', 'C93', 'MSI', 'MSI+', 'CODABAR', 'CODE11'))) {
  27528. $llm = $arrcode['lightmL'] * $xres; // Left Quiet margin
  27529. $rlm = $arrcode['lightmR'] * $xres; // Right Quiet margin
  27530. $tlm = $blm = $arrcode['lightTB'] * $xres * $height;
  27531. }
  27532. $bcw = ($arrcode["maxw"] * $xres);
  27533. $fbw = $bcw + $llm + $rlm; // Full barcode width incl. light margins
  27534. $bch = ($arrcode["nom-H"] * $size * $height);
  27535. $fbh = $bch + $tlm + $blm; // Full barcode height
  27536. // PRINT border background color
  27537. $xpos = $x;
  27538. $ypos = $y;
  27539. if ($col) {
  27540. $this->SetDColor($col);
  27541. $this->SetTColor($col);
  27542. } else {
  27543. $this->SetDColor($this->ConvertColor(0));
  27544. $this->SetTColor($this->ConvertColor(0));
  27545. }
  27546. if ($bgcol) {
  27547. $this->SetFColor($bgcol);
  27548. } else {
  27549. $this->SetFColor($this->ConvertColor(255));
  27550. }
  27551. // PRINT BARS
  27552. if ($col) {
  27553. $this->SetFColor($col);
  27554. } else {
  27555. $this->SetFColor($this->ConvertColor(0));
  27556. }
  27557. $xpos = $x + $llm;
  27558. if ($arrcode !== false) {
  27559. foreach ($arrcode["bcode"] AS $v) {
  27560. $bw = ($v["w"] * $xres);
  27561. if ($v["t"]) {
  27562. $ypos = $y + $tlm + ($bch * $v['p'] / $arrcode['maxh']);
  27563. $this->Rect($xpos, $ypos, $bw, ($v['h'] * $bch / $arrcode['maxh']), 'F');
  27564. }
  27565. $xpos += $bw;
  27566. }
  27567. }
  27568. // PRINT BEARER BARS
  27569. if ($btype == 'I25B' || $btype == 'I25B+') {
  27570. $this->Rect($x, $y, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
  27571. $this->Rect($x, $y + $tlm + $bch, $fbw, ($arrcode['lightTB'] * $xres * $height), 'F');
  27572. }
  27573. // Restore **************
  27574. $this->DrawColor = $prevDrawColor;
  27575. $this->TextColor = $prevTextColor;
  27576. $this->FillColor = $prevFillColor;
  27577. $this->SetLineWidth($lw);
  27578. $this->SetY($y);
  27579. }
  27580. /* -- END BARCODES -- */
  27581. // ====================================================
  27582. // ====================================================
  27583. function StartTransform($returnstring = false)
  27584. {
  27585. if ($returnstring) {
  27586. return('q');
  27587. } else {
  27588. $this->_out('q');
  27589. }
  27590. }
  27591. function StopTransform($returnstring = false)
  27592. {
  27593. if ($returnstring) {
  27594. return('Q');
  27595. } else {
  27596. $this->_out('Q');
  27597. }
  27598. }
  27599. function transformScale($s_x, $s_y, $x = '', $y = '', $returnstring = false)
  27600. {
  27601. if ($x === '') {
  27602. $x = $this->x;
  27603. }
  27604. if ($y === '') {
  27605. $y = $this->y;
  27606. }
  27607. if (($s_x == 0) OR ( $s_y == 0)) {
  27608. throw new MpdfException('Please do not use values equal to zero for scaling');
  27609. }
  27610. $y = ($this->h - $y) * _MPDFK;
  27611. $x *= _MPDFK;
  27612. //calculate elements of transformation matrix
  27613. $s_x /= 100;
  27614. $s_y /= 100;
  27615. $tm = array();
  27616. $tm[0] = $s_x;
  27617. $tm[1] = 0;
  27618. $tm[2] = 0;
  27619. $tm[3] = $s_y;
  27620. $tm[4] = $x * (1 - $s_x);
  27621. $tm[5] = $y * (1 - $s_y);
  27622. //scale the coordinate system
  27623. if ($returnstring) {
  27624. return($this->_transform($tm, true));
  27625. } else {
  27626. $this->_transform($tm);
  27627. }
  27628. }
  27629. function transformTranslate($t_x, $t_y, $returnstring = false)
  27630. {
  27631. //calculate elements of transformation matrix
  27632. $tm = array();
  27633. $tm[0] = 1;
  27634. $tm[1] = 0;
  27635. $tm[2] = 0;
  27636. $tm[3] = 1;
  27637. $tm[4] = $t_x * _MPDFK;
  27638. $tm[5] = -$t_y * _MPDFK;
  27639. //translate the coordinate system
  27640. if ($returnstring) {
  27641. return($this->_transform($tm, true));
  27642. } else {
  27643. $this->_transform($tm);
  27644. }
  27645. }
  27646. function transformRotate($angle, $x = '', $y = '', $returnstring = false)
  27647. {
  27648. if ($x === '') {
  27649. $x = $this->x;
  27650. }
  27651. if ($y === '') {
  27652. $y = $this->y;
  27653. }
  27654. $angle = -$angle;
  27655. $y = ($this->h - $y) * _MPDFK;
  27656. $x *= _MPDFK;
  27657. //calculate elements of transformation matrix
  27658. $tm = array();
  27659. $tm[0] = cos(deg2rad($angle));
  27660. $tm[1] = sin(deg2rad($angle));
  27661. $tm[2] = -$tm[1];
  27662. $tm[3] = $tm[0];
  27663. $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
  27664. $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
  27665. //rotate the coordinate system around ($x,$y)
  27666. if ($returnstring) {
  27667. return($this->_transform($tm, true));
  27668. } else {
  27669. $this->_transform($tm);
  27670. }
  27671. }
  27672. // mPDF 5.7.3 TRANSFORMS
  27673. function transformSkew($angle_x, $angle_y, $x = '', $y = '', $returnstring = false)
  27674. {
  27675. if ($x === '') {
  27676. $x = $this->x;
  27677. }
  27678. if ($y === '') {
  27679. $y = $this->y;
  27680. }
  27681. $angle_x = -$angle_x;
  27682. $angle_y = -$angle_y;
  27683. $x *= _MPDFK;
  27684. $y = ($this->h - $y) * _MPDFK;
  27685. //calculate elements of transformation matrix
  27686. $tm = array();
  27687. $tm[0] = 1;
  27688. $tm[1] = tan(deg2rad($angle_y));
  27689. $tm[2] = tan(deg2rad($angle_x));
  27690. $tm[3] = 1;
  27691. $tm[4] = -$tm[2] * $y;
  27692. $tm[5] = -$tm[1] * $x;
  27693. //skew the coordinate system
  27694. if ($returnstring) {
  27695. return($this->_transform($tm, true));
  27696. } else {
  27697. $this->_transform($tm);
  27698. }
  27699. }
  27700. function _transform($tm, $returnstring = false)
  27701. {
  27702. if ($returnstring) {
  27703. return(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
  27704. } else {
  27705. $this->_out(sprintf('%.4F %.4F %.4F %.4F %.4F %.4F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
  27706. }
  27707. }
  27708. // AUTOFONT =========================
  27709. function markScriptToLang($html)
  27710. {
  27711. if ($this->onlyCoreFonts) {
  27712. return $html;
  27713. }
  27714. // sets $this->script2lang
  27715. if (empty($this->script2lang)) {
  27716. include(_MPDF_PATH . 'config_script2lang.php');
  27717. }
  27718. $n = '';
  27719. $a = preg_split('/<(.*?)>/ms', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
  27720. foreach ($a as $i => $e) {
  27721. if ($i % 2 == 0) {
  27722. //ignore if in Textarea
  27723. if ($i > 0 && strtolower(substr($a[$i - 1], 1, 8)) == 'textarea') {
  27724. $a[$i] = $e;
  27725. continue;
  27726. }
  27727. $e = strcode2utf($e);
  27728. $e = $this->lesser_entity_decode($e);
  27729. $earr = $this->UTF8StringToArray($e, false);
  27730. $scriptblock = 0;
  27731. $scriptblocks = array();
  27732. $scriptblocks[0] = 0;
  27733. $chardata = array();
  27734. $subchunk = 0;
  27735. $charctr = 0;
  27736. foreach ($earr as $char) {
  27737. $ucd_record = UCDN::get_ucd_record($char);
  27738. $sbl = $ucd_record[6];
  27739. if ($sbl && $sbl != 40 && $sbl != 102) {
  27740. if ($scriptblock == 0) {
  27741. $scriptblock = $sbl;
  27742. $scriptblocks[$subchunk] = $scriptblock;
  27743. } elseif ($scriptblock > 0 && $scriptblock != $sbl) {
  27744. // NEW (non-common) Script encountered in this chunk.
  27745. // Start a new subchunk
  27746. $subchunk++;
  27747. $scriptblock = $sbl;
  27748. $charctr = 0;
  27749. $scriptblocks[$subchunk] = $scriptblock;
  27750. }
  27751. }
  27752. $chardata[$subchunk][$charctr]['script'] = $sbl;
  27753. $chardata[$subchunk][$charctr]['uni'] = $char;
  27754. $charctr++;
  27755. }
  27756. // If scriptblock[x] = common & non-baseScript
  27757. // and scriptblock[x+1] = baseScript
  27758. // Move common script from end of x to start of x+1
  27759. for ($sch = 0; $sch < $subchunk; $sch++) {
  27760. if ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && $scriptblocks[$sch + 1] == $this->baseScript) {
  27761. $end = count($chardata[$sch]) - 1;
  27762. while ($chardata[$sch][$end]['script'] == 0 && $end > 1) { // common script
  27763. $tmp = array_pop($chardata[$sch]);
  27764. array_unshift($chardata[$sch + 1], $tmp);
  27765. $end--;
  27766. }
  27767. }
  27768. }
  27769. $o = '';
  27770. for ($sch = 0; $sch <= $subchunk; $sch++) {
  27771. if (isset($chardata[$sch])) {
  27772. $s = '';
  27773. for ($j = 0; $j < count($chardata[$sch]); $j++) {
  27774. $s.=code2utf($chardata[$sch][$j]['uni']);
  27775. }
  27776. // ZZZ99 Undo lesser_entity_decode as above - but only for <>&
  27777. $s = str_replace("&", "&amp;", $s);
  27778. $s = str_replace("<", "&lt;", $s);
  27779. $s = str_replace(">", "&gt;", $s);
  27780. // Check Vietnamese if Latin script - even if Basescript
  27781. if ($scriptblocks[$sch] == UCDN::SCRIPT_LATIN && $this->autoVietnamese && preg_match("/([" . $this->viet . "])/u", $s)) {
  27782. $o .= '<span lang="vi" class="lang_vi">' . $s . '</span>';
  27783. }
  27784. // Check Arabic for different languages if Arabic script - even if Basescript
  27785. elseif ($scriptblocks[$sch] == UCDN::SCRIPT_ARABIC && $this->autoArabic) {
  27786. if (preg_match("/[" . $this->sindhi . "]/u", $s)) {
  27787. $o .= '<span lang="sd" class="lang_sd">' . $s . '</span>';
  27788. } elseif (preg_match("/[" . $this->urdu . "]/u", $s)) {
  27789. $o .= '<span lang="ur" class="lang_ur">' . $s . '</span>';
  27790. } elseif (preg_match("/[" . $this->pashto . "]/u", $s)) {
  27791. $o .= '<span lang="ps" class="lang_ps">' . $s . '</span>';
  27792. } elseif (preg_match("/[" . $this->persian . "]/u", $s)) {
  27793. $o .= '<span lang="fa" class="lang_fa">' . $s . '</span>';
  27794. } elseif ($this->baseScript != UCDN::SCRIPT_ARABIC && isset($this->script2lang[$scriptblocks[$sch]])) {
  27795. $o .= '<span lang="' . $this->script2lang[$scriptblocks[$sch]] . '" class="lang_' . $this->script2lang[$scriptblocks[$sch]] . '">' . $s . '</span>';
  27796. } else {
  27797. // Just output chars
  27798. $o .= $s;
  27799. }
  27800. }
  27801. // Identify Script block if not Basescript, and mark up as language
  27802. elseif ($scriptblocks[$sch] > 0 && $scriptblocks[$sch] != $this->baseScript && isset($this->script2lang[$scriptblocks[$sch]])) {
  27803. // Encase in <span>
  27804. $o .= '<span lang="' . $this->script2lang[$scriptblocks[$sch]] . '" class="lang_' . $this->script2lang[$scriptblocks[$sch]] . '">';
  27805. $o .= $s;
  27806. $o .= '</span>';
  27807. } else {
  27808. // Just output chars
  27809. $o .= $s;
  27810. }
  27811. }
  27812. }
  27813. $a[$i] = $o;
  27814. } else {
  27815. $a[$i] = '<' . $e . '>';
  27816. }
  27817. }
  27818. $n = implode('', $a);
  27819. return $n;
  27820. }
  27821. //===========================
  27822. // Functions
  27823. // Call-back function Used for usort in fn _tableWrite
  27824. function _cmpdom($a, $b)
  27825. {
  27826. return ($a["dom"] < $b["dom"]) ? -1 : 1;
  27827. }
  27828. function mb_strrev($str, $enc = 'utf-8')
  27829. {
  27830. $ch = array();
  27831. $ch = preg_split('//u', $str);
  27832. $revch = array_reverse($ch);
  27833. return implode('', $revch);
  27834. }
  27835. /* -- COLUMNS -- */
  27836. // Callback function from function printcolumnbuffer in mpdf
  27837. function columnAdjustAdd($type, $k, $xadj, $yadj, $a, $b, $c = 0, $d = 0, $e = 0, $f = 0)
  27838. {
  27839. if ($type == 'Td') { // xpos,ypos
  27840. $a += ($xadj * $k);
  27841. $b -= ($yadj * $k);
  27842. return 'BT ' . sprintf('%.3F %.3F', $a, $b) . ' Td';
  27843. } elseif ($type == 're') { // xpos,ypos,width,height
  27844. $a += ($xadj * $k);
  27845. $b -= ($yadj * $k);
  27846. return sprintf('%.3F %.3F %.3F %.3F', $a, $b, $c, $d) . ' re';
  27847. } elseif ($type == 'l') { // xpos,ypos,x2pos,y2pos
  27848. $a += ($xadj * $k);
  27849. $b -= ($yadj * $k);
  27850. return sprintf('%.3F %.3F l', $a, $b);
  27851. } elseif ($type == 'img') { // width,height,xpos,ypos
  27852. $c += ($xadj * $k);
  27853. $d -= ($yadj * $k);
  27854. return sprintf('q %.3F 0 0 %.3F %.3F %.3F', $a, $b, $c, $d) . ' cm /' . $e;
  27855. } elseif ($type == 'draw') { // xpos,ypos
  27856. $a += ($xadj * $k);
  27857. $b -= ($yadj * $k);
  27858. return sprintf('%.3F %.3F m', $a, $b);
  27859. } elseif ($type == 'bezier') { // xpos,ypos,x2pos,y2pos,x3pos,y3pos
  27860. $a += ($xadj * $k);
  27861. $b -= ($yadj * $k);
  27862. $c += ($xadj * $k);
  27863. $d -= ($yadj * $k);
  27864. $e += ($xadj * $k);
  27865. $f -= ($yadj * $k);
  27866. return sprintf('%.3F %.3F %.3F %.3F %.3F %.3F', $a, $b, $c, $d, $e, $f) . ' c';
  27867. }
  27868. }
  27869. /* -- END COLUMNS -- */
  27870. function ConvertColor($color = "#000000")
  27871. {
  27872. static $cache;
  27873. $color = trim(strtolower($color));
  27874. $c = false;
  27875. $cstr = '';
  27876. if ($color == 'transparent') {
  27877. return false;
  27878. } elseif ($color == 'inherit') {
  27879. return false;
  27880. } elseif (isset($this->SVGcolors[$color]))
  27881. $color = $this->SVGcolors[$color];
  27882. if (!isset($cache[$color])) {
  27883. if (preg_match('/^[\d]+$/', $color)) {
  27884. $c = (array(1, $color));
  27885. } // i.e. integer only
  27886. elseif ($color[0] == '#') { //case of #nnnnnn or #nnn
  27887. $cor = preg_replace('/\s+.*/', '', $color); // in case of Background: #CCC url() x-repeat etc.
  27888. if (strlen($cor) == 4) { // Turn #RGB into #RRGGBB
  27889. $cor = "#" . $cor[1] . $cor[1] . $cor[2] . $cor[2] . $cor[3] . $cor[3];
  27890. }
  27891. $r = hexdec(substr($cor, 1, 2));
  27892. $g = hexdec(substr($cor, 3, 2));
  27893. $b = hexdec(substr($cor, 5, 2));
  27894. $c = array(3, $r, $g, $b);
  27895. } elseif (preg_match('/(rgba|rgb|device-cmyka|cmyka|device-cmyk|cmyk|hsla|hsl|spot)\((.*?)\)/', $color, $m)) {
  27896. $type = $m[1];
  27897. $cores = explode(",", $m[2]);
  27898. $ncores = count($cores);
  27899. if (stristr($cores[0], '%')) {
  27900. $cores[0] += 0;
  27901. if ($type == 'rgb' || $type == 'rgba') {
  27902. $cores[0] = intval($cores[0] * 255 / 100);
  27903. }
  27904. }
  27905. if ($ncores > 1 && stristr($cores[1], '%')) {
  27906. $cores[1] += 0;
  27907. if ($type == 'rgb' || $type == 'rgba') {
  27908. $cores[1] = intval($cores[1] * 255 / 100);
  27909. }
  27910. if ($type == 'hsl' || $type == 'hsla') {
  27911. $cores[1] = $cores[1] / 100;
  27912. }
  27913. }
  27914. if ($ncores > 2 && stristr($cores[2], '%')) {
  27915. $cores[2] += 0;
  27916. if ($type == 'rgb' || $type == 'rgba') {
  27917. $cores[2] = intval($cores[2] * 255 / 100);
  27918. }
  27919. if ($type == 'hsl' || $type == 'hsla') {
  27920. $cores[2] = $cores[2] / 100;
  27921. }
  27922. }
  27923. if ($ncores > 3 && stristr($cores[3], '%')) {
  27924. $cores[3] += 0;
  27925. }
  27926. if ($type == 'rgb') {
  27927. $c = array(3, $cores[0], $cores[1], $cores[2]);
  27928. } elseif ($type == 'rgba') {
  27929. $c = array(5, $cores[0], $cores[1], $cores[2], $cores[3] * 100);
  27930. } elseif ($type == 'cmyk' || $type == 'device-cmyk') {
  27931. $c = array(4, $cores[0], $cores[1], $cores[2], $cores[3]);
  27932. } elseif ($type == 'cmyka' || $type == 'device-cmyka') {
  27933. $c = array(6, $cores[0], $cores[1], $cores[2], $cores[3], $cores[4] * 100);
  27934. } elseif ($type == 'hsl' || $type == 'hsla') {
  27935. $conv = $this->hsl2rgb($cores[0] / 360, $cores[1], $cores[2]);
  27936. if ($type == 'hsl') {
  27937. $c = array(3, $conv[0], $conv[1], $conv[2]);
  27938. } elseif ($type == 'hsla') {
  27939. $c = array(5, $conv[0], $conv[1], $conv[2], $cores[3] * 100);
  27940. }
  27941. } elseif ($type == 'spot') {
  27942. $name = strtoupper(trim($cores[0]));
  27943. if (!isset($this->spotColors[$name])) {
  27944. if (isset($cores[5])) {
  27945. $this->AddSpotColor($cores[0], $cores[2], $cores[3], $cores[4], $cores[5]);
  27946. } else {
  27947. throw new MpdfException('Undefined spot color: ' . $name);
  27948. }
  27949. }
  27950. $c = array(2, $this->spotColors[$name]['i'], $cores[1]);
  27951. }
  27952. }
  27953. // $this->restrictColorSpace
  27954. // 1 - allow GRAYSCALE only [convert CMYK/RGB->gray]
  27955. // 2 - allow RGB / SPOT COLOR / Grayscale [convert CMYK->RGB]
  27956. // 3 - allow CMYK / SPOT COLOR / Grayscale [convert RGB->CMYK]
  27957. if ($this->PDFA || $this->PDFX || $this->restrictColorSpace) {
  27958. if ($c[0] == 1) { // GRAYSCALE
  27959. } elseif ($c[0] == 2) { // SPOT COLOR
  27960. if (!isset($this->spotColorIDs[$c[1]])) {
  27961. throw new MpdfException('Error: Spot colour has not been defined - ' . $this->spotColorIDs[$c[1]]);
  27962. }
  27963. if ($this->PDFA) {
  27964. if ($this->PDFA && !$this->PDFAauto) {
  27965. $this->PDFAXwarnings[] = "Spot color specified '" . $this->spotColorIDs[$c[1]] . "' (converted to process color)";
  27966. }
  27967. if ($this->restrictColorSpace != 3) {
  27968. $sp = $this->spotColors[$this->spotColorIDs[$c[1]]];
  27969. $c = $this->cmyk2rgb(array(4, $sp['c'], $sp['m'], $sp['y'], $sp['k']));
  27970. }
  27971. } elseif ($this->restrictColorSpace == 1) {
  27972. $sp = $this->spotColors[$this->spotColorIDs[$c[1]]];
  27973. $c = $this->cmyk2gray(array(4, $sp['c'], $sp['m'], $sp['y'], $sp['k']));
  27974. }
  27975. }
  27976. // RGB
  27977. elseif ($c[0] == 3) {
  27978. if ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
  27979. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  27980. $this->PDFAXwarnings[] = "RGB color specified '" . $color . "' (converted to CMYK)";
  27981. }
  27982. $c = $this->rgb2cmyk($c);
  27983. } elseif ($this->restrictColorSpace == 1) {
  27984. $c = $this->rgb2gray($c);
  27985. } elseif ($this->restrictColorSpace == 3) {
  27986. $c = $this->rgb2cmyk($c);
  27987. }
  27988. }
  27989. // CMYK
  27990. elseif ($c[0] == 4) {
  27991. if ($this->PDFA && $this->restrictColorSpace != 3) {
  27992. if ($this->PDFA && !$this->PDFAauto) {
  27993. $this->PDFAXwarnings[] = "CMYK color specified '" . $color . "' (converted to RGB)";
  27994. }
  27995. $c = $this->cmyk2rgb($c);
  27996. } elseif ($this->restrictColorSpace == 1) {
  27997. $c = $this->cmyk2gray($c);
  27998. } elseif ($this->restrictColorSpace == 2) {
  27999. $c = $this->cmyk2rgb($c);
  28000. }
  28001. }
  28002. // RGBa
  28003. elseif ($c[0] == 5) {
  28004. if ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
  28005. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  28006. $this->PDFAXwarnings[] = "RGB color with transparency specified '" . $color . "' (converted to CMYK without transparency)";
  28007. }
  28008. $c = $this->rgb2cmyk($c);
  28009. $c = array(4, $c[1], $c[2], $c[3], $c[4]);
  28010. } elseif ($this->PDFA && $this->restrictColorSpace != 3) {
  28011. if (!$this->PDFAauto) {
  28012. $this->PDFAXwarnings[] = "RGB color with transparency specified '" . $color . "' (converted to RGB without transparency)";
  28013. }
  28014. $c = $this->rgb2cmyk($c);
  28015. $c = array(4, $c[1], $c[2], $c[3], $c[4]);
  28016. } elseif ($this->restrictColorSpace == 1) {
  28017. $c = $this->rgb2gray($c);
  28018. } elseif ($this->restrictColorSpace == 3) {
  28019. $c = $this->rgb2cmyk($c);
  28020. }
  28021. }
  28022. // CMYKa
  28023. elseif ($c[0] == 6) {
  28024. if ($this->PDFA && $this->restrictColorSpace != 3) {
  28025. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  28026. $this->PDFAXwarnings[] = "CMYK color with transparency specified '" . $color . "' (converted to RGB without transparency)";
  28027. }
  28028. $c = $this->cmyk2rgb($c);
  28029. $c = array(3, $c[1], $c[2], $c[3]);
  28030. } elseif ($this->PDFX || ($this->PDFA && $this->restrictColorSpace == 3)) {
  28031. if (($this->PDFA && !$this->PDFAauto) || ($this->PDFX && !$this->PDFXauto)) {
  28032. $this->PDFAXwarnings[] = "CMYK color with transparency specified '" . $color . "' (converted to CMYK without transparency)";
  28033. }
  28034. $c = $this->cmyk2rgb($c);
  28035. $c = array(3, $c[1], $c[2], $c[3]);
  28036. } elseif ($this->restrictColorSpace == 1) {
  28037. $c = $this->cmyk2gray($c);
  28038. } elseif ($this->restrictColorSpace == 2) {
  28039. $c = $this->cmyk2rgb($c);
  28040. }
  28041. }
  28042. }
  28043. if (is_array($c)) {
  28044. $c = array_pad($c, 6, 0);
  28045. $cstr = pack("a1ccccc", $c[0], ($c[1] & 0xFF), ($c[2] & 0xFF), ($c[3] & 0xFF), ($c[4] & 0xFF), ($c[5] & 0xFF));
  28046. }
  28047. $cache[$color] = $cstr;
  28048. }
  28049. return $cache[$color];
  28050. }
  28051. function rgb2gray($c)
  28052. {
  28053. if (isset($c[4])) {
  28054. return array(1, (($c[1] * .21) + ($c[2] * .71) + ($c[3] * .07)), ord(1), $c[4]);
  28055. } else {
  28056. return array(1, (($c[1] * .21) + ($c[2] * .71) + ($c[3] * .07)));
  28057. }
  28058. }
  28059. function cmyk2gray($c)
  28060. {
  28061. $rgb = $this->cmyk2rgb($c);
  28062. return $this->rgb2gray($rgb);
  28063. }
  28064. function rgb2cmyk($c)
  28065. {
  28066. $cyan = 1 - ($c[1] / 255);
  28067. $magenta = 1 - ($c[2] / 255);
  28068. $yellow = 1 - ($c[3] / 255);
  28069. $min = min($cyan, $magenta, $yellow);
  28070. if ($min == 1) {
  28071. if ($c[0] == 5) {
  28072. return array(6, 100, 100, 100, 100, $c[4]);
  28073. } else {
  28074. return array(4, 100, 100, 100, 100);
  28075. }
  28076. // For K-Black
  28077. //if ($c[0]==5) { return array (6,0,0,0,100, $c[4]); }
  28078. //else { return array (4,0,0,0,100); }
  28079. }
  28080. $K = $min;
  28081. $black = 1 - $K;
  28082. if ($c[0] == 5) {
  28083. return array(6, ($cyan - $K) * 100 / $black, ($magenta - $K) * 100 / $black, ($yellow - $K) * 100 / $black, $K * 100, $c[4]);
  28084. } else {
  28085. return array(4, ($cyan - $K) * 100 / $black, ($magenta - $K) * 100 / $black, ($yellow - $K) * 100 / $black, $K * 100);
  28086. }
  28087. }
  28088. function cmyk2rgb($c)
  28089. {
  28090. $rgb = array();
  28091. $colors = 255 - ($c[4] * 2.55);
  28092. $rgb[0] = intval($colors * (255 - ($c[1] * 2.55)) / 255);
  28093. $rgb[1] = intval($colors * (255 - ($c[2] * 2.55)) / 255);
  28094. $rgb[2] = intval($colors * (255 - ($c[3] * 2.55)) / 255);
  28095. if ($c[0] == 6) {
  28096. return array(5, $rgb[0], $rgb[1], $rgb[2], $c[5]);
  28097. } else {
  28098. return array(3, $rgb[0], $rgb[1], $rgb[2]);
  28099. }
  28100. }
  28101. function rgb2hsl($var_r, $var_g, $var_b)
  28102. {
  28103. $var_min = min($var_r, $var_g, $var_b);
  28104. $var_max = max($var_r, $var_g, $var_b);
  28105. $del_max = $var_max - $var_min;
  28106. $l = ($var_max + $var_min) / 2;
  28107. if ($del_max == 0) {
  28108. $h = 0;
  28109. $s = 0;
  28110. } else {
  28111. if ($l < 0.5) {
  28112. $s = $del_max / ($var_max + $var_min);
  28113. } else {
  28114. $s = $del_max / (2 - $var_max - $var_min);
  28115. }
  28116. $del_r = ((($var_max - $var_r) / 6) + ($del_max / 2)) / $del_max;
  28117. $del_g = ((($var_max - $var_g) / 6) + ($del_max / 2)) / $del_max;
  28118. $del_b = ((($var_max - $var_b) / 6) + ($del_max / 2)) / $del_max;
  28119. if ($var_r == $var_max) {
  28120. $h = $del_b - $del_g;
  28121. } elseif ($var_g == $var_max) {
  28122. $h = (1 / 3) + $del_r - $del_b;
  28123. } elseif ($var_b == $var_max) {
  28124. $h = (2 / 3) + $del_g - $del_r;
  28125. };
  28126. if ($h < 0) {
  28127. $h += 1;
  28128. }
  28129. if ($h > 1) {
  28130. $h -= 1;
  28131. }
  28132. }
  28133. return array($h, $s, $l);
  28134. }
  28135. function hsl2rgb($h2, $s2, $l2)
  28136. {
  28137. // Input is HSL value of complementary colour, held in $h2, $s, $l as fractions of 1
  28138. // Output is RGB in normal 255 255 255 format, held in $r, $g, $b
  28139. // Hue is converted using function hue_2_rgb, shown at the end of this code
  28140. if ($s2 == 0) {
  28141. $r = $l2 * 255;
  28142. $g = $l2 * 255;
  28143. $b = $l2 * 255;
  28144. } else {
  28145. if ($l2 < 0.5) {
  28146. $var_2 = $l2 * (1 + $s2);
  28147. } else {
  28148. $var_2 = ($l2 + $s2) - ($s2 * $l2);
  28149. }
  28150. $var_1 = 2 * $l2 - $var_2;
  28151. $r = round(255 * $this->hue_2_rgb($var_1, $var_2, $h2 + (1 / 3)));
  28152. $g = round(255 * $this->hue_2_rgb($var_1, $var_2, $h2));
  28153. $b = round(255 * $this->hue_2_rgb($var_1, $var_2, $h2 - (1 / 3)));
  28154. }
  28155. return array($r, $g, $b);
  28156. }
  28157. function hue_2_rgb($v1, $v2, $vh)
  28158. {
  28159. // Function to convert hue to RGB, called from above
  28160. if ($vh < 0) {
  28161. $vh += 1;
  28162. };
  28163. if ($vh > 1) {
  28164. $vh -= 1;
  28165. };
  28166. if ((6 * $vh) < 1) {
  28167. return ($v1 + ($v2 - $v1) * 6 * $vh);
  28168. };
  28169. if ((2 * $vh) < 1) {
  28170. return ($v2);
  28171. };
  28172. if ((3 * $vh) < 2) {
  28173. return ($v1 + ($v2 - $v1) * ((2 / 3 - $vh) * 6));
  28174. };
  28175. return ($v1);
  28176. }
  28177. function _invertColor($cor)
  28178. {
  28179. if ($cor[0] == 3 || $cor[0] == 5) { // RGB
  28180. return array(3, (255 - $cor[1]), (255 - $cor[2]), (255 - $cor[3]));
  28181. } elseif ($cor[0] == 4 || $cor[0] == 6) { // CMYK
  28182. return array(4, (100 - $cor[1]), (100 - $cor[2]), (100 - $cor[3]), (100 - $cor[4]));
  28183. } elseif ($cor[0] == 1) { // Grayscale
  28184. return array(1, (255 - $cor[1]));
  28185. }
  28186. // Cannot cope with non-RGB colors at present
  28187. throw new MpdfException('Error in _invertColor - trying to invert non-RGB color');
  28188. }
  28189. function _colAtoString($cor)
  28190. {
  28191. $s = '';
  28192. if ($cor{0} == 1)
  28193. $s = 'rgb(' . ord($cor{1}) . ',' . ord($cor{1}) . ',' . ord($cor{1}) . ')';
  28194. elseif ($cor{0} == 2)
  28195. $s = 'spot(' . ord($cor{1}) . ',' . ord($cor{2}) . ')'; // SPOT COLOR
  28196. elseif ($cor{0} == 3)
  28197. $s = 'rgb(' . ord($cor{1}) . ',' . ord($cor{2}) . ',' . ord($cor{3}) . ')';
  28198. elseif ($cor{0} == 4)
  28199. $s = 'cmyk(' . ord($cor{1}) . ',' . ord($cor{2}) . ',' . ord($cor{3}) . ',' . ord($cor{4}) . ')';
  28200. elseif ($cor{0} == 5)
  28201. $s = 'rgba(' . ord($cor{1}) . ',' . ord($cor{2}) . ',' . ord($cor{3}) . ',' . sprintf('%0.2F', ord($cor{4}) / 100) . ')';
  28202. elseif ($cor{0} == 6)
  28203. $s = 'cmyka(' . ord($cor{1}) . ',' . ord($cor{2}) . ',' . ord($cor{3}) . ',' . ord($cor{4}) . ',' . sprintf('%0.2F', ord($cor{5}) / 100) . ')';
  28204. return $s;
  28205. }
  28206. function ConvertSize($size = 5, $maxsize = 0, $fontsize = false, $usefontsize = true)
  28207. {
  28208. // usefontsize - set false for e.g. margins - will ignore fontsize for % values
  28209. // Depends of maxsize value to make % work properly. Usually maxsize == pagewidth
  28210. // For text $maxsize = Fontsize
  28211. // Setting e.g. margin % will use maxsize (pagewidth) and em will use fontsize
  28212. // Returns values using 'mm' units
  28213. $size = trim(strtolower($size));
  28214. if ($size == 'thin')
  28215. $size = 1 * (25.4 / $this->dpi); //1 pixel width for table borders
  28216. elseif (stristr($size, 'px'))
  28217. $size *= (25.4 / $this->dpi); //pixels
  28218. elseif (stristr($size, 'cm'))
  28219. $size *= 10; //centimeters
  28220. elseif (stristr($size, 'mm'))
  28221. $size += 0; //millimeters
  28222. elseif (stristr($size, 'pt'))
  28223. $size *= 25.4 / 72; //72 pts/inch
  28224. elseif (stristr($size, 'rem')) {
  28225. $size += 0; //make "0.83rem" become simply "0.83"
  28226. $size *= ($this->default_font_size / _MPDFK);
  28227. } elseif (stristr($size, 'em')) {
  28228. $size += 0; //make "0.83em" become simply "0.83"
  28229. if ($fontsize) {
  28230. $size *= $fontsize;
  28231. } else {
  28232. $size *= $maxsize;
  28233. }
  28234. } elseif (stristr($size, '%')) {
  28235. $size += 0; //make "90%" become simply "90"
  28236. if ($fontsize && $usefontsize) {
  28237. $size *= $fontsize / 100;
  28238. } else {
  28239. $size *= $maxsize / 100;
  28240. }
  28241. } elseif (stristr($size, 'in'))
  28242. $size *= 25.4; //inches
  28243. elseif (stristr($size, 'pc'))
  28244. $size *= 38.1 / 9; //PostScript picas
  28245. elseif (stristr($size, 'ex')) { // Approximates "ex" as half of font height
  28246. $size += 0; //make "3.5ex" become simply "3.5"
  28247. if ($fontsize) {
  28248. $size *= $fontsize / 2;
  28249. } else {
  28250. $size *= $maxsize / 2;
  28251. }
  28252. } elseif ($size == 'medium')
  28253. $size = 3 * (25.4 / $this->dpi); //3 pixel width for table borders
  28254. elseif ($size == 'thick')
  28255. $size = 5 * (25.4 / $this->dpi); //5 pixel width for table borders
  28256. elseif ($size == 'xx-small') {
  28257. if ($fontsize) {
  28258. $size *= $fontsize * 0.7;
  28259. } else {
  28260. $size *= $maxsize * 0.7;
  28261. }
  28262. } elseif ($size == 'x-small') {
  28263. if ($fontsize) {
  28264. $size *= $fontsize * 0.77;
  28265. } else {
  28266. $size *= $maxsize * 0.77;
  28267. }
  28268. } elseif ($size == 'small') {
  28269. if ($fontsize) {
  28270. $size *= $fontsize * 0.86;
  28271. } else {
  28272. $size *= $maxsize * 0.86;
  28273. }
  28274. } elseif ($size == 'medium') {
  28275. if ($fontsize) {
  28276. $size *= $fontsize;
  28277. } else {
  28278. $size *= $maxsize;
  28279. }
  28280. } elseif ($size == 'large') {
  28281. if ($fontsize) {
  28282. $size *= $fontsize * 1.2;
  28283. } else {
  28284. $size *= $maxsize * 1.2;
  28285. }
  28286. } elseif ($size == 'x-large') {
  28287. if ($fontsize) {
  28288. $size *= $fontsize * 1.5;
  28289. } else {
  28290. $size *= $maxsize * 1.5;
  28291. }
  28292. } elseif ($size == 'xx-large') {
  28293. if ($fontsize) {
  28294. $size *= $fontsize * 2;
  28295. } else {
  28296. $size *= $maxsize * 2;
  28297. }
  28298. } else
  28299. $size *= (25.4 / $this->dpi); //nothing == px
  28300. return $size;
  28301. }
  28302. // mPDF 5.7.3 TRANSFORMS
  28303. function ConvertAngle($s, $makepositive = true)
  28304. {
  28305. if (preg_match('/([\-]*[0-9\.]+)(deg|grad|rad)/i', $s, $m)) {
  28306. $angle = $m[1] + 0;
  28307. if (strtolower($m[2]) == 'deg') {
  28308. $angle = $angle;
  28309. } elseif (strtolower($m[2]) == 'grad') {
  28310. $angle *= (360 / 400);
  28311. } elseif (strtolower($m[2]) == 'rad') {
  28312. $angle = rad2deg($angle);
  28313. }
  28314. while ($angle >= 360) {
  28315. $angle -= 360;
  28316. }
  28317. while ($angle <= -360) {
  28318. $angle += 360;
  28319. }
  28320. if ($makepositive) { // always returns an angle between 0 and 360deg
  28321. if ($angle < 0) {
  28322. $angle += 360;
  28323. }
  28324. }
  28325. } else {
  28326. $angle = $s + 0;
  28327. }
  28328. return $angle;
  28329. }
  28330. function lesser_entity_decode($html)
  28331. {
  28332. //supports the most used entity codes (only does ascii safe characters)
  28333. $html = str_replace("&lt;", "<", $html);
  28334. $html = str_replace("&gt;", ">", $html);
  28335. $html = str_replace("&apos;", "'", $html);
  28336. $html = str_replace("&quot;", '"', $html);
  28337. $html = str_replace("&amp;", "&", $html);
  28338. return $html;
  28339. }
  28340. function AdjustHTML($html, $tabSpaces = 8)
  28341. {
  28342. //Try to make the html text more manageable (turning it into XHTML)
  28343. if (PHP_VERSION_ID < 50307) {
  28344. if (strlen($html) > 100000) {
  28345. if (PHP_VERSION_ID < 50200)
  28346. throw new MpdfException("The HTML code is more than 100,000 characters. You should use WriteHTML() with smaller string lengths.");
  28347. else
  28348. ini_set("pcre.backtrack_limit", "1000000");
  28349. }
  28350. }
  28351. /* -- ANNOTATIONS -- */
  28352. preg_match_all("/(<annotation.*?>)/si", $html, $m);
  28353. if (count($m[1])) {
  28354. for ($i = 0; $i < count($m[1]); $i++) {
  28355. $sub = preg_replace("/\n/si", "\xbb\xa4\xac", $m[1][$i]);
  28356. $html = preg_replace('/' . preg_quote($m[1][$i], '/') . '/si', $sub, $html);
  28357. }
  28358. }
  28359. /* -- END ANNOTATIONS -- */
  28360. preg_match_all("/(<svg.*?<\/svg>)/si", $html, $svgi);
  28361. if (count($svgi[0])) {
  28362. for ($i = 0; $i < count($svgi[0]); $i++) {
  28363. $file = _MPDF_TEMP_PATH . '_tempSVG' . uniqid(rand(1, 100000), true) . '_' . $i . '.svg';
  28364. //Save to local file
  28365. file_put_contents($file, $svgi[0][$i]);
  28366. $html = str_replace($svgi[0][$i], '<img src="' . $file . '" />', $html);
  28367. }
  28368. }
  28369. //Remove javascript code from HTML (should not appear in the PDF file)
  28370. $html = preg_replace('/<script.*?<\/script>/is', '', $html);
  28371. //Remove special comments
  28372. $html = preg_replace('/<!--mpdf/i', '', $html);
  28373. $html = preg_replace('/mpdf-->/i', '', $html);
  28374. //Remove comments from HTML (should not appear in the PDF file)
  28375. $html = preg_replace('/<!--.*?-->/s', '', $html);
  28376. $html = preg_replace('/\f/', '', $html); //replace formfeed by nothing
  28377. $html = preg_replace('/\r/', '', $html); //replace carriage return by nothing
  28378. // Well formed XHTML end tags
  28379. $html = preg_replace('/<(br|hr)>/i', "<\\1 />", $html); // mPDF 6
  28380. $html = preg_replace('/<(br|hr)\/>/i', "<\\1 />", $html);
  28381. // Get rid of empty <thead></thead> etc
  28382. $html = preg_replace('/<tr>\s*<\/tr>/i', '', $html);
  28383. $html = preg_replace('/<thead>\s*<\/thead>/i', '', $html);
  28384. $html = preg_replace('/<tfoot>\s*<\/tfoot>/i', '', $html);
  28385. $html = preg_replace('/<table[^>]*>\s*<\/table>/i', '', $html);
  28386. // Remove spaces at end of table cells
  28387. $html = preg_replace("/[ \n\r]+<\/t(d|h)/", '</t\\1', $html);
  28388. $html = preg_replace("/[ ]*<dottab\s*[\/]*>[ ]*/", '<dottab />', $html);
  28389. // Concatenates any Substitute characters from symbols/dingbats
  28390. $html = str_replace('</tts><tts>', '|', $html);
  28391. $html = str_replace('</ttz><ttz>', '|', $html);
  28392. $html = str_replace('</tta><tta>', '|', $html);
  28393. $html = preg_replace('/<br \/>\s*/is', "<br />", $html);
  28394. $html = preg_replace('/<wbr[ \/]*>\s*/is', "&#173;", $html);
  28395. // Preserve '\n's in content between the tags <pre> and </pre>
  28396. if (preg_match('/<pre/', $html)) {
  28397. $html_a = preg_split('/(\<\/?pre[^\>]*\>)/', $html, -1, 2);
  28398. $h = array();
  28399. $c = 0;
  28400. foreach ($html_a AS $s) {
  28401. if ($c > 1 && preg_match('/^<\/pre/i', $s)) {
  28402. $c--;
  28403. $s = preg_replace('/<\/pre/i', '</innerpre', $s);
  28404. } elseif ($c > 0 && preg_match('/^<pre/i', $s)) {
  28405. $c++;
  28406. $s = preg_replace('/<pre/i', '<innerpre', $s);
  28407. } elseif (preg_match('/^<pre/i', $s)) {
  28408. $c++;
  28409. } elseif (preg_match('/^<\/pre/i', $s)) {
  28410. $c--;
  28411. }
  28412. array_push($h, $s);
  28413. }
  28414. $html = implode("", $h);
  28415. }
  28416. $thereispre = preg_match_all('#<pre(.*?)>(.*?)</pre>#si', $html, $temp);
  28417. // Preserve '\n's in content between the tags <textarea> and </textarea>
  28418. $thereistextarea = preg_match_all('#<textarea(.*?)>(.*?)</textarea>#si', $html, $temp2);
  28419. $html = preg_replace('/[\n]/', ' ', $html); //replace linefeed by spaces
  28420. $html = preg_replace('/[\t]/', ' ', $html); //replace tabs by spaces
  28421. // Converts < to &lt; when not a tag
  28422. $html = preg_replace('/<([^!\/a-zA-Z_:])/i', '&lt;\\1', $html); // mPDF 5.7.3
  28423. $html = preg_replace("/[ ]+/", ' ', $html);
  28424. $html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
  28425. $html = preg_replace('/\/(u|o)l>\s+<\/li/i', '/\\1l></li', $html);
  28426. $html = preg_replace('/\/li>\s+<\/(u|o)l/i', '/li></\\1l', $html);
  28427. $html = preg_replace('/\/li>\s+<li/i', '/li><li', $html);
  28428. $html = preg_replace('/<(u|o)l([^>]*)>[ ]+/i', '<\\1l\\2>', $html);
  28429. $html = preg_replace('/[ ]+<(u|o)l/i', '<\\1l', $html);
  28430. // Make self closing tabs valid XHTML
  28431. // Tags which are self-closing: 1) Replaceable and 2) Non-replaced items
  28432. $selftabs = 'input|hr|img|br|jpgraph|barcode|dottab';
  28433. $selftabs2 = 'indexentry|indexinsert|bookmark|watermarktext|watermarkimage|column_break|columnbreak|newcolumn|newpage|page_break|pagebreak|formfeed|columns|toc|tocpagebreak|setpageheader|setpagefooter|sethtmlpageheader|sethtmlpagefooter|annotation';
  28434. $html = preg_replace('/(<(' . $selftabs . '|' . $selftabs2 . ')[^>\/]*)>/i', '\\1 />', $html);
  28435. $iterator = 0;
  28436. while ($thereispre) { //Recover <pre attributes>content</pre>
  28437. $temp[2][$iterator] = preg_replace('/<([^!\/a-zA-Z_:])/', '&lt;\\1', $temp[2][$iterator]); // mPDF 5.7.2 // mPDF 5.7.3
  28438. $temp[2][$iterator] = preg_replace_callback("/^([^\n\t]*?)\t/m", array($this, 'tabs2spaces_callback'), $temp[2][$iterator]); // mPDF 5.7+
  28439. $temp[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp[2][$iterator]);
  28440. $temp[2][$iterator] = preg_replace('/\n/', "<br />", $temp[2][$iterator]);
  28441. $temp[2][$iterator] = str_replace('\\', "\\\\", $temp[2][$iterator]);
  28442. //$html = preg_replace('#<pre(.*?)>(.*?)</pre>#si','<erp'.$temp[1][$iterator].'>'.$temp[2][$iterator].'</erp>',$html,1);
  28443. $html = preg_replace('#<pre(.*?)>(.*?)</pre>#si', '<erp' . $temp[1][$iterator] . '>' . str_replace('$', '\$', $temp[2][$iterator]) . '</erp>', $html, 1); // mPDF 5.7+
  28444. $thereispre--;
  28445. $iterator++;
  28446. }
  28447. $iterator = 0;
  28448. while ($thereistextarea) { //Recover <textarea attributes>content</textarea>
  28449. $temp2[2][$iterator] = preg_replace('/\t/', str_repeat(" ", $tabSpaces), $temp2[2][$iterator]);
  28450. $temp2[2][$iterator] = str_replace('\\', "\\\\", $temp2[2][$iterator]);
  28451. $html = preg_replace('#<textarea(.*?)>(.*?)</textarea>#si', '<aeratxet' . $temp2[1][$iterator] . '>' . trim($temp2[2][$iterator]) . '</aeratxet>', $html, 1);
  28452. $thereistextarea--;
  28453. $iterator++;
  28454. }
  28455. //Restore original tag names
  28456. $html = str_replace("<erp", "<pre", $html);
  28457. $html = str_replace("</erp>", "</pre>", $html);
  28458. $html = str_replace("<aeratxet", "<textarea", $html);
  28459. $html = str_replace("</aeratxet>", "</textarea>", $html);
  28460. $html = str_replace("</innerpre", "</pre", $html);
  28461. $html = str_replace("<innerpre", "<pre", $html);
  28462. $html = preg_replace('/<textarea([^>]*)><\/textarea>/si', '<textarea\\1> </textarea>', $html);
  28463. $html = preg_replace('/(<table[^>]*>)\s*(<caption)(.*?<\/caption>)(.*?<\/table>)/si', '\\2 position="top"\\3\\1\\4\\2 position="bottom"\\3', $html); // *TABLES*
  28464. $html = preg_replace('/<(h[1-6])([^>]*)(>(?:(?!h[1-6]).)*?<\/\\1>\s*<table)/si', '<\\1\\2 keep-with-table="1"\\3', $html); // *TABLES*
  28465. $html = preg_replace("/\xbb\xa4\xac/", "\n", $html);
  28466. // Fixes <p>&#8377</p> which browser copes with even though it is wrong!
  28467. $html = preg_replace("/(&#[x]{0,1}[0-9a-f]{1,5})</i", "\\1;<", $html);
  28468. return $html;
  28469. }
  28470. // mPDF 5.7+
  28471. function tabs2spaces_callback($matches)
  28472. {
  28473. return (stripslashes($matches[1]) . str_repeat(' ', $this->tabSpaces - (mb_strlen(stripslashes($matches[1])) % $this->tabSpaces)));
  28474. }
  28475. // mPDF 5.7+
  28476. function date_callback($matches)
  28477. {
  28478. return date($matches[1]);
  28479. }
  28480. function dec2other($num, $cp, $check = true)
  28481. {
  28482. // From printlistbuffer: font is set, so check if character is available
  28483. // From docPageNum: font is not set, so no check
  28484. $nstr = (string) $num;
  28485. $rnum = '';
  28486. for ($i = 0; $i < strlen($nstr); $i++) {
  28487. if (!$check || $this->_charDefined($this->CurrentFont['cw'], $cp + intval($nstr[$i]))) {
  28488. $rnum .= code2utf($cp + intval($nstr[$i]));
  28489. } else {
  28490. $rnum .= $nstr[$i];
  28491. }
  28492. }
  28493. return $rnum;
  28494. }
  28495. function dec2cjk($num)
  28496. {
  28497. $nstr = (string) $num;
  28498. $rnum = '';
  28499. $glyphs = array(0x3007, 0x4E00, 0x4E8C, 0x4E09, 0x56DB, 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D);
  28500. for ($i = 0; $i < strlen($nstr); $i++) {
  28501. $rnum .= code2utf($glyphs[intval($nstr[$i])]);
  28502. }
  28503. return $rnum;
  28504. }
  28505. function dec2alpha($valor, $toupper = "true")
  28506. {
  28507. // returns a string from A-Z to AA-ZZ to AAA-ZZZ
  28508. // OBS: A = 65 ASCII TABLE VALUE
  28509. if (($valor < 1) || ($valor > 18278))
  28510. return "?"; //supports 'only' up to 18278
  28511. $c1 = $c2 = $c3 = '';
  28512. if ($valor > 702) { // 3 letters (up to 18278)
  28513. $c1 = 65 + floor(($valor - 703) / 676);
  28514. $c2 = 65 + floor((($valor - 703) % 676) / 26);
  28515. $c3 = 65 + floor((($valor - 703) % 676) % 26);
  28516. } elseif ($valor > 26) { // 2 letters (up to 702)
  28517. $c1 = (64 + (int) (($valor - 1) / 26));
  28518. $c2 = (64 + (int) ($valor % 26));
  28519. if ($c2 == 64)
  28520. $c2 += 26;
  28521. }
  28522. else { // 1 letter (up to 26)
  28523. $c1 = (64 + $valor);
  28524. }
  28525. $alpha = chr($c1);
  28526. if ($c2 != '')
  28527. $alpha .= chr($c2);
  28528. if ($c3 != '')
  28529. $alpha .= chr($c3);
  28530. if (!$toupper)
  28531. $alpha = strtolower($alpha);
  28532. return $alpha;
  28533. }
  28534. // mPDF 6
  28535. function dec2hebrew($in, $reverse = false)
  28536. {
  28537. // reverse is used when called from Lists, as these do not pass through bidi-algorithm
  28538. $i = intval($in); // I initially be the counter value
  28539. $s = ''; // S initially be the empty string
  28540. //and glyph list initially be the list of additive tuples.
  28541. $additive_nums = array(400, 300, 200, 100, 90, 80, 70, 60, 50, 40, 30, 20, 19, 18, 17, 16, 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
  28542. $additive_glyphs = array(0x05EA, 0x05E9, 0x05E8, 0x05E7, 0x05E6, 0x05E4, 0x05E2, 0x05E1, 0x05E0, 0x05DE, 0x05DC, 0x05DB,
  28543. array(0x05D9, 0x05D8), array(0x05D9, 0x05D7), array(0x05D9, 0x05D6), array(0x05D8, 0x05D6), array(0x05D8, 0x05D5), 0x05D9,
  28544. 0x05D8, 0x05D7, 0x05D6, 0x05D5, 0x05D4, 0x05D3, 0x05D2, 0x05D1, 0x05D0);
  28545. /* NB This system manually specifies the values for 19-15 to force the correct display of 15 and 16, which are commonly
  28546. rewritten to avoid a close resemblance to the Tetragrammaton. */
  28547. // This function only works up to 1,000
  28548. if ($i > 999) {
  28549. return $in;
  28550. } // return as initial numeric string
  28551. // If I is initially 0, and there is an additive tuple with a weight of 0, append that tuple's counter glyph to S and return S.
  28552. if ($i == 0) {
  28553. return '0';
  28554. }
  28555. // Otherwise, while I is greater than 0 and there are elements left in the glyph list:
  28556. for ($t = 0; $t < count($additive_nums); $t++) {
  28557. // Pop the first additive tuple from the glyph list. This is the current tuple.
  28558. $ct = $additive_nums[$t];
  28559. // Append the current tuple's counter glyph to S x floor( I / current tuple's weight ) times (this may be 0).
  28560. $n = floor($i / $ct);
  28561. for ($j = 0; $j < $n; $j++) {
  28562. if (is_array($additive_glyphs[$t])) {
  28563. foreach ($additive_glyphs[$t] AS $ag) {
  28564. if ($reverse) {
  28565. $s = code2utf($ag) . $s;
  28566. } else {
  28567. $s .= code2utf($ag);
  28568. }
  28569. }
  28570. } else {
  28571. if ($reverse) {
  28572. $s = code2utf($additive_glyphs[$t]) . $s;
  28573. } else {
  28574. $s .= code2utf($additive_glyphs[$t]);
  28575. }
  28576. }
  28577. $i -= ($ct * $n);
  28578. }
  28579. if ($i == 0) {
  28580. return $s;
  28581. }
  28582. }
  28583. return $in; // return as initial string
  28584. }
  28585. function dec2roman($valor, $toupper = true)
  28586. {
  28587. //returns a string as a roman numeral
  28588. $r1 = $r2 = $r3 = $r4 = '';
  28589. if (($valor >= 5000) || ($valor < 1))
  28590. return "?"; //supports 'only' up to 4999
  28591. $aux = (int) ($valor / 1000);
  28592. if ($aux !== 0) {
  28593. $valor %= 1000;
  28594. while ($aux !== 0) {
  28595. $r1 .= "M";
  28596. $aux--;
  28597. }
  28598. }
  28599. $aux = (int) ($valor / 100);
  28600. if ($aux !== 0) {
  28601. $valor %= 100;
  28602. switch ($aux) {
  28603. case 3: $r2 = "C";
  28604. case 2: $r2.="C";
  28605. case 1: $r2.="C";
  28606. break;
  28607. case 9: $r2 = "CM";
  28608. break;
  28609. case 8: $r2 = "C";
  28610. case 7: $r2.="C";
  28611. case 6: $r2.="C";
  28612. case 5: $r2 = "D" . $r2;
  28613. break;
  28614. case 4: $r2 = "CD";
  28615. break;
  28616. default: break;
  28617. }
  28618. }
  28619. $aux = (int) ($valor / 10);
  28620. if ($aux !== 0) {
  28621. $valor %= 10;
  28622. switch ($aux) {
  28623. case 3: $r3 = "X";
  28624. case 2: $r3.="X";
  28625. case 1: $r3.="X";
  28626. break;
  28627. case 9: $r3 = "XC";
  28628. break;
  28629. case 8: $r3 = "X";
  28630. case 7: $r3.="X";
  28631. case 6: $r3.="X";
  28632. case 5: $r3 = "L" . $r3;
  28633. break;
  28634. case 4: $r3 = "XL";
  28635. break;
  28636. default: break;
  28637. }
  28638. }
  28639. switch ($valor) {
  28640. case 3: $r4 = "I";
  28641. case 2: $r4.="I";
  28642. case 1: $r4.="I";
  28643. break;
  28644. case 9: $r4 = "IX";
  28645. break;
  28646. case 8: $r4 = "I";
  28647. case 7: $r4.="I";
  28648. case 6: $r4.="I";
  28649. case 5: $r4 = "V" . $r4;
  28650. break;
  28651. case 4: $r4 = "IV";
  28652. break;
  28653. default: break;
  28654. }
  28655. $roman = $r1 . $r2 . $r3 . $r4;
  28656. if (!$toupper)
  28657. $roman = strtolower($roman);
  28658. return $roman;
  28659. }
  28660. //===========================
  28661. /* -- IMPORTS -- */
  28662. function SetImportUse()
  28663. {
  28664. if (!class_exists('fpdi_pdf_parser')) {
  28665. throw new MpdfException('Class fpdi_pdf_parser not found. Please run composer update or require setasign/fpdi 1.6.* manually');
  28666. }
  28667. $this->enableImports = true;
  28668. }
  28669. // from mPDFI
  28670. function hex2str($hex)
  28671. {
  28672. return pack("H*", str_replace(array("\r", "\n", " "), "", $hex));
  28673. }
  28674. function str2hex($str)
  28675. {
  28676. return current(unpack("H*", $str));
  28677. }
  28678. function pdf_write_value(&$value)
  28679. {
  28680. switch ($value[0]) {
  28681. case pdf_parser::TYPE_TOKEN:
  28682. $this->_out($value[1] . ' ', false);
  28683. break;
  28684. case pdf_parser::TYPE_NUMERIC:
  28685. case pdf_parser::TYPE_REAL:
  28686. if (is_float($value[1]) && $value[1] != 0) {
  28687. $this->_out(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ', false);
  28688. } else {
  28689. $this->_out($value[1] . ' ', false);
  28690. }
  28691. break;
  28692. case pdf_parser::TYPE_ARRAY :
  28693. // An array. Output the proper
  28694. // structure and move on.
  28695. $this->_out("[", false);
  28696. for ($i = 0; $i < count($value[1]); $i++) {
  28697. $this->pdf_write_value($value[1][$i]);
  28698. }
  28699. $this->_out("]");
  28700. break;
  28701. case pdf_parser::TYPE_DICTIONARY :
  28702. // A dictionary.
  28703. $this->_out("<<", false);
  28704. reset($value[1]);
  28705. while (list($k, $v) = each($value[1])) {
  28706. $this->_out($k . ' ',false);
  28707. $this->pdf_write_value($v);
  28708. }
  28709. $this->_out(">>");
  28710. break;
  28711. case pdf_parser::TYPE_OBJREF :
  28712. // An indirect object reference
  28713. // Fill the object stack if needed
  28714. $cpfn = $this->current_parser->filename;
  28715. if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
  28716. $this->_newobj(false, true);
  28717. $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
  28718. $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
  28719. }
  28720. $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
  28721. $this->_out("{$objid} 0 R"); //{$value[2]}
  28722. break;
  28723. case pdf_parser::TYPE_STRING :
  28724. if ($this->encrypted) {
  28725. $value[1] = $this->_RC4($this->_objectkey($this->_current_obj_id), $value[1]);
  28726. $value[1] = $this->_escape($value[1]);
  28727. }
  28728. // A string.
  28729. $this->_out('(' . $value[1] . ')');
  28730. break;
  28731. case pdf_parser::TYPE_STREAM :
  28732. // A stream. First, output the
  28733. // stream dictionary, then the
  28734. // stream data itself.
  28735. $this->pdf_write_value($value[1]);
  28736. if ($this->encrypted) {
  28737. $value[2][1] = $this->_RC4($this->_objectkey($this->_current_obj_id), $value[2][1]);
  28738. }
  28739. $this->_out("stream");
  28740. $this->_out($value[2][1]);
  28741. $this->_out("endstream");
  28742. break;
  28743. case pdf_parser::TYPE_HEX :
  28744. if ($this->encrypted) {
  28745. $value[1] = $this->hex2str($value[1]);
  28746. $value[1] = $this->_RC4($this->_objectkey($this->_current_obj_id), $value[1]);
  28747. // remake hexstring of encrypted string
  28748. $value[1] = $this->str2hex($value[1]);
  28749. }
  28750. $this->_out("<" . $value[1] . ">");
  28751. break;
  28752. case pdf_parser::TYPE_BOOLEAN :
  28753. $this->_out($value[1] ? 'true' : 'false');
  28754. break;
  28755. case pdf_parser::TYPE_NULL :
  28756. // The null object.
  28757. $this->_out("null");
  28758. break;
  28759. }
  28760. }
  28761. // ========== OVERWRITE SEARCH STRING IN A PDF FILE ================
  28762. function OverWrite($file_in, $search, $replacement, $dest = "D", $file_out = "mpdf")
  28763. {
  28764. $pdf = file_get_contents($file_in);
  28765. if (!is_array($search)) {
  28766. $x = $search;
  28767. $search = array($x);
  28768. }
  28769. if (!is_array($replacement)) {
  28770. $x = $replacement;
  28771. $replacement = array($x); // mPDF 5.7.4
  28772. }
  28773. if (!$this->onlyCoreFonts && !$this->usingCoreFont) {
  28774. foreach ($search AS $k => $val) {
  28775. $search[$k] = $this->UTF8ToUTF16BE($search[$k], false);
  28776. $search[$k] = $this->_escape($search[$k]);
  28777. $replacement[$k] = $this->UTF8ToUTF16BE($replacement[$k], false);
  28778. $replacement[$k] = $this->_escape($replacement[$k]);
  28779. }
  28780. } else {
  28781. foreach ($replacement AS $k => $val) {
  28782. $replacement[$k] = mb_convert_encoding($replacement[$k], $this->mb_enc, 'utf-8');
  28783. $replacement[$k] = $this->_escape($replacement[$k]);
  28784. }
  28785. }
  28786. // Get xref into array
  28787. $xref = array();
  28788. preg_match("/xref\n0 (\d+)\n(.*?)\ntrailer/s", $pdf, $m);
  28789. $xref_objid = $m[1];
  28790. preg_match_all('/(\d{10}) (\d{5}) (f|n)/', $m[2], $x);
  28791. for ($i = 0; $i < count($x[0]); $i++) {
  28792. $xref[] = array(intval($x[1][$i]), $x[2][$i], $x[3][$i]);
  28793. }
  28794. $changes = array();
  28795. preg_match("/<<\s*\/Type\s*\/Pages\s*\/Kids\s*\[(.*?)\]\s*\/Count/s", $pdf, $m);
  28796. preg_match_all("/(\d+) 0 R /s", $m[1], $o);
  28797. $objlist = $o[1];
  28798. foreach ($objlist AS $obj) {
  28799. if ($this->compress) {
  28800. preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Filter\s*\/FlateDecode\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
  28801. } else {
  28802. preg_match("/" . ($obj + 1) . " 0 obj\n<<\s*\/Length (\d+)>>\nstream\n(.*?)\nendstream\n/s", $pdf, $m);
  28803. }
  28804. $s = $m[2];
  28805. if (!$s) {
  28806. continue;
  28807. }
  28808. $oldlen = $m[1];
  28809. if ($this->encrypted) {
  28810. $s = $this->_RC4($this->_objectkey($obj + 1), $s);
  28811. }
  28812. if ($this->compress) {
  28813. $s = gzuncompress($s);
  28814. }
  28815. foreach ($search AS $k => $val) {
  28816. $s = str_replace($search[$k], $replacement[$k], $s);
  28817. }
  28818. if ($this->compress) {
  28819. $s = gzcompress($s);
  28820. }
  28821. if ($this->encrypted) {
  28822. $s = $this->_RC4($this->_objectkey($obj + 1), $s);
  28823. }
  28824. $newlen = strlen($s);
  28825. $changes[($xref[$obj + 1][0])] = ($newlen - $oldlen) + (strlen($newlen) - strlen($oldlen));
  28826. if ($this->compress) {
  28827. $newstr = ($obj + 1) . " 0 obj\n<</Filter /FlateDecode /Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
  28828. } else {
  28829. $newstr = ($obj + 1) . " 0 obj\n<</Length " . $newlen . ">>\nstream\n" . $s . "\nendstream\n";
  28830. }
  28831. $pdf = str_replace($m[0], $newstr, $pdf);
  28832. }
  28833. // Update xref in PDF
  28834. krsort($changes);
  28835. $newxref = "xref\n0 " . $xref_objid . "\n";
  28836. foreach ($xref AS $v) {
  28837. foreach ($changes AS $ck => $cv) {
  28838. if ($v[0] > $ck) {
  28839. $v[0] += $cv;
  28840. }
  28841. }
  28842. $newxref .= sprintf('%010d', $v[0]) . ' ' . $v[1] . ' ' . $v[2] . " \n";
  28843. }
  28844. $newxref .= "trailer";
  28845. $pdf = preg_replace("/xref\n0 \d+\n.*?\ntrailer/s", $newxref, $pdf);
  28846. // Update startxref in PDF
  28847. preg_match("/startxref\n(\d+)\n%%EOF/s", $pdf, $m);
  28848. $startxref = $m[1];
  28849. $startxref += array_sum($changes);
  28850. $pdf = preg_replace("/startxref\n(\d+)\n%%EOF/s", "startxref\n" . $startxref . "\n%%EOF", $pdf);
  28851. // OUTPUT
  28852. switch ($dest) {
  28853. case 'I':
  28854. //Send to standard output
  28855. if (isset($_SERVER['SERVER_NAME'])) {
  28856. //We send to a browser
  28857. Header('Content-Type: application/pdf');
  28858. Header('Content-Length: ' . strlen($pdf));
  28859. Header('Content-disposition: inline; filename=' . $file_out);
  28860. }
  28861. echo $pdf;
  28862. break;
  28863. case 'F':
  28864. //Save to local file
  28865. if (!$file_out) {
  28866. $file_out = 'mpdf.pdf';
  28867. }
  28868. $f = fopen($file_out, 'wb');
  28869. if (!$f)
  28870. throw new MpdfException('Unable to create output file: ' . $file_out);
  28871. fwrite($f, $pdf, strlen($pdf));
  28872. fclose($f);
  28873. break;
  28874. case 'S':
  28875. //Return as a string
  28876. return $pdf;
  28877. case 'D':
  28878. default:
  28879. //Download file
  28880. if (isset($_SERVER['HTTP_USER_AGENT']) and strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE'))
  28881. Header('Content-Type: application/force-download');
  28882. else
  28883. Header('Content-Type: application/octet-stream');
  28884. Header('Content-Length: ' . strlen($pdf));
  28885. Header('Content-disposition: attachment; filename=' . $file_out);
  28886. echo $pdf;
  28887. break;
  28888. }
  28889. }
  28890. function GetTemplateSize($tplidx, $_w = 0, $_h = 0)
  28891. {
  28892. if (!$this->tpls[$tplidx])
  28893. return false;
  28894. $w = $this->tpls[$tplidx]['box']['w'];
  28895. $h = $this->tpls[$tplidx]['box']['h'];
  28896. if ($_w == 0 and $_h == 0) {
  28897. $_w = $w;
  28898. $_h = $h;
  28899. }
  28900. if ($_w == 0)
  28901. $_w = $_h * $w / $h;
  28902. if ($_h == 0)
  28903. $_h = $_w * $h / $w;
  28904. return array("w" => $_w, "h" => $_h);
  28905. }
  28906. function Thumbnail($file, $npr = 3, $spacing = 10)
  28907. {
  28908. //$npr = number per row
  28909. $w = (($this->pgwidth + $spacing) / $npr) - $spacing;
  28910. $oldlinewidth = $this->LineWidth;
  28911. $this->SetLineWidth(0.02);
  28912. $this->SetDColor($this->ConvertColor(0));
  28913. $h = 0;
  28914. $maxh = 0;
  28915. $x = $_x = $this->lMargin;
  28916. $_y = $this->tMargin;
  28917. if ($this->y == 0) {
  28918. $y = $_y;
  28919. } else {
  28920. $y = $this->y;
  28921. }
  28922. $pagecount = $this->SetSourceFile($file);
  28923. for ($n = 1; $n <= $pagecount; $n++) {
  28924. $tplidx = $this->ImportPage($n);
  28925. $size = $this->useTemplate($tplidx, $x, $y, $w);
  28926. $this->Rect($x, $y, $size['w'], $size['h']);
  28927. $h = max($h, $size['h']);
  28928. $maxh = max($h, $maxh);
  28929. if ($n % $npr == 0) {
  28930. if (($y + $h + $spacing + $maxh) > $this->PageBreakTrigger && $n != $pagecount) {
  28931. $this->AddPage();
  28932. $x = $_x;
  28933. $y = $_y;
  28934. } else {
  28935. $y += $h + $spacing;
  28936. $x = $_x;
  28937. $h = 0;
  28938. }
  28939. } else {
  28940. $x += $w + $spacing;
  28941. }
  28942. }
  28943. $this->SetLineWidth($oldlinewidth);
  28944. }
  28945. function SetSourceFile($filename)
  28946. {
  28947. $this->current_filename = $filename;
  28948. $fn = $this->current_filename;
  28949. if (!isset($this->parsers[$fn])) {
  28950. // $this->parsers[$fn] =& new fpdi_pdf_parser($fn,$this);
  28951. try {
  28952. $this->parsers[$fn] = new fpdi_pdf_parser($fn, $this);
  28953. } catch (Exception $e) {
  28954. throw new MpdfException($e->getMessage()); // Delete this line to return false on fail
  28955. return false;
  28956. }
  28957. }
  28958. $this->current_parser = $this->parsers[$fn];
  28959. return $this->parsers[$fn]->getPageCount();
  28960. }
  28961. function ImportPage($pageno = 1, $crop_x = null, $crop_y = null, $crop_w = 0, $crop_h = 0, $boxName = '/CropBox')
  28962. {
  28963. $fn = $this->current_filename;
  28964. $parser = $this->parsers[$fn];
  28965. $parser->setPageno($pageno);
  28966. $this->tpl++;
  28967. $this->tpls[$this->tpl] = array();
  28968. $tpl = & $this->tpls[$this->tpl];
  28969. $tpl['parser'] = $parser;
  28970. $tpl['resources'] = $parser->getPageResources();
  28971. $tpl['buffer'] = $parser->getContent();
  28972. if (!in_array($boxName, $parser->availableBoxes)) {
  28973. throw new MpdfException(sprintf("Unknown box: %s", $boxName));
  28974. }
  28975. $pageboxes = $parser->getPageBoxes($pageno, _MPDFK);
  28976. /**
  28977. * MediaBox
  28978. * CropBox: Default -> MediaBox
  28979. * BleedBox: Default -> CropBox
  28980. * TrimBox: Default -> CropBox
  28981. * ArtBox: Default -> CropBox
  28982. */
  28983. if (!isset($pageboxes[$boxName]) && ($boxName == "/BleedBox" || $boxName == "/TrimBox" || $boxName == "/ArtBox")) {
  28984. $boxName = "/CropBox";
  28985. }
  28986. if (!isset($pageboxes[$boxName]) && $boxName == "/CropBox") {
  28987. $boxName = "/MediaBox";
  28988. }
  28989. if (!isset($pageboxes[$boxName])) {
  28990. return false;
  28991. }
  28992. $box = $pageboxes[$boxName];
  28993. $tpl['box'] = $box;
  28994. // To build an array that can be used by useTemplate()
  28995. $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box);
  28996. // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
  28997. $tpl['x'] = 0;
  28998. $tpl['y'] = 0;
  28999. $tpl['w'] = $tpl['box']['w'];
  29000. $tpl['h'] = $tpl['box']['h'];
  29001. if ($crop_w) {
  29002. $tpl['box']['w'] = $crop_w;
  29003. }
  29004. if ($crop_h) {
  29005. $tpl['box']['h'] = $crop_h;
  29006. }
  29007. if (isset($crop_x)) {
  29008. $tpl['box']['x'] = $crop_x;
  29009. }
  29010. if (isset($crop_y)) {
  29011. $tpl['box']['y'] = $tpl['h'] - $crop_y - $crop_h;
  29012. }
  29013. // fix for rotated pages
  29014. $rotation = $parser->getPageRotation($pageno);
  29015. if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0 && $tpl['box']['w'] == $tpl['w']) {
  29016. $steps = $angle / 90;
  29017. $_w = $tpl['w'];
  29018. $_h = $tpl['h'];
  29019. $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
  29020. $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
  29021. if ($steps % 2 != 0) {
  29022. $x = $y = ($steps == 1 || $steps == -3) ? $tpl['h'] : $tpl['w'];
  29023. } else {
  29024. $x = $tpl['w'];
  29025. $y = $tpl['h'];
  29026. }
  29027. $cx = ($x / 2 + $tpl['box']['x']) * _MPDFK;
  29028. $cy = ($y / 2 + $tpl['box']['y']) * _MPDFK;
  29029. $angle*=-1;
  29030. $angle*=M_PI / 180;
  29031. $c = cos($angle);
  29032. $s = sin($angle);
  29033. $tpl['box']['w'] = $tpl['w'];
  29034. $tpl['box']['h'] = $tpl['h'];
  29035. $tpl['buffer'] = sprintf('q %.5F %.5F %.5F %.5F %.2F %.2F cm 1 0 0 1 %.2F %.2F cm %s Q', $c, $s, -$s, $c, $cx, $cy, -$cx, -$cy, $tpl['buffer']);
  29036. }
  29037. return $this->tpl;
  29038. }
  29039. function UseTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0)
  29040. {
  29041. if (!isset($this->tpls[$tplidx])) {
  29042. throw new MpdfException("Template does not exist!");
  29043. }
  29044. if ($this->state == 0) {
  29045. $this->AddPage();
  29046. }
  29047. $out = 'q 0 J 1 w 0 j 0 G' . "\n"; // reset standard values
  29048. $x = $this->tpls[$tplidx]['x'];
  29049. $y = $this->tpls[$tplidx]['y'];
  29050. $w = $this->tpls[$tplidx]['w'];
  29051. $h = $this->tpls[$tplidx]['h'];
  29052. if ($_x == null) {
  29053. $_x = $x;
  29054. }
  29055. if ($_y == null) {
  29056. $_y = $y;
  29057. }
  29058. if ($_x === -1) {
  29059. $_x = $this->x;
  29060. }
  29061. if ($_y === -1) {
  29062. $_y = $this->y;
  29063. }
  29064. $wh = $this->getTemplateSize($tplidx, $_w, $_h);
  29065. $_w = $wh['w'];
  29066. $_h = $wh['h'];
  29067. $out .= sprintf("q %.4F 0 0 %.4F %.2F %.2F cm", ($_w / $this->tpls[$tplidx]['box']['w']), ($_h / $this->tpls[$tplidx]['box']['h']), $_x * _MPDFK, ($this->h - ($_y + $_h)) * _MPDFK) . "\n";
  29068. $out .= $this->tplprefix . $tplidx . " Do Q\n";
  29069. $s = array("w" => $_w, "h" => $_h);
  29070. $out .= "Q\n";
  29071. $this->pages[$this->page] = $out . $this->pages[$this->page];
  29072. return $s;
  29073. }
  29074. function SetPageTemplate($tplidx = '')
  29075. {
  29076. if (!isset($this->tpls[$tplidx])) {
  29077. $this->pageTemplate = '';
  29078. return false;
  29079. }
  29080. $this->pageTemplate = $tplidx;
  29081. }
  29082. function SetDocTemplate($file = '', $continue = 0)
  29083. {
  29084. $this->docTemplate = $file;
  29085. $this->docTemplateContinue = $continue;
  29086. }
  29087. /* -- END IMPORTS -- */
  29088. /* ---------------------------------------------- */
  29089. /* ---------------------------------------------- */
  29090. /* ---------------------------------------------- */
  29091. /* ---------------------------------------------- */
  29092. /* ---------------------------------------------- */
  29093. // JAVASCRIPT
  29094. function _set_object_javascript($string)
  29095. {
  29096. $this->_newobj();
  29097. $this->_out('<<');
  29098. $this->_out('/S /JavaScript ');
  29099. $this->_out('/JS ' . $this->_textstring($string));
  29100. $this->_out('>>');
  29101. $this->_out('endobj');
  29102. }
  29103. function SetJS($script)
  29104. {
  29105. $this->js = $script;
  29106. }
  29107. }