Subversion Repositories eFlore/Applications.cel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1665 raphael 1
<?php
2
//============================================================+
3
// File name   : tcpdf.php
4
// Version     : 6.0.020
5
// Begin       : 2002-08-03
6
// Last Update : 2013-06-04
7
// Author      : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8
// License     : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9
// -------------------------------------------------------------------
10
// Copyright (C) 2002-2013 Nicola Asuni - Tecnick.com LTD
11
//
12
// This file is part of TCPDF software library.
13
//
14
// TCPDF is free software: you can redistribute it and/or modify it
15
// under the terms of the GNU Lesser General Public License as
16
// published by the Free Software Foundation, either version 3 of the
17
// License, or (at your option) any later version.
18
//
19
// TCPDF is distributed in the hope that it will be useful, but
20
// WITHOUT ANY WARRANTY; without even the implied warranty of
21
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22
// See the GNU Lesser General Public License for more details.
23
//
24
// You should have received a copy of the License
25
// along with TCPDF. If not, see
26
// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
27
//
28
// See LICENSE.TXT file for more information.
29
// -------------------------------------------------------------------
30
//
31
// Description :
32
//   This is a PHP class for generating PDF documents without requiring external extensions.
33
//
34
// NOTE:
35
//   This class was originally derived in 2002 from the Public
36
//   Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
37
//   but now is almost entirely rewritten and contains thousands of
38
//   new lines of code and hundreds new features.
39
//
40
// Main features:
41
//  * no external libraries are required for the basic functions;
42
//  * all standard page formats, custom page formats, custom margins and units of measure;
43
//  * UTF-8 Unicode and Right-To-Left languages;
44
//  * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
45
//  * font subsetting;
46
//  * methods to publish some XHTML + CSS code, Javascript and Forms;
47
//  * images, graphic (geometric figures) and transformation methods;
48
//  * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
49
//  * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50
//  * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51
//  * automatic page header and footer management;
52
//  * document encryption up to 256 bit and digital signature certifications;
53
//  * transactions to UNDO commands;
54
//  * PDF annotations, including links, text and file attachments;
55
//  * text rendering modes (fill, stroke and clipping);
56
//  * multiple columns mode;
57
//  * no-write page regions;
58
//  * bookmarks, named destinations and table of content;
59
//  * text hyphenation;
60
//  * text stretching and spacing (tracking);
61
//  * automatic page break, line break and text alignments including justification;
62
//  * automatic page numbering and page groups;
63
//  * move and delete pages;
64
//  * page compression (requires php-zlib extension);
65
//  * XOBject Templates;
66
//  * Layers and object visibility.
67
//	* PDF/A-1b support.
68
//
69
// -----------------------------------------------------------
70
// THANKS TO:
71
//
72
// Olivier Plathey (http://www.fpdf.org) for original FPDF.
73
// Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
74
// Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
75
// Warren Sherliker (wsherliker@gmail.com) for better image handling.
76
// dullus for text Justification.
77
// Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
78
// Patrick Benny for text stretch suggestion on Cell().
79
// Johannes Güntert for JavaScript support.
80
// Denis Van Nuffelen for Dynamic Form.
81
// Jacek Czekaj for multibyte justification
82
// Anthony Ferrara for the reintroduction of legacy image methods.
83
// Sourceforge user 1707880 (hucste) for line-through mode.
84
// Larry Stanbery for page groups.
85
// Martin Hall-May for transparency.
86
// Aaron C. Spike for Polycurve method.
87
// Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
88
// Moritz Wagner and Andreas Wurmser for graphic functions.
89
// Andrew Whitehead for core fonts support.
90
// Esteban Joël Marín for OpenType font conversion.
91
// Teus Hagen for several suggestions and fixes.
92
// Yukihiro Nakadaira for CID-0 CJK fonts fixes.
93
// Kosmas Papachristos for some CSS improvements.
94
// Marcel Partap for some fixes.
95
// Won Kyu Park for several suggestions, fixes and patches.
96
// Dominik Dzienia for QR-code support.
97
// Laurent Minguet for some suggestions.
98
// Christian Deligant for some suggestions and fixes.
99
// Travis Harris for crop mark suggestion.
100
// Aleksey Kuznetsov for some suggestions and text shadows.
101
// Jim Hanlon for several suggestions and patches.
102
// Anyone else that has reported a bug or sent a suggestion.
103
//============================================================+
104
 
105
/**
106
 * @file
107
 * This is a PHP class for generating PDF documents without requiring external extensions.<br>
108
 * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
109
 * <h3>TCPDF main features are:</h3>
110
 * <ul>
111
 * <li>no external libraries are required for the basic functions;</li>
112
 * <li>all standard page formats, custom page formats, custom margins and units of measure;</li>
113
 * <li>UTF-8 Unicode and Right-To-Left languages;</li>
114
 * <li>TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
115
 * <li>font subsetting;</li>
116
 * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li>
117
 * <li>images, graphic (geometric figures) and transformation methods;
118
 * <li>supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li>
119
 * <li>1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;</li>
120
 * <li>JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
121
 * <li>automatic page header and footer management;</li>
122
 * <li>document encryption up to 256 bit and digital signature certifications;</li>
123
 * <li>transactions to UNDO commands;</li>
124
 * <li>PDF annotations, including links, text and file attachments;</li>
125
 * <li>text rendering modes (fill, stroke and clipping);</li>
126
 * <li>multiple columns mode;</li>
127
 * <li>no-write page regions;</li>
128
 * <li>bookmarks, named destinations and table of content;</li>
129
 * <li>text hyphenation;</li>
130
 * <li>text stretching and spacing (tracking);</li>
131
 * <li>automatic page break, line break and text alignments including justification;</li>
132
 * <li>automatic page numbering and page groups;</li>
133
 * <li>move and delete pages;</li>
134
 * <li>page compression (requires php-zlib extension);</li>
135
 * <li>XOBject Templates;</li>
136
 * <li>Layers and object visibility;</li>
137
 * <li>PDF/A-1b support.</li>
138
 * </ul>
139
 * Tools to encode your unicode fonts are on fonts/utils directory.</p>
140
 * @package com.tecnick.tcpdf
141
 * @author Nicola Asuni
142
 * @version 6.0.020
143
 */
144
 
145
// TCPDF configuration
146
require_once(dirname(__FILE__).'/tcpdf_autoconfig.php');
147
// TCPDF static font methods and data
148
require_once(dirname(__FILE__).'/include/tcpdf_font_data.php');
149
// TCPDF static font methods and data
150
require_once(dirname(__FILE__).'/include/tcpdf_fonts.php');
151
// TCPDF static color methods and data
152
require_once(dirname(__FILE__).'/include/tcpdf_colors.php');
153
// TCPDF static image methods and data
154
require_once(dirname(__FILE__).'/include/tcpdf_images.php');
155
// TCPDF static methods and data
156
require_once(dirname(__FILE__).'/include/tcpdf_static.php');
157
 
158
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159
 
160
/**
161
 * @class TCPDF
162
 * PHP class for generating PDF documents without requiring external extensions.
163
 * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
164
 * @package com.tecnick.tcpdf
165
 * @brief PHP class for generating PDF documents without requiring external extensions.
166
 * @version 6.0.020
167
 * @author Nicola Asuni - info@tecnick.com
168
 */
169
class TCPDF {
170
 
171
	// Protected properties
172
 
173
	/**
174
	 * Current page number.
175
	 * @protected
176
	 */
177
	protected $page;
178
 
179
	/**
180
	 * Current object number.
181
	 * @protected
182
	 */
183
	protected $n;
184
 
185
	/**
186
	 * Array of object offsets.
187
	 * @protected
188
	 */
189
	protected $offsets = array();
190
 
191
	/**
192
	 * Array of object IDs for each page.
193
	 * @protected
194
	 */
195
	protected $pageobjects = array();
196
 
197
	/**
198
	 * Buffer holding in-memory PDF.
199
	 * @protected
200
	 */
201
	protected $buffer;
202
 
203
	/**
204
	 * Array containing pages.
205
	 * @protected
206
	 */
207
	protected $pages = array();
208
 
209
	/**
210
	 * Current document state.
211
	 * @protected
212
	 */
213
	protected $state;
214
 
215
	/**
216
	 * Compression flag.
217
	 * @protected
218
	 */
219
	protected $compress;
220
 
221
	/**
222
	 * Current page orientation (P = Portrait, L = Landscape).
223
	 * @protected
224
	 */
225
	protected $CurOrientation;
226
 
227
	/**
228
	 * Page dimensions.
229
	 * @protected
230
	 */
231
	protected $pagedim = array();
232
 
233
	/**
234
	 * Scale factor (number of points in user unit).
235
	 * @protected
236
	 */
237
	protected $k;
238
 
239
	/**
240
	 * Width of page format in points.
241
	 * @protected
242
	 */
243
	protected $fwPt;
244
 
245
	/**
246
	 * Height of page format in points.
247
	 * @protected
248
	 */
249
	protected $fhPt;
250
 
251
	/**
252
	 * Current width of page in points.
253
	 * @protected
254
	 */
255
	protected $wPt;
256
 
257
	/**
258
	 * Current height of page in points.
259
	 * @protected
260
	 */
261
	protected $hPt;
262
 
263
	/**
264
	 * Current width of page in user unit.
265
	 * @protected
266
	 */
267
	protected $w;
268
 
269
	/**
270
	 * Current height of page in user unit.
271
	 * @protected
272
	 */
273
	protected $h;
274
 
275
	/**
276
	 * Left margin.
277
	 * @protected
278
	 */
279
	protected $lMargin;
280
 
281
	/**
282
	 * Right margin.
283
	 * @protected
284
	 */
285
	protected $rMargin;
286
 
287
	/**
288
	 * Cell left margin (used by regions).
289
	 * @protected
290
	 */
291
	protected $clMargin;
292
 
293
	/**
294
	 * Cell right margin (used by regions).
295
	 * @protected
296
	 */
297
	protected $crMargin;
298
 
299
	/**
300
	 * Top margin.
301
	 * @protected
302
	 */
303
	protected $tMargin;
304
 
305
	/**
306
	 * Page break margin.
307
	 * @protected
308
	 */
309
	protected $bMargin;
310
 
311
	/**
312
	 * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
313
	 * @since 5.9.000 (2010-10-03)
314
	 * @protected
315
	 */
316
	protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
317
 
318
	/**
319
	 * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
320
	 * @since 5.9.000 (2010-10-04)
321
	 * @protected
322
	 */
323
	protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
324
 
325
	/**
326
	 * Current horizontal position in user unit for cell positioning.
327
	 * @protected
328
	 */
329
	protected $x;
330
 
331
	/**
332
	 * Current vertical position in user unit for cell positioning.
333
	 * @protected
334
	 */
335
	protected $y;
336
 
337
	/**
338
	 * Height of last cell printed.
339
	 * @protected
340
	 */
341
	protected $lasth;
342
 
343
	/**
344
	 * Line width in user unit.
345
	 * @protected
346
	 */
347
	protected $LineWidth;
348
 
349
	/**
350
	 * Array of standard font names.
351
	 * @protected
352
	 */
353
	protected $CoreFonts;
354
 
355
	/**
356
	 * Array of used fonts.
357
	 * @protected
358
	 */
359
	protected $fonts = array();
360
 
361
	/**
362
	 * Array of font files.
363
	 * @protected
364
	 */
365
	protected $FontFiles = array();
366
 
367
	/**
368
	 * Array of encoding differences.
369
	 * @protected
370
	 */
371
	protected $diffs = array();
372
 
373
	/**
374
	 * Array of used images.
375
	 * @protected
376
	 */
377
	protected $images = array();
378
 
379
	/**
380
	 * Array of cached files.
381
	 * @protected
382
	 */
383
	protected $cached_files = array();
384
 
385
	/**
386
	 * Array of Annotations in pages.
387
	 * @protected
388
	 */
389
	protected $PageAnnots = array();
390
 
391
	/**
392
	 * Array of internal links.
393
	 * @protected
394
	 */
395
	protected $links = array();
396
 
397
	/**
398
	 * Current font family.
399
	 * @protected
400
	 */
401
	protected $FontFamily;
402
 
403
	/**
404
	 * Current font style.
405
	 * @protected
406
	 */
407
	protected $FontStyle;
408
 
409
	/**
410
	 * Current font ascent (distance between font top and baseline).
411
	 * @protected
412
	 * @since 2.8.000 (2007-03-29)
413
	 */
414
	protected $FontAscent;
415
 
416
	/**
417
	 * Current font descent (distance between font bottom and baseline).
418
	 * @protected
419
	 * @since 2.8.000 (2007-03-29)
420
	 */
421
	protected $FontDescent;
422
 
423
	/**
424
	 * Underlining flag.
425
	 * @protected
426
	 */
427
	protected $underline;
428
 
429
	/**
430
	 * Overlining flag.
431
	 * @protected
432
	 */
433
	protected $overline;
434
 
435
	/**
436
	 * Current font info.
437
	 * @protected
438
	 */
439
	protected $CurrentFont;
440
 
441
	/**
442
	 * Current font size in points.
443
	 * @protected
444
	 */
445
	protected $FontSizePt;
446
 
447
	/**
448
	 * Current font size in user unit.
449
	 * @protected
450
	 */
451
	protected $FontSize;
452
 
453
	/**
454
	 * Commands for drawing color.
455
	 * @protected
456
	 */
457
	protected $DrawColor;
458
 
459
	/**
460
	 * Commands for filling color.
461
	 * @protected
462
	 */
463
	protected $FillColor;
464
 
465
	/**
466
	 * Commands for text color.
467
	 * @protected
468
	 */
469
	protected $TextColor;
470
 
471
	/**
472
	 * Indicates whether fill and text colors are different.
473
	 * @protected
474
	 */
475
	protected $ColorFlag;
476
 
477
	/**
478
	 * Automatic page breaking.
479
	 * @protected
480
	 */
481
	protected $AutoPageBreak;
482
 
483
	/**
484
	 * Threshold used to trigger page breaks.
485
	 * @protected
486
	 */
487
	protected $PageBreakTrigger;
488
 
489
	/**
490
	 * Flag set when processing page header.
491
	 * @protected
492
	 */
493
	protected $InHeader = false;
494
 
495
	/**
496
	 * Flag set when processing page footer.
497
	 * @protected
498
	 */
499
	protected $InFooter = false;
500
 
501
	/**
502
	 * Zoom display mode.
503
	 * @protected
504
	 */
505
	protected $ZoomMode;
506
 
507
	/**
508
	 * Layout display mode.
509
	 * @protected
510
	 */
511
	protected $LayoutMode;
512
 
513
	/**
514
	 * If true set the document information dictionary in Unicode.
515
	 * @protected
516
	 */
517
	protected $docinfounicode = true;
518
 
519
	/**
520
	 * Document title.
521
	 * @protected
522
	 */
523
	protected $title = '';
524
 
525
	/**
526
	 * Document subject.
527
	 * @protected
528
	 */
529
	protected $subject = '';
530
 
531
	/**
532
	 * Document author.
533
	 * @protected
534
	 */
535
	protected $author = '';
536
 
537
	/**
538
	 * Document keywords.
539
	 * @protected
540
	 */
541
	protected $keywords = '';
542
 
543
	/**
544
	 * Document creator.
545
	 * @protected
546
	 */
547
	protected $creator = '';
548
 
549
	/**
550
	 * Starting page number.
551
	 * @protected
552
	 */
553
	protected $starting_page_number = 1;
554
 
555
	/**
556
	 * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
557
	 * @since 2002-07-31
558
	 * @author Nicola Asuni
559
	 * @protected
560
	 */
561
	protected $img_rb_x;
562
 
563
	/**
564
	 * The right-bottom corner Y coordinate of last inserted image.
565
	 * @since 2002-07-31
566
	 * @author Nicola Asuni
567
	 * @protected
568
	 */
569
	protected $img_rb_y;
570
 
571
	/**
572
	 * Adjusting factor to convert pixels to user units.
573
	 * @since 2004-06-14
574
	 * @author Nicola Asuni
575
	 * @protected
576
	 */
577
	protected $imgscale = 1;
578
 
579
	/**
580
	 * Boolean flag set to true when the input text is unicode (require unicode fonts).
581
	 * @since 2005-01-02
582
	 * @author Nicola Asuni
583
	 * @protected
584
	 */
585
	protected $isunicode = false;
586
 
587
	/**
588
	 * PDF version.
589
	 * @since 1.5.3
590
	 * @protected
591
	 */
592
	protected $PDFVersion = '1.7';
593
 
594
	/**
595
	 * ID of the stored default header template (-1 = not set).
596
	 * @protected
597
	 */
598
	protected $header_xobjid = -1;
599
 
600
	/**
601
	 * If true reset the Header Xobject template at each page
602
	 * @protected
603
	 */
604
	protected $header_xobj_autoreset = false;
605
 
606
	/**
607
	 * Minimum distance between header and top page margin.
608
	 * @protected
609
	 */
610
	protected $header_margin;
611
 
612
	/**
613
	 * Minimum distance between footer and bottom page margin.
614
	 * @protected
615
	 */
616
	protected $footer_margin;
617
 
618
	/**
619
	 * Original left margin value.
620
	 * @protected
621
	 * @since 1.53.0.TC013
622
	 */
623
	protected $original_lMargin;
624
 
625
	/**
626
	 * Original right margin value.
627
	 * @protected
628
	 * @since 1.53.0.TC013
629
	 */
630
	protected $original_rMargin;
631
 
632
	/**
633
	 * Default font used on page header.
634
	 * @protected
635
	 */
636
	protected $header_font;
637
 
638
	/**
639
	 * Default font used on page footer.
640
	 * @protected
641
	 */
642
	protected $footer_font;
643
 
644
	/**
645
	 * Language templates.
646
	 * @protected
647
	 */
648
	protected $l;
649
 
650
	/**
651
	 * Barcode to print on page footer (only if set).
652
	 * @protected
653
	 */
654
	protected $barcode = false;
655
 
656
	/**
657
	 * Boolean flag to print/hide page header.
658
	 * @protected
659
	 */
660
	protected $print_header = true;
661
 
662
	/**
663
	 * Boolean flag to print/hide page footer.
664
	 * @protected
665
	 */
666
	protected $print_footer = true;
667
 
668
	/**
669
	 * Header image logo.
670
	 * @protected
671
	 */
672
	protected $header_logo = '';
673
 
674
	/**
675
	 * Width of header image logo in user units.
676
	 * @protected
677
	 */
678
	protected $header_logo_width = 30;
679
 
680
	/**
681
	 * Title to be printed on default page header.
682
	 * @protected
683
	 */
684
	protected $header_title = '';
685
 
686
	/**
687
	 * String to pring on page header after title.
688
	 * @protected
689
	 */
690
	protected $header_string = '';
691
 
692
	/**
693
	 * Color for header text (RGB array).
694
	 * @since 5.9.174 (2012-07-25)
695
	 * @protected
696
	 */
697
	protected $header_text_color = array(0,0,0);
698
 
699
	/**
700
	 * Color for header line (RGB array).
701
	 * @since 5.9.174 (2012-07-25)
702
	 * @protected
703
	 */
704
	protected $header_line_color = array(0,0,0);
705
 
706
	/**
707
	 * Color for footer text (RGB array).
708
	 * @since 5.9.174 (2012-07-25)
709
	 * @protected
710
	 */
711
	protected $footer_text_color = array(0,0,0);
712
 
713
	/**
714
	 * Color for footer line (RGB array).
715
	 * @since 5.9.174 (2012-07-25)
716
	 * @protected
717
	 */
718
	protected $footer_line_color = array(0,0,0);
719
 
720
	/**
721
	 * Text shadow data array.
722
	 * @since 5.9.174 (2012-07-25)
723
	 * @protected
724
	 */
725
	protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
726
 
727
	/**
728
	 * Default number of columns for html table.
729
	 * @protected
730
	 */
731
	protected $default_table_columns = 4;
732
 
733
	// variables for html parser
734
 
735
	/**
736
	 * HTML PARSER: array to store current link and rendering styles.
737
	 * @protected
738
	 */
739
	protected $HREF = array();
740
 
741
	/**
742
	 * List of available fonts on filesystem.
743
	 * @protected
744
	 */
745
	protected $fontlist = array();
746
 
747
	/**
748
	 * Current foreground color.
749
	 * @protected
750
	 */
751
	protected $fgcolor;
752
 
753
	/**
754
	 * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
755
	 * @protected
756
	 */
757
	protected $listordered = array();
758
 
759
	/**
760
	 * HTML PARSER: array count list items on nested lists.
761
	 * @protected
762
	 */
763
	protected $listcount = array();
764
 
765
	/**
766
	 * HTML PARSER: current list nesting level.
767
	 * @protected
768
	 */
769
	protected $listnum = 0;
770
 
771
	/**
772
	 * HTML PARSER: indent amount for lists.
773
	 * @protected
774
	 */
775
	protected $listindent = 0;
776
 
777
	/**
778
	 * HTML PARSER: current list indententation level.
779
	 * @protected
780
	 */
781
	protected $listindentlevel = 0;
782
 
783
	/**
784
	 * Current background color.
785
	 * @protected
786
	 */
787
	protected $bgcolor;
788
 
789
	/**
790
	 * Temporary font size in points.
791
	 * @protected
792
	 */
793
	protected $tempfontsize = 10;
794
 
795
	/**
796
	 * Spacer string for LI tags.
797
	 * @protected
798
	 */
799
	protected $lispacer = '';
800
 
801
	/**
802
	 * Default encoding.
803
	 * @protected
804
	 * @since 1.53.0.TC010
805
	 */
806
	protected $encoding = 'UTF-8';
807
 
808
	/**
809
	 * PHP internal encoding.
810
	 * @protected
811
	 * @since 1.53.0.TC016
812
	 */
813
	protected $internal_encoding;
814
 
815
	/**
816
	 * Boolean flag to indicate if the document language is Right-To-Left.
817
	 * @protected
818
	 * @since 2.0.000
819
	 */
820
	protected $rtl = false;
821
 
822
	/**
823
	 * Boolean flag used to force RTL or LTR string direction.
824
	 * @protected
825
	 * @since 2.0.000
826
	 */
827
	protected $tmprtl = false;
828
 
829
	// --- Variables used for document encryption:
830
 
831
	/**
832
	 * IBoolean flag indicating whether document is protected.
833
	 * @protected
834
	 * @since 2.0.000 (2008-01-02)
835
	 */
836
	protected $encrypted;
837
 
838
	/**
839
	 * Array containing encryption settings.
840
	 * @protected
841
	 * @since 5.0.005 (2010-05-11)
842
	 */
843
	protected $encryptdata = array();
844
 
845
	/**
846
	 * Last RC4 key encrypted (cached for optimisation).
847
	 * @protected
848
	 * @since 2.0.000 (2008-01-02)
849
	 */
850
	protected $last_enc_key;
851
 
852
	/**
853
	 * Last RC4 computed key.
854
	 * @protected
855
	 * @since 2.0.000 (2008-01-02)
856
	 */
857
	protected $last_enc_key_c;
858
 
859
	/**
860
	 * File ID (used on document trailer).
861
	 * @protected
862
	 * @since 5.0.005 (2010-05-12)
863
	 */
864
	protected $file_id;
865
 
866
	// --- bookmark ---
867
 
868
	/**
869
	 * Outlines for bookmark.
870
	 * @protected
871
	 * @since 2.1.002 (2008-02-12)
872
	 */
873
	protected $outlines = array();
874
 
875
	/**
876
	 * Outline root for bookmark.
877
	 * @protected
878
	 * @since 2.1.002 (2008-02-12)
879
	 */
880
	protected $OutlineRoot;
881
 
882
	// --- javascript and form ---
883
 
884
	/**
885
	 * Javascript code.
886
	 * @protected
887
	 * @since 2.1.002 (2008-02-12)
888
	 */
889
	protected $javascript = '';
890
 
891
	/**
892
	 * Javascript counter.
893
	 * @protected
894
	 * @since 2.1.002 (2008-02-12)
895
	 */
896
	protected $n_js;
897
 
898
	/**
899
	 * line through state
900
	 * @protected
901
	 * @since 2.8.000 (2008-03-19)
902
	 */
903
	protected $linethrough;
904
 
905
	/**
906
	 * Array with additional document-wide usage rights for the document.
907
	 * @protected
908
	 * @since 5.8.014 (2010-08-23)
909
	 */
910
	protected $ur = array();
911
 
912
	/**
913
	 * DPI (Dot Per Inch) Document Resolution (do not change).
914
	 * @protected
915
	 * @since 3.0.000 (2008-03-27)
916
	 */
917
	protected $dpi = 72;
918
 
919
	/**
920
	 * Array of page numbers were a new page group was started (the page numbers are the keys of the array).
921
	 * @protected
922
	 * @since 3.0.000 (2008-03-27)
923
	 */
924
	protected $newpagegroup = array();
925
 
926
	/**
927
	 * Array that contains the number of pages in each page group.
928
	 * @protected
929
	 * @since 3.0.000 (2008-03-27)
930
	 */
931
	protected $pagegroups = array();
932
 
933
	/**
934
	 * Current page group number.
935
	 * @protected
936
	 * @since 3.0.000 (2008-03-27)
937
	 */
938
	protected $currpagegroup = 0;
939
 
940
	/**
941
	 * Array of transparency objects and parameters.
942
	 * @protected
943
	 * @since 3.0.000 (2008-03-27)
944
	 */
945
	protected $extgstates;
946
 
947
	/**
948
	 * Set the default JPEG compression quality (1-100).
949
	 * @protected
950
	 * @since 3.0.000 (2008-03-27)
951
	 */
952
	protected $jpeg_quality;
953
 
954
	/**
955
	 * Default cell height ratio.
956
	 * @protected
957
	 * @since 3.0.014 (2008-05-23)
958
	 */
959
	protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
960
 
961
	/**
962
	 * PDF viewer preferences.
963
	 * @protected
964
	 * @since 3.1.000 (2008-06-09)
965
	 */
966
	protected $viewer_preferences;
967
 
968
	/**
969
	 * A name object specifying how the document should be displayed when opened.
970
	 * @protected
971
	 * @since 3.1.000 (2008-06-09)
972
	 */
973
	protected $PageMode;
974
 
975
	/**
976
	 * Array for storing gradient information.
977
	 * @protected
978
	 * @since 3.1.000 (2008-06-09)
979
	 */
980
	protected $gradients = array();
981
 
982
	/**
983
	 * Array used to store positions inside the pages buffer (keys are the page numbers).
984
	 * @protected
985
	 * @since 3.2.000 (2008-06-26)
986
	 */
987
	protected $intmrk = array();
988
 
989
	/**
990
	 * Array used to store positions inside the pages buffer (keys are the page numbers).
991
	 * @protected
992
	 * @since 5.7.000 (2010-08-03)
993
	 */
994
	protected $bordermrk = array();
995
 
996
	/**
997
	 * Array used to store page positions to track empty pages (keys are the page numbers).
998
	 * @protected
999
	 * @since 5.8.007 (2010-08-18)
1000
	 */
1001
	protected $emptypagemrk = array();
1002
 
1003
	/**
1004
	 * Array used to store content positions inside the pages buffer (keys are the page numbers).
1005
	 * @protected
1006
	 * @since 4.6.021 (2009-07-20)
1007
	 */
1008
	protected $cntmrk = array();
1009
 
1010
	/**
1011
	 * Array used to store footer positions of each page.
1012
	 * @protected
1013
	 * @since 3.2.000 (2008-07-01)
1014
	 */
1015
	protected $footerpos = array();
1016
 
1017
	/**
1018
	 * Array used to store footer length of each page.
1019
	 * @protected
1020
	 * @since 4.0.014 (2008-07-29)
1021
	 */
1022
	protected $footerlen = array();
1023
 
1024
	/**
1025
	 * Boolean flag to indicate if a new line is created.
1026
	 * @protected
1027
	 * @since 3.2.000 (2008-07-01)
1028
	 */
1029
	protected $newline = true;
1030
 
1031
	/**
1032
	 * End position of the latest inserted line.
1033
	 * @protected
1034
	 * @since 3.2.000 (2008-07-01)
1035
	 */
1036
	protected $endlinex = 0;
1037
 
1038
	/**
1039
	 * PDF string for width value of the last line.
1040
	 * @protected
1041
	 * @since 4.0.006 (2008-07-16)
1042
	 */
1043
	protected $linestyleWidth = '';
1044
 
1045
	/**
1046
	 * PDF string for CAP value of the last line.
1047
	 * @protected
1048
	 * @since 4.0.006 (2008-07-16)
1049
	 */
1050
	protected $linestyleCap = '0 J';
1051
 
1052
	/**
1053
	 * PDF string for join value of the last line.
1054
	 * @protected
1055
	 * @since 4.0.006 (2008-07-16)
1056
	 */
1057
	protected $linestyleJoin = '0 j';
1058
 
1059
	/**
1060
	 * PDF string for dash value of the last line.
1061
	 * @protected
1062
	 * @since 4.0.006 (2008-07-16)
1063
	 */
1064
	protected $linestyleDash = '[] 0 d';
1065
 
1066
	/**
1067
	 * Boolean flag to indicate if marked-content sequence is open.
1068
	 * @protected
1069
	 * @since 4.0.013 (2008-07-28)
1070
	 */
1071
	protected $openMarkedContent = false;
1072
 
1073
	/**
1074
	 * Count the latest inserted vertical spaces on HTML.
1075
	 * @protected
1076
	 * @since 4.0.021 (2008-08-24)
1077
	 */
1078
	protected $htmlvspace = 0;
1079
 
1080
	/**
1081
	 * Array of Spot colors.
1082
	 * @protected
1083
	 * @since 4.0.024 (2008-09-12)
1084
	 */
1085
	protected $spot_colors = array();
1086
 
1087
	/**
1088
	 * Symbol used for HTML unordered list items.
1089
	 * @protected
1090
	 * @since 4.0.028 (2008-09-26)
1091
	 */
1092
	protected $lisymbol = '';
1093
 
1094
	/**
1095
	 * String used to mark the beginning and end of EPS image blocks.
1096
	 * @protected
1097
	 * @since 4.1.000 (2008-10-18)
1098
	 */
1099
	protected $epsmarker = 'x#!#EPS#!#x';
1100
 
1101
	/**
1102
	 * Array of transformation matrix.
1103
	 * @protected
1104
	 * @since 4.2.000 (2008-10-29)
1105
	 */
1106
	protected $transfmatrix = array();
1107
 
1108
	/**
1109
	 * Current key for transformation matrix.
1110
	 * @protected
1111
	 * @since 4.8.005 (2009-09-17)
1112
	 */
1113
	protected $transfmatrix_key = 0;
1114
 
1115
	/**
1116
	 * Booklet mode for double-sided pages.
1117
	 * @protected
1118
	 * @since 4.2.000 (2008-10-29)
1119
	 */
1120
	protected $booklet = false;
1121
 
1122
	/**
1123
	 * Epsilon value used for float calculations.
1124
	 * @protected
1125
	 * @since 4.2.000 (2008-10-29)
1126
	 */
1127
	protected $feps = 0.005;
1128
 
1129
	/**
1130
	 * Array used for custom vertical spaces for HTML tags.
1131
	 * @protected
1132
	 * @since 4.2.001 (2008-10-30)
1133
	 */
1134
	protected $tagvspaces = array();
1135
 
1136
	/**
1137
	 * HTML PARSER: custom indent amount for lists. Negative value means disabled.
1138
	 * @protected
1139
	 * @since 4.2.007 (2008-11-12)
1140
	 */
1141
	protected $customlistindent = -1;
1142
 
1143
	/**
1144
	 * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
1145
	 * @protected
1146
	 * @since 4.2.010 (2008-11-14)
1147
	 */
1148
	protected $opencell = true;
1149
 
1150
	/**
1151
	 * Array of files to embedd.
1152
	 * @protected
1153
	 * @since 4.4.000 (2008-12-07)
1154
	 */
1155
	protected $embeddedfiles = array();
1156
 
1157
	/**
1158
	 * Boolean flag to indicate if we are inside a PRE tag.
1159
	 * @protected
1160
	 * @since 4.4.001 (2008-12-08)
1161
	 */
1162
	protected $premode = false;
1163
 
1164
	/**
1165
	 * Array used to store positions of graphics transformation blocks inside the page buffer.
1166
	 * keys are the page numbers
1167
	 * @protected
1168
	 * @since 4.4.002 (2008-12-09)
1169
	 */
1170
	protected $transfmrk = array();
1171
 
1172
	/**
1173
	 * Default color for html links.
1174
	 * @protected
1175
	 * @since 4.4.003 (2008-12-09)
1176
	 */
1177
	protected $htmlLinkColorArray = array(0, 0, 255);
1178
 
1179
	/**
1180
	 * Default font style to add to html links.
1181
	 * @protected
1182
	 * @since 4.4.003 (2008-12-09)
1183
	 */
1184
	protected $htmlLinkFontStyle = 'U';
1185
 
1186
	/**
1187
	 * Counts the number of pages.
1188
	 * @protected
1189
	 * @since 4.5.000 (2008-12-31)
1190
	 */
1191
	protected $numpages = 0;
1192
 
1193
	/**
1194
	 * Array containing page lengths in bytes.
1195
	 * @protected
1196
	 * @since 4.5.000 (2008-12-31)
1197
	 */
1198
	protected $pagelen = array();
1199
 
1200
	/**
1201
	 * Counts the number of pages.
1202
	 * @protected
1203
	 * @since 4.5.000 (2008-12-31)
1204
	 */
1205
	protected $numimages = 0;
1206
 
1207
	/**
1208
	 * Store the image keys.
1209
	 * @protected
1210
	 * @since 4.5.000 (2008-12-31)
1211
	 */
1212
	protected $imagekeys = array();
1213
 
1214
	/**
1215
	 * Length of the buffer in bytes.
1216
	 * @protected
1217
	 * @since 4.5.000 (2008-12-31)
1218
	 */
1219
	protected $bufferlen = 0;
1220
 
1221
	/**
1222
	 * If true enables disk caching.
1223
	 * @protected
1224
	 * @since 4.5.000 (2008-12-31)
1225
	 */
1226
	protected $diskcache = false;
1227
 
1228
	/**
1229
	 * Counts the number of fonts.
1230
	 * @protected
1231
	 * @since 4.5.000 (2009-01-02)
1232
	 */
1233
	protected $numfonts = 0;
1234
 
1235
	/**
1236
	 * Store the font keys.
1237
	 * @protected
1238
	 * @since 4.5.000 (2009-01-02)
1239
	 */
1240
	protected $fontkeys = array();
1241
 
1242
	/**
1243
	 * Store the font object IDs.
1244
	 * @protected
1245
	 * @since 4.8.001 (2009-09-09)
1246
	 */
1247
	protected $font_obj_ids = array();
1248
 
1249
	/**
1250
	 * Store the fage status (true when opened, false when closed).
1251
	 * @protected
1252
	 * @since 4.5.000 (2009-01-02)
1253
	 */
1254
	protected $pageopen = array();
1255
 
1256
	/**
1257
	 * Default monospace font.
1258
	 * @protected
1259
	 * @since 4.5.025 (2009-03-10)
1260
	 */
1261
	protected $default_monospaced_font = 'courier';
1262
 
1263
	/**
1264
	 * Cloned copy of the current class object.
1265
	 * @protected
1266
	 * @since 4.5.029 (2009-03-19)
1267
	 */
1268
	protected $objcopy;
1269
 
1270
	/**
1271
	 * Array used to store the lengths of cache files.
1272
	 * @protected
1273
	 * @since 4.5.029 (2009-03-19)
1274
	 */
1275
	protected $cache_file_length = array();
1276
 
1277
	/**
1278
	 * Table header content to be repeated on each new page.
1279
	 * @protected
1280
	 * @since 4.5.030 (2009-03-20)
1281
	 */
1282
	protected $thead = '';
1283
 
1284
	/**
1285
	 * Margins used for table header.
1286
	 * @protected
1287
	 * @since 4.5.030 (2009-03-20)
1288
	 */
1289
	protected $theadMargins = array();
1290
 
1291
	/**
1292
	 * Boolean flag to enable document digital signature.
1293
	 * @protected
1294
	 * @since 4.6.005 (2009-04-24)
1295
	 */
1296
	protected $sign = false;
1297
 
1298
	/**
1299
	 * Digital signature data.
1300
	 * @protected
1301
	 * @since 4.6.005 (2009-04-24)
1302
	 */
1303
	protected $signature_data = array();
1304
 
1305
	/**
1306
	 * Digital signature max length.
1307
	 * @protected
1308
	 * @since 4.6.005 (2009-04-24)
1309
	 */
1310
	protected $signature_max_length = 11742;
1311
 
1312
	/**
1313
	 * Data for digital signature appearance.
1314
	 * @protected
1315
	 * @since 5.3.011 (2010-06-16)
1316
	 */
1317
	protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1318
 
1319
	/**
1320
	 * Array of empty digital signature appearances.
1321
	 * @protected
1322
	 * @since 5.9.101 (2011-07-06)
1323
	 */
1324
	protected $empty_signature_appearance = array();
1325
 
1326
	/**
1327
	 * Regular expression used to find blank characters (required for word-wrapping).
1328
	 * @protected
1329
	 * @since 4.6.006 (2009-04-28)
1330
	 */
1331
	protected $re_spaces = '/[^\S\xa0]/';
1332
 
1333
	/**
1334
	 * Array of $re_spaces parts.
1335
	 * @protected
1336
	 * @since 5.5.011 (2010-07-09)
1337
	 */
1338
	protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1339
 
1340
	/**
1341
	 * Digital signature object ID.
1342
	 * @protected
1343
	 * @since 4.6.022 (2009-06-23)
1344
	 */
1345
	protected $sig_obj_id = 0;
1346
 
1347
	/**
1348
	 * ID of page objects.
1349
	 * @protected
1350
	 * @since 4.7.000 (2009-08-29)
1351
	 */
1352
	protected $page_obj_id = array();
1353
 
1354
	/**
1355
	 * List of form annotations IDs.
1356
	 * @protected
1357
	 * @since 4.8.000 (2009-09-07)
1358
	 */
1359
	protected $form_obj_id = array();
1360
 
1361
	/**
1362
	 * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry.
1363
	 * @protected
1364
	 * @since 4.8.000 (2009-09-07)
1365
	 */
1366
	protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1367
 
1368
	/**
1369
	 * Javascript objects array.
1370
	 * @protected
1371
	 * @since 4.8.000 (2009-09-07)
1372
	 */
1373
	protected $js_objects = array();
1374
 
1375
	/**
1376
	 * Current form action (used during XHTML rendering).
1377
	 * @protected
1378
	 * @since 4.8.000 (2009-09-07)
1379
	 */
1380
	protected $form_action = '';
1381
 
1382
	/**
1383
	 * Current form encryption type (used during XHTML rendering).
1384
	 * @protected
1385
	 * @since 4.8.000 (2009-09-07)
1386
	 */
1387
	protected $form_enctype = 'application/x-www-form-urlencoded';
1388
 
1389
	/**
1390
	 * Current method to submit forms.
1391
	 * @protected
1392
	 * @since 4.8.000 (2009-09-07)
1393
	 */
1394
	protected $form_mode = 'post';
1395
 
1396
	/**
1397
	 * List of fonts used on form fields (fontname => fontkey).
1398
	 * @protected
1399
	 * @since 4.8.001 (2009-09-09)
1400
	 */
1401
	protected $annotation_fonts = array();
1402
 
1403
	/**
1404
	 * List of radio buttons parent objects.
1405
	 * @protected
1406
	 * @since 4.8.001 (2009-09-09)
1407
	 */
1408
	protected $radiobutton_groups = array();
1409
 
1410
	/**
1411
	 * List of radio group objects IDs.
1412
	 * @protected
1413
	 * @since 4.8.001 (2009-09-09)
1414
	 */
1415
	protected $radio_groups = array();
1416
 
1417
	/**
1418
	 * Text indentation value (used for text-indent CSS attribute).
1419
	 * @protected
1420
	 * @since 4.8.006 (2009-09-23)
1421
	 */
1422
	protected $textindent = 0;
1423
 
1424
	/**
1425
	 * Store page number when startTransaction() is called.
1426
	 * @protected
1427
	 * @since 4.8.006 (2009-09-23)
1428
	 */
1429
	protected $start_transaction_page = 0;
1430
 
1431
	/**
1432
	 * Store Y position when startTransaction() is called.
1433
	 * @protected
1434
	 * @since 4.9.001 (2010-03-28)
1435
	 */
1436
	protected $start_transaction_y = 0;
1437
 
1438
	/**
1439
	 * True when we are printing the thead section on a new page.
1440
	 * @protected
1441
	 * @since 4.8.027 (2010-01-25)
1442
	 */
1443
	protected $inthead = false;
1444
 
1445
	/**
1446
	 * Array of column measures (width, space, starting Y position).
1447
	 * @protected
1448
	 * @since 4.9.001 (2010-03-28)
1449
	 */
1450
	protected $columns = array();
1451
 
1452
	/**
1453
	 * Number of colums.
1454
	 * @protected
1455
	 * @since 4.9.001 (2010-03-28)
1456
	 */
1457
	protected $num_columns = 1;
1458
 
1459
	/**
1460
	 * Current column number.
1461
	 * @protected
1462
	 * @since 4.9.001 (2010-03-28)
1463
	 */
1464
	protected $current_column = 0;
1465
 
1466
	/**
1467
	 * Starting page for columns.
1468
	 * @protected
1469
	 * @since 4.9.001 (2010-03-28)
1470
	 */
1471
	protected $column_start_page = 0;
1472
 
1473
	/**
1474
	 * Maximum page and column selected.
1475
	 * @protected
1476
	 * @since 5.8.000 (2010-08-11)
1477
	 */
1478
	protected $maxselcol = array('page' => 0, 'column' => 0);
1479
 
1480
	/**
1481
	 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1482
	 * @protected
1483
	 * @since 5.8.000 (2010-08-11)
1484
	 */
1485
	protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1486
 
1487
	/**
1488
	 * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping.
1489
	 * @protected
1490
	 * @since 4.9.008 (2010-04-03)
1491
	 */
1492
	protected $textrendermode = 0;
1493
 
1494
	/**
1495
	 * Text stroke width in doc units.
1496
	 * @protected
1497
	 * @since 4.9.008 (2010-04-03)
1498
	 */
1499
	protected $textstrokewidth = 0;
1500
 
1501
	/**
1502
	 * Current stroke color.
1503
	 * @protected
1504
	 * @since 4.9.008 (2010-04-03)
1505
	 */
1506
	protected $strokecolor;
1507
 
1508
	/**
1509
	 * Default unit of measure for document.
1510
	 * @protected
1511
	 * @since 5.0.000 (2010-04-22)
1512
	 */
1513
	protected $pdfunit = 'mm';
1514
 
1515
	/**
1516
	 * Boolean flag true when we are on TOC (Table Of Content) page.
1517
	 * @protected
1518
	 */
1519
	protected $tocpage = false;
1520
 
1521
	/**
1522
	 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1523
	 * @protected
1524
	 * @since 5.0.000 (2010-04-26)
1525
	 */
1526
	protected $rasterize_vector_images = false;
1527
 
1528
	/**
1529
	 * Boolean flag: if true enables font subsetting by default.
1530
	 * @protected
1531
	 * @since 5.3.002 (2010-06-07)
1532
	 */
1533
	protected $font_subsetting = true;
1534
 
1535
	/**
1536
	 * Array of default graphic settings.
1537
	 * @protected
1538
	 * @since 5.5.008 (2010-07-02)
1539
	 */
1540
	protected $default_graphic_vars = array();
1541
 
1542
	/**
1543
	 * Array of XObjects.
1544
	 * @protected
1545
	 * @since 5.8.014 (2010-08-23)
1546
	 */
1547
	protected $xobjects = array();
1548
 
1549
	/**
1550
	 * Boolean value true when we are inside an XObject.
1551
	 * @protected
1552
	 * @since 5.8.017 (2010-08-24)
1553
	 */
1554
	protected $inxobj = false;
1555
 
1556
	/**
1557
	 * Current XObject ID.
1558
	 * @protected
1559
	 * @since 5.8.017 (2010-08-24)
1560
	 */
1561
	protected $xobjid = '';
1562
 
1563
	/**
1564
	 * Percentage of character stretching.
1565
	 * @protected
1566
	 * @since 5.9.000 (2010-09-29)
1567
	 */
1568
	protected $font_stretching = 100;
1569
 
1570
	/**
1571
	 * Increases or decreases the space between characters in a text by the specified amount (tracking).
1572
	 * @protected
1573
	 * @since 5.9.000 (2010-09-29)
1574
	 */
1575
	protected $font_spacing = 0;
1576
 
1577
	/**
1578
	 * Array of no-write regions.
1579
	 * ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right)
1580
	 * @protected
1581
	 * @since 5.9.003 (2010-10-14)
1582
	 */
1583
	protected $page_regions = array();
1584
 
1585
	/**
1586
	 * Boolean value true when page region check is active.
1587
	 * @protected
1588
	 */
1589
	protected $check_page_regions = true;
1590
 
1591
	/**
1592
	 * Array of PDF layers data.
1593
	 * @protected
1594
	 * @since 5.9.102 (2011-07-13)
1595
	 */
1596
	protected $pdflayers = array();
1597
 
1598
	/**
1599
	 * A dictionary of names and corresponding destinations (Dests key on document Catalog).
1600
	 * @protected
1601
	 * @since 5.9.097 (2011-06-23)
1602
	 */
1603
	protected $dests = array();
1604
 
1605
	/**
1606
	 * Object ID for Named Destinations
1607
	 * @protected
1608
	 * @since 5.9.097 (2011-06-23)
1609
	 */
1610
	protected $n_dests;
1611
 
1612
	/**
1613
	 * Embedded Files Names
1614
	 * @protected
1615
	 * @since 5.9.204 (2013-01-23)
1616
	 */
1617
	protected $efnames = array();
1618
 
1619
	/**
1620
	 * Directory used for the last SVG image.
1621
	 * @protected
1622
	 * @since 5.0.000 (2010-05-05)
1623
	 */
1624
	protected $svgdir = '';
1625
 
1626
	/**
1627
	 *  Deafult unit of measure for SVG.
1628
	 * @protected
1629
	 * @since 5.0.000 (2010-05-02)
1630
	 */
1631
	protected $svgunit = 'px';
1632
 
1633
	/**
1634
	 * Array of SVG gradients.
1635
	 * @protected
1636
	 * @since 5.0.000 (2010-05-02)
1637
	 */
1638
	protected $svggradients = array();
1639
 
1640
	/**
1641
	 * ID of last SVG gradient.
1642
	 * @protected
1643
	 * @since 5.0.000 (2010-05-02)
1644
	 */
1645
	protected $svggradientid = 0;
1646
 
1647
	/**
1648
	 * Boolean value true when in SVG defs group.
1649
	 * @protected
1650
	 * @since 5.0.000 (2010-05-02)
1651
	 */
1652
	protected $svgdefsmode = false;
1653
 
1654
	/**
1655
	 * Array of SVG defs.
1656
	 * @protected
1657
	 * @since 5.0.000 (2010-05-02)
1658
	 */
1659
	protected $svgdefs = array();
1660
 
1661
	/**
1662
	 * Boolean value true when in SVG clipPath tag.
1663
	 * @protected
1664
	 * @since 5.0.000 (2010-04-26)
1665
	 */
1666
	protected $svgclipmode = false;
1667
 
1668
	/**
1669
	 * Array of SVG clipPath commands.
1670
	 * @protected
1671
	 * @since 5.0.000 (2010-05-02)
1672
	 */
1673
	protected $svgclippaths = array();
1674
 
1675
	/**
1676
	 * Array of SVG clipPath tranformation matrix.
1677
	 * @protected
1678
	 * @since 5.8.022 (2010-08-31)
1679
	 */
1680
	protected $svgcliptm = array();
1681
 
1682
	/**
1683
	 * ID of last SVG clipPath.
1684
	 * @protected
1685
	 * @since 5.0.000 (2010-05-02)
1686
	 */
1687
	protected $svgclipid = 0;
1688
 
1689
	/**
1690
	 * SVG text.
1691
	 * @protected
1692
	 * @since 5.0.000 (2010-05-02)
1693
	 */
1694
	protected $svgtext = '';
1695
 
1696
	/**
1697
	 * SVG text properties.
1698
	 * @protected
1699
	 * @since 5.8.013 (2010-08-23)
1700
	 */
1701
	protected $svgtextmode = array();
1702
 
1703
	/**
1704
	 * Array of SVG properties.
1705
	 * @protected
1706
	 * @since 5.0.000 (2010-05-02)
1707
	 */
1708
	protected $svgstyles = array(array(
1709
		'alignment-baseline' => 'auto',
1710
		'baseline-shift' => 'baseline',
1711
		'clip' => 'auto',
1712
		'clip-path' => 'none',
1713
		'clip-rule' => 'nonzero',
1714
		'color' => 'black',
1715
		'color-interpolation' => 'sRGB',
1716
		'color-interpolation-filters' => 'linearRGB',
1717
		'color-profile' => 'auto',
1718
		'color-rendering' => 'auto',
1719
		'cursor' => 'auto',
1720
		'direction' => 'ltr',
1721
		'display' => 'inline',
1722
		'dominant-baseline' => 'auto',
1723
		'enable-background' => 'accumulate',
1724
		'fill' => 'black',
1725
		'fill-opacity' => 1,
1726
		'fill-rule' => 'nonzero',
1727
		'filter' => 'none',
1728
		'flood-color' => 'black',
1729
		'flood-opacity' => 1,
1730
		'font' => '',
1731
		'font-family' => 'helvetica',
1732
		'font-size' => 'medium',
1733
		'font-size-adjust' => 'none',
1734
		'font-stretch' => 'normal',
1735
		'font-style' => 'normal',
1736
		'font-variant' => 'normal',
1737
		'font-weight' => 'normal',
1738
		'glyph-orientation-horizontal' => '0deg',
1739
		'glyph-orientation-vertical' => 'auto',
1740
		'image-rendering' => 'auto',
1741
		'kerning' => 'auto',
1742
		'letter-spacing' => 'normal',
1743
		'lighting-color' => 'white',
1744
		'marker' => '',
1745
		'marker-end' => 'none',
1746
		'marker-mid' => 'none',
1747
		'marker-start' => 'none',
1748
		'mask' => 'none',
1749
		'opacity' => 1,
1750
		'overflow' => 'auto',
1751
		'pointer-events' => 'visiblePainted',
1752
		'shape-rendering' => 'auto',
1753
		'stop-color' => 'black',
1754
		'stop-opacity' => 1,
1755
		'stroke' => 'none',
1756
		'stroke-dasharray' => 'none',
1757
		'stroke-dashoffset' => 0,
1758
		'stroke-linecap' => 'butt',
1759
		'stroke-linejoin' => 'miter',
1760
		'stroke-miterlimit' => 4,
1761
		'stroke-opacity' => 1,
1762
		'stroke-width' => 1,
1763
		'text-anchor' => 'start',
1764
		'text-decoration' => 'none',
1765
		'text-rendering' => 'auto',
1766
		'unicode-bidi' => 'normal',
1767
		'visibility' => 'visible',
1768
		'word-spacing' => 'normal',
1769
		'writing-mode' => 'lr-tb',
1770
		'text-color' => 'black',
1771
		'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1772
		));
1773
 
1774
	/**
1775
	 * If true force sRGB color profile for all document.
1776
	 * @protected
1777
	 * @since 5.9.121 (2011-09-28)
1778
	 */
1779
	protected $force_srgb = false;
1780
 
1781
	/**
1782
	 * If true set the document to PDF/A mode.
1783
	 * @protected
1784
	 * @since 5.9.121 (2011-09-27)
1785
	 */
1786
	protected $pdfa_mode = false;
1787
 
1788
	/**
1789
	 * Document creation date-time
1790
	 * @protected
1791
	 * @since 5.9.152 (2012-03-22)
1792
	 */
1793
	protected $doc_creation_timestamp;
1794
 
1795
	/**
1796
	 * Document modification date-time
1797
	 * @protected
1798
	 * @since 5.9.152 (2012-03-22)
1799
	 */
1800
	protected $doc_modification_timestamp;
1801
 
1802
	/**
1803
	 * Custom XMP data.
1804
	 * @protected
1805
	 * @since 5.9.128 (2011-10-06)
1806
	 */
1807
	protected $custom_xmp = '';
1808
 
1809
	/**
1810
	 * Overprint mode array.
1811
	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1812
	 * @protected
1813
	 * @since 5.9.152 (2012-03-23)
1814
	 */
1815
	protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1816
 
1817
	/**
1818
	 * Alpha mode array.
1819
	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1820
	 * @protected
1821
	 * @since 5.9.152 (2012-03-23)
1822
	 */
1823
	protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1824
 
1825
	/**
1826
	 * Define the page boundaries boxes to be set on document.
1827
	 * @protected
1828
	 * @since 5.9.152 (2012-03-23)
1829
	 */
1830
	protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1831
 
1832
	/**
1833
	 * If true print TCPDF meta link.
1834
	 * @protected
1835
	 * @since 5.9.152 (2012-03-23)
1836
	 */
1837
	protected $tcpdflink = true;
1838
 
1839
	/**
1840
	 * Cache array for computed GD gamma values.
1841
	 * @protected
1842
	 * @since 5.9.1632 (2012-06-05)
1843
	 */
1844
	protected $gdgammacache = array();
1845
 
1846
	//------------------------------------------------------------
1847
	// METHODS
1848
	//------------------------------------------------------------
1849
 
1850
	/**
1851
	 * This is the class constructor.
1852
	 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
1853
	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
1854
	 * @param $unit (string) User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1855
	 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
1856
	 * @param $unicode (boolean) TRUE means that the input text is unicode (default = true)
1857
	 * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8.
1858
	 * @param $diskcache (boolean) If TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1859
	 * @param $pdfa (boolean) If TRUE set the document to PDF/A mode.
1860
	 * @public
1861
	 * @see getPageSizeFromFormat(), setPageFormat()
1862
	 */
1863
	public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1864
		/* Set internal character encoding to ASCII */
1865
		if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1866
			$this->internal_encoding = mb_internal_encoding();
1867
			mb_internal_encoding('ASCII');
1868
		}
1869
		$this->font_obj_ids = array();
1870
		$this->page_obj_id = array();
1871
		$this->form_obj_id = array();
1872
		// set pdf/a mode
1873
		$this->pdfa_mode = $pdfa;
1874
		$this->force_srgb = false;
1875
		// set disk caching
1876
		$this->diskcache = $diskcache ? true : false;
1877
		// set language direction
1878
		$this->rtl = false;
1879
		$this->tmprtl = false;
1880
		// some checks
1881
		$this->_dochecks();
1882
		// initialization of properties
1883
		$this->isunicode = $unicode;
1884
		$this->page = 0;
1885
		$this->transfmrk[0] = array();
1886
		$this->pagedim = array();
1887
		$this->n = 2;
1888
		$this->buffer = '';
1889
		$this->pages = array();
1890
		$this->state = 0;
1891
		$this->fonts = array();
1892
		$this->FontFiles = array();
1893
		$this->diffs = array();
1894
		$this->images = array();
1895
		$this->links = array();
1896
		$this->gradients = array();
1897
		$this->InFooter = false;
1898
		$this->lasth = 0;
1899
		$this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1900
		$this->FontStyle = '';
1901
		$this->FontSizePt = 12;
1902
		$this->underline = false;
1903
		$this->overline = false;
1904
		$this->linethrough = false;
1905
		$this->DrawColor = '0 G';
1906
		$this->FillColor = '0 g';
1907
		$this->TextColor = '0 g';
1908
		$this->ColorFlag = false;
1909
		$this->pdflayers = array();
1910
		// encryption values
1911
		$this->encrypted = false;
1912
		$this->last_enc_key = '';
1913
		// standard Unicode fonts
1914
		$this->CoreFonts = array(
1915
			'courier'=>'Courier',
1916
			'courierB'=>'Courier-Bold',
1917
			'courierI'=>'Courier-Oblique',
1918
			'courierBI'=>'Courier-BoldOblique',
1919
			'helvetica'=>'Helvetica',
1920
			'helveticaB'=>'Helvetica-Bold',
1921
			'helveticaI'=>'Helvetica-Oblique',
1922
			'helveticaBI'=>'Helvetica-BoldOblique',
1923
			'times'=>'Times-Roman',
1924
			'timesB'=>'Times-Bold',
1925
			'timesI'=>'Times-Italic',
1926
			'timesBI'=>'Times-BoldItalic',
1927
			'symbol'=>'Symbol',
1928
			'zapfdingbats'=>'ZapfDingbats'
1929
		);
1930
		// set scale factor
1931
		$this->setPageUnit($unit);
1932
		// set page format and orientation
1933
		$this->setPageFormat($format, $orientation);
1934
		// page margins (1 cm)
1935
		$margin = 28.35 / $this->k;
1936
		$this->SetMargins($margin, $margin);
1937
		$this->clMargin = $this->lMargin;
1938
		$this->crMargin = $this->rMargin;
1939
		// internal cell padding
1940
		$cpadding = $margin / 10;
1941
		$this->setCellPaddings($cpadding, 0, $cpadding, 0);
1942
		// cell margins
1943
		$this->setCellMargins(0, 0, 0, 0);
1944
		// line width (0.2 mm)
1945
		$this->LineWidth = 0.57 / $this->k;
1946
		$this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1947
		$this->linestyleCap = '0 J';
1948
		$this->linestyleJoin = '0 j';
1949
		$this->linestyleDash = '[] 0 d';
1950
		// automatic page break
1951
		$this->SetAutoPageBreak(true, (2 * $margin));
1952
		// full width display mode
1953
		$this->SetDisplayMode('fullwidth');
1954
		// compression
1955
		$this->SetCompression();
1956
		// set default PDF version number
1957
		$this->setPDFVersion();
1958
		$this->tcpdflink = true;
1959
		$this->encoding = $encoding;
1960
		$this->HREF = array();
1961
		$this->getFontsList();
1962
		$this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1963
		$this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1964
		$this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1965
		$this->extgstates = array();
1966
		$this->setTextShadow();
1967
		// user's rights
1968
		$this->sign = false;
1969
		$this->ur['enabled'] = false;
1970
		$this->ur['document'] = '/FullSave';
1971
		$this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1972
		$this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1973
		$this->ur['signature'] = '/Modify';
1974
		$this->ur['ef'] = '/Create/Delete/Modify/Import';
1975
		$this->ur['formex'] = '';
1976
		$this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1977
		$this->empty_signature_appearance = array();
1978
		// set default JPEG quality
1979
		$this->jpeg_quality = 75;
1980
		// initialize some settings
1981
		TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont);
1982
		// set default font
1983
		$this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1984
		// check if PCRE Unicode support is enabled
1985
		if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1986
			// PCRE unicode support is turned ON
1987
			// \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1988
			// \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1989
			// \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1990
			//$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
1991
			$this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
1992
		} else {
1993
			// PCRE unicode support is turned OFF
1994
			$this->setSpacesRE('/[^\S\xa0]/');
1995
		}
1996
		$this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1997
		// set file ID for trailer
1998
		$serformat = (is_array($format) ? serialize($format) : $format);
1999
		$this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
2000
		// set document creation and modification timestamp
2001
		$this->doc_creation_timestamp = time();
2002
		$this->doc_modification_timestamp = $this->doc_creation_timestamp;
2003
		// get default graphic vars
2004
		$this->default_graphic_vars = $this->getGraphicVars();
2005
		$this->header_xobj_autoreset = false;
2006
		$this->custom_xmp = '';
2007
	}
2008
 
2009
	/**
2010
	 * Default destructor.
2011
	 * @public
2012
	 * @since 1.53.0.TC016
2013
	 */
2014
	public function __destruct() {
2015
		// restore internal encoding
2016
		if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
2017
			mb_internal_encoding($this->internal_encoding);
2018
		}
2019
		// unset all class variables
2020
		$this->_destroy(true);
2021
	}
2022
 
2023
	/**
2024
	 * Set the units of measure for the document.
2025
	 * @param $unit (string) User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
2026
	 * @public
2027
	 * @since 3.0.015 (2008-06-06)
2028
	 */
2029
	public function setPageUnit($unit) {
2030
		$unit = strtolower($unit);
2031
		//Set scale factor
2032
		switch ($unit) {
2033
			// points
2034
			case 'px':
2035
			case 'pt': {
2036
				$this->k = 1;
2037
				break;
2038
			}
2039
			// millimeters
2040
			case 'mm': {
2041
				$this->k = $this->dpi / 25.4;
2042
				break;
2043
			}
2044
			// centimeters
2045
			case 'cm': {
2046
				$this->k = $this->dpi / 2.54;
2047
				break;
2048
			}
2049
			// inches
2050
			case 'in': {
2051
				$this->k = $this->dpi;
2052
				break;
2053
			}
2054
			// unsupported unit
2055
			default : {
2056
				$this->Error('Incorrect unit: '.$unit);
2057
				break;
2058
			}
2059
		}
2060
		$this->pdfunit = $unit;
2061
		if (isset($this->CurOrientation)) {
2062
			$this->setPageOrientation($this->CurOrientation);
2063
		}
2064
	}
2065
 
2066
	/**
2067
	 * Change the format of the current page
2068
	 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() documentation or an array of two numners (width, height) or an array containing the following measures and options:<ul>
2069
	 * <li>['format'] = page format name (one of the above);</li>
2070
	 * <li>['Rotate'] : The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li>
2071
	 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
2072
	 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
2073
	 * <li>['MediaBox']['llx'] : lower-left x coordinate in points</li>
2074
	 * <li>['MediaBox']['lly'] : lower-left y coordinate in points</li>
2075
	 * <li>['MediaBox']['urx'] : upper-right x coordinate in points</li>
2076
	 * <li>['MediaBox']['ury'] : upper-right y coordinate in points</li>
2077
	 * <li>['CropBox'] : the visible region of default user space:</li>
2078
	 * <li>['CropBox']['llx'] : lower-left x coordinate in points</li>
2079
	 * <li>['CropBox']['lly'] : lower-left y coordinate in points</li>
2080
	 * <li>['CropBox']['urx'] : upper-right x coordinate in points</li>
2081
	 * <li>['CropBox']['ury'] : upper-right y coordinate in points</li>
2082
	 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
2083
	 * <li>['BleedBox']['llx'] : lower-left x coordinate in points</li>
2084
	 * <li>['BleedBox']['lly'] : lower-left y coordinate in points</li>
2085
	 * <li>['BleedBox']['urx'] : upper-right x coordinate in points</li>
2086
	 * <li>['BleedBox']['ury'] : upper-right y coordinate in points</li>
2087
	 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
2088
	 * <li>['TrimBox']['llx'] : lower-left x coordinate in points</li>
2089
	 * <li>['TrimBox']['lly'] : lower-left y coordinate in points</li>
2090
	 * <li>['TrimBox']['urx'] : upper-right x coordinate in points</li>
2091
	 * <li>['TrimBox']['ury'] : upper-right y coordinate in points</li>
2092
	 * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
2093
	 * <li>['ArtBox']['llx'] : lower-left x coordinate in points</li>
2094
	 * <li>['ArtBox']['lly'] : lower-left y coordinate in points</li>
2095
	 * <li>['ArtBox']['urx'] : upper-right x coordinate in points</li>
2096
	 * <li>['ArtBox']['ury'] : upper-right y coordinate in points</li>
2097
	 * <li>['BoxColorInfo'] :specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for each of the possible page boundaries other than the MediaBox:</li>
2098
	 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
2099
	 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
2100
	 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
2101
	 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
2102
	 * <li>['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation</li>
2103
	 * <li>['trans']['Dur'] : The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li>
2104
	 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
2105
	 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
2106
	 * <li>['trans']['Dm'] : (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li>
2107
	 * <li>['trans']['M'] : (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li>
2108
	 * <li>['trans']['Di'] : (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li>
2109
	 * <li>['trans']['SS'] : (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0.</li>
2110
	 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
2111
	 * </ul>
2112
	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul>
2113
	 * <li>P or Portrait (default)</li>
2114
	 * <li>L or Landscape</li>
2115
	 * <li>'' (empty string) for automatic orientation</li>
2116
	 * </ul>
2117
	 * @protected
2118
	 * @since 3.0.015 (2008-06-06)
2119
	 * @see getPageSizeFromFormat()
2120
	 */
2121
	protected function setPageFormat($format, $orientation='P') {
2122
		if (!empty($format) AND isset($this->pagedim[$this->page])) {
2123
			// remove inherited values
2124
			unset($this->pagedim[$this->page]);
2125
		}
2126
		if (is_string($format)) {
2127
			// get page measures from format name
2128
			$pf = TCPDF_STATIC::getPageSizeFromFormat($format);
2129
			$this->fwPt = $pf[0];
2130
			$this->fhPt = $pf[1];
2131
		} else {
2132
			// the boundaries of the physical medium on which the page shall be displayed or printed
2133
			if (isset($format['MediaBox'])) {
2134
				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false, $this->k, $this->pagedim);
2135
				$this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2136
				$this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2137
			} else {
2138
				if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2139
					$pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2140
				} else {
2141
					if (!isset($format['format'])) {
2142
						// default value
2143
						$format['format'] = 'A4';
2144
					}
2145
					$pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2146
				}
2147
				$this->fwPt = $pf[0];
2148
				$this->fhPt = $pf[1];
2149
				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2150
			}
2151
			// the visible region of default user space
2152
			if (isset($format['CropBox'])) {
2153
				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false, $this->k, $this->pagedim);
2154
			}
2155
			// the region to which the contents of the page shall be clipped when output in a production environment
2156
			if (isset($format['BleedBox'])) {
2157
				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false, $this->k, $this->pagedim);
2158
			}
2159
			// the intended dimensions of the finished page after trimming
2160
			if (isset($format['TrimBox'])) {
2161
				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false, $this->k, $this->pagedim);
2162
			}
2163
			// the page's meaningful content (including potential white space)
2164
			if (isset($format['ArtBox'])) {
2165
				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false, $this->k, $this->pagedim);
2166
			}
2167
			// specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2168
			if (isset($format['BoxColorInfo'])) {
2169
				$this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2170
			}
2171
			if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2172
				// The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2173
				$this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2174
			}
2175
			if (isset($format['PZ'])) {
2176
				// The page's preferred zoom (magnification) factor
2177
				$this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2178
			}
2179
			if (isset($format['trans'])) {
2180
				// The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2181
				if (isset($format['trans']['Dur'])) {
2182
					// The page's display duration
2183
					$this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2184
				}
2185
				$stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2186
				if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2187
					// The transition style that shall be used when moving to this page from another during a presentation
2188
					$this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2189
					$valid_effect = array('Split', 'Blinds');
2190
					$valid_vals = array('H', 'V');
2191
					if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2192
						$this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2193
					}
2194
					$valid_effect = array('Split', 'Box', 'Fly');
2195
					$valid_vals = array('I', 'O');
2196
					if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2197
						$this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2198
					}
2199
					$valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2200
					if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2201
						if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2202
							OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2203
							OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2204
							$this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2205
						}
2206
					}
2207
					if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2208
						$this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2209
					}
2210
					if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2211
						$this->pagedim[$this->page]['trans']['B'] = 'true';
2212
					}
2213
				} else {
2214
					$this->pagedim[$this->page]['trans']['S'] = 'R';
2215
				}
2216
				if (isset($format['trans']['D'])) {
2217
					// The duration of the transition effect, in seconds
2218
					$this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2219
				} else {
2220
					$this->pagedim[$this->page]['trans']['D'] = 1;
2221
				}
2222
			}
2223
		}
2224
		$this->setPageOrientation($orientation);
2225
	}
2226
 
2227
	/**
2228
	 * Set page orientation.
2229
	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
2230
	 * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off.
2231
	 * @param $bottommargin (float) bottom margin of the page.
2232
	 * @public
2233
	 * @since 3.0.015 (2008-06-06)
2234
	 */
2235
	public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2236
		if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2237
			// the boundaries of the physical medium on which the page shall be displayed or printed
2238
			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2239
		}
2240
		if (!isset($this->pagedim[$this->page]['CropBox'])) {
2241
			// the visible region of default user space
2242
			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true, $this->k, $this->pagedim);
2243
		}
2244
		if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2245
			// the region to which the contents of the page shall be clipped when output in a production environment
2246
			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2247
		}
2248
		if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2249
			// the intended dimensions of the finished page after trimming
2250
			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2251
		}
2252
		if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2253
			// the page's meaningful content (including potential white space)
2254
			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2255
		}
2256
		if (!isset($this->pagedim[$this->page]['Rotate'])) {
2257
			// The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2258
			$this->pagedim[$this->page]['Rotate'] = 0;
2259
		}
2260
		if (!isset($this->pagedim[$this->page]['PZ'])) {
2261
			// The page's preferred zoom (magnification) factor
2262
			$this->pagedim[$this->page]['PZ'] = 1;
2263
		}
2264
		if ($this->fwPt > $this->fhPt) {
2265
			// landscape
2266
			$default_orientation = 'L';
2267
		} else {
2268
			// portrait
2269
			$default_orientation = 'P';
2270
		}
2271
		$valid_orientations = array('P', 'L');
2272
		if (empty($orientation)) {
2273
			$orientation = $default_orientation;
2274
		} else {
2275
			$orientation = strtoupper($orientation{0});
2276
		}
2277
		if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2278
			$this->CurOrientation = $orientation;
2279
			$this->wPt = $this->fhPt;
2280
			$this->hPt = $this->fwPt;
2281
		} else {
2282
			$this->CurOrientation = $default_orientation;
2283
			$this->wPt = $this->fwPt;
2284
			$this->hPt = $this->fhPt;
2285
		}
2286
		if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2287
			// swap X and Y coordinates (change page orientation)
2288
			$this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2289
		}
2290
		$this->w = ($this->wPt / $this->k);
2291
		$this->h = ($this->hPt / $this->k);
2292
		if (TCPDF_STATIC::empty_string($autopagebreak)) {
2293
			if (isset($this->AutoPageBreak)) {
2294
				$autopagebreak = $this->AutoPageBreak;
2295
			} else {
2296
				$autopagebreak = true;
2297
			}
2298
		}
2299
		if (TCPDF_STATIC::empty_string($bottommargin)) {
2300
			if (isset($this->bMargin)) {
2301
				$bottommargin = $this->bMargin;
2302
			} else {
2303
				// default value = 2 cm
2304
				$bottommargin = 2 * 28.35 / $this->k;
2305
			}
2306
		}
2307
		$this->SetAutoPageBreak($autopagebreak, $bottommargin);
2308
		// store page dimensions
2309
		$this->pagedim[$this->page]['w'] = $this->wPt;
2310
		$this->pagedim[$this->page]['h'] = $this->hPt;
2311
		$this->pagedim[$this->page]['wk'] = $this->w;
2312
		$this->pagedim[$this->page]['hk'] = $this->h;
2313
		$this->pagedim[$this->page]['tm'] = $this->tMargin;
2314
		$this->pagedim[$this->page]['bm'] = $bottommargin;
2315
		$this->pagedim[$this->page]['lm'] = $this->lMargin;
2316
		$this->pagedim[$this->page]['rm'] = $this->rMargin;
2317
		$this->pagedim[$this->page]['pb'] = $autopagebreak;
2318
		$this->pagedim[$this->page]['or'] = $this->CurOrientation;
2319
		$this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2320
		$this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2321
	}
2322
 
2323
	/**
2324
	 * Set regular expression to detect withespaces or word separators.
2325
	 * The pattern delimiter must be the forward-slash character "/".
2326
	 * Some example patterns are:
2327
	 * <pre>
2328
	 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
2329
	 * Unicode and PCRE unicode support: "/[^\S\P{Z}\xa0]/u"
2330
	 * Unicode and PCRE unicode support in Chinese mode: "/[^\S\P{Z}\P{Lo}\xa0]/u"
2331
	 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
2332
	 * "\p{Z}" or "\p{Separator}": any kind of Unicode whitespace or invisible separator.
2333
	 * "\p{Lo}" or "\p{Other_Letter}": a Unicode letter or ideograph that does not have lowercase and uppercase variants.
2334
	 * "\p{Lo}" is needed for Chinese characters because are packed next to each other without spaces in between.
2335
	 * </pre>
2336
	 * @param $re (string) regular expression (leave empty for default).
2337
	 * @public
2338
	 * @since 4.6.016 (2009-06-15)
2339
	 */
2340
	public function setSpacesRE($re='/[^\S\xa0]/') {
2341
		$this->re_spaces = $re;
2342
		$re_parts = explode('/', $re);
2343
		// get pattern parts
2344
		$this->re_space = array();
2345
		if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2346
			$this->re_space['p'] = $re_parts[1];
2347
		} else {
2348
			$this->re_space['p'] = '[\s]';
2349
		}
2350
		// set pattern modifiers
2351
		if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2352
			$this->re_space['m'] = $re_parts[2];
2353
		} else {
2354
			$this->re_space['m'] = '';
2355
		}
2356
	}
2357
 
2358
	/**
2359
	 * Enable or disable Right-To-Left language mode
2360
	 * @param $enable (Boolean) if true enable Right-To-Left language mode.
2361
	 * @param $resetx (Boolean) if true reset the X position on direction change.
2362
	 * @public
2363
	 * @since 2.0.000 (2008-01-03)
2364
	 */
2365
	public function setRTL($enable, $resetx=true) {
2366
		$enable = $enable ? true : false;
2367
		$resetx = ($resetx AND ($enable != $this->rtl));
2368
		$this->rtl = $enable;
2369
		$this->tmprtl = false;
2370
		if ($resetx) {
2371
			$this->Ln(0);
2372
		}
2373
	}
2374
 
2375
	/**
2376
	 * Return the RTL status
2377
	 * @return boolean
2378
	 * @public
2379
	 * @since 4.0.012 (2008-07-24)
2380
	 */
2381
	public function getRTL() {
2382
		return $this->rtl;
2383
	}
2384
 
2385
	/**
2386
	 * Force temporary RTL language direction
2387
	 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
2388
	 * @public
2389
	 * @since 2.1.000 (2008-01-09)
2390
	 */
2391
	public function setTempRTL($mode) {
2392
		$newmode = false;
2393
		switch (strtoupper($mode)) {
2394
			case 'LTR':
2395
			case 'L': {
2396
				if ($this->rtl) {
2397
					$newmode = 'L';
2398
				}
2399
				break;
2400
			}
2401
			case 'RTL':
2402
			case 'R': {
2403
				if (!$this->rtl) {
2404
					$newmode = 'R';
2405
				}
2406
				break;
2407
			}
2408
			case false:
2409
			default: {
2410
				$newmode = false;
2411
				break;
2412
			}
2413
		}
2414
		$this->tmprtl = $newmode;
2415
	}
2416
 
2417
	/**
2418
	 * Return the current temporary RTL status
2419
	 * @return boolean
2420
	 * @public
2421
	 * @since 4.8.014 (2009-11-04)
2422
	 */
2423
	public function isRTLTextDir() {
2424
		return ($this->rtl OR ($this->tmprtl == 'R'));
2425
	}
2426
 
2427
	/**
2428
	 * Set the last cell height.
2429
	 * @param $h (float) cell height.
2430
	 * @author Nicola Asuni
2431
	 * @public
2432
	 * @since 1.53.0.TC034
2433
	 */
2434
	public function setLastH($h) {
2435
		$this->lasth = $h;
2436
	}
2437
 
2438
	/**
2439
	 * Reset the last cell height.
2440
	 * @public
2441
	 * @since 5.9.000 (2010-10-03)
2442
	 */
2443
	public function resetLastH() {
2444
		$this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
2445
	}
2446
 
2447
	/**
2448
	 * Get the last cell height.
2449
	 * @return last cell height
2450
	 * @public
2451
	 * @since 4.0.017 (2008-08-05)
2452
	 */
2453
	public function getLastH() {
2454
		return $this->lasth;
2455
	}
2456
 
2457
	/**
2458
	 * Set the adjusting factor to convert pixels to user units.
2459
	 * @param $scale (float) adjusting factor to convert pixels to user units.
2460
	 * @author Nicola Asuni
2461
	 * @public
2462
	 * @since 1.5.2
2463
	 */
2464
	public function setImageScale($scale) {
2465
		$this->imgscale = $scale;
2466
	}
2467
 
2468
	/**
2469
	 * Returns the adjusting factor to convert pixels to user units.
2470
	 * @return float adjusting factor to convert pixels to user units.
2471
	 * @author Nicola Asuni
2472
	 * @public
2473
	 * @since 1.5.2
2474
	 */
2475
	public function getImageScale() {
2476
		return $this->imgscale;
2477
	}
2478
 
2479
	/**
2480
	 * Returns an array of page dimensions:
2481
	 * <ul><li>$this->pagedim[$this->page]['w'] = page width in points</li><li>$this->pagedim[$this->page]['h'] = height in points</li><li>$this->pagedim[$this->page]['wk'] = page width in user units</li><li>$this->pagedim[$this->page]['hk'] = page height in user units</li><li>$this->pagedim[$this->page]['tm'] = top margin</li><li>$this->pagedim[$this->page]['bm'] = bottom margin</li><li>$this->pagedim[$this->page]['lm'] = left margin</li><li>$this->pagedim[$this->page]['rm'] = right margin</li><li>$this->pagedim[$this->page]['pb'] = auto page break</li><li>$this->pagedim[$this->page]['or'] = page orientation</li><li>$this->pagedim[$this->page]['olm'] = original left margin</li><li>$this->pagedim[$this->page]['orm'] = original right margin</li><li>$this->pagedim[$this->page]['Rotate'] = The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li><li>$this->pagedim[$this->page]['PZ'] = The page's preferred zoom (magnification) factor.</li><li>$this->pagedim[$this->page]['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation<ul><li>$this->pagedim[$this->page]['trans']['Dur'] = The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li><li>$this->pagedim[$this->page]['trans']['S'] = transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li><li>$this->pagedim[$this->page]['trans']['D'] = The duration of the transition effect, in seconds.</li><li>$this->pagedim[$this->page]['trans']['Dm'] = (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li><li>$this->pagedim[$this->page]['trans']['M'] = (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li><li>$this->pagedim[$this->page]['trans']['Di'] = (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li><li>$this->pagedim[$this->page]['trans']['SS'] = (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0. </li><li>$this->pagedim[$this->page]['trans']['B'] = (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li></ul></li><li>$this->pagedim[$this->page]['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed<ul><li>$this->pagedim[$this->page]['MediaBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['CropBox'] : the visible region of default user space<ul><li>$this->pagedim[$this->page]['CropBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment<ul><li>$this->pagedim[$this->page]['BleedBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['TrimBox'] : the intended dimensions of the finished page after trimming<ul><li>$this->pagedim[$this->page]['TrimBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['ArtBox'] : the extent of the page's meaningful content<ul><li>$this->pagedim[$this->page]['ArtBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['ury'] = upper-right y coordinate in points</li></ul></li></ul>
2482
	 * @param $pagenum (int) page number (empty = current page)
2483
	 * @return array of page dimensions.
2484
	 * @author Nicola Asuni
2485
	 * @public
2486
	 * @since 4.5.027 (2009-03-16)
2487
	 */
2488
	public function getPageDimensions($pagenum='') {
2489
		if (empty($pagenum)) {
2490
			$pagenum = $this->page;
2491
		}
2492
		return $this->pagedim[$pagenum];
2493
	}
2494
 
2495
	/**
2496
	 * Returns the page width in units.
2497
	 * @param $pagenum (int) page number (empty = current page)
2498
	 * @return int page width.
2499
	 * @author Nicola Asuni
2500
	 * @public
2501
	 * @since 1.5.2
2502
	 * @see getPageDimensions()
2503
	 */
2504
	public function getPageWidth($pagenum='') {
2505
		if (empty($pagenum)) {
2506
			return $this->w;
2507
		}
2508
		return $this->pagedim[$pagenum]['w'];
2509
	}
2510
 
2511
	/**
2512
	 * Returns the page height in units.
2513
	 * @param $pagenum (int) page number (empty = current page)
2514
	 * @return int page height.
2515
	 * @author Nicola Asuni
2516
	 * @public
2517
	 * @since 1.5.2
2518
	 * @see getPageDimensions()
2519
	 */
2520
	public function getPageHeight($pagenum='') {
2521
		if (empty($pagenum)) {
2522
			return $this->h;
2523
		}
2524
		return $this->pagedim[$pagenum]['h'];
2525
	}
2526
 
2527
	/**
2528
	 * Returns the page break margin.
2529
	 * @param $pagenum (int) page number (empty = current page)
2530
	 * @return int page break margin.
2531
	 * @author Nicola Asuni
2532
	 * @public
2533
	 * @since 1.5.2
2534
	 * @see getPageDimensions()
2535
	 */
2536
	public function getBreakMargin($pagenum='') {
2537
		if (empty($pagenum)) {
2538
			return $this->bMargin;
2539
		}
2540
		return $this->pagedim[$pagenum]['bm'];
2541
	}
2542
 
2543
	/**
2544
	 * Returns the scale factor (number of points in user unit).
2545
	 * @return int scale factor.
2546
	 * @author Nicola Asuni
2547
	 * @public
2548
	 * @since 1.5.2
2549
	 */
2550
	public function getScaleFactor() {
2551
		return $this->k;
2552
	}
2553
 
2554
	/**
2555
	 * Defines the left, top and right margins.
2556
	 * @param $left (float) Left margin.
2557
	 * @param $top (float) Top margin.
2558
	 * @param $right (float) Right margin. Default value is the left one.
2559
	 * @param $keepmargins (boolean) if true overwrites the default page margins
2560
	 * @public
2561
	 * @since 1.0
2562
	 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
2563
	 */
2564
	public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2565
		//Set left, top and right margins
2566
		$this->lMargin = $left;
2567
		$this->tMargin = $top;
2568
		if ($right == -1) {
2569
			$right = $left;
2570
		}
2571
		$this->rMargin = $right;
2572
		if ($keepmargins) {
2573
			// overwrite original values
2574
			$this->original_lMargin = $this->lMargin;
2575
			$this->original_rMargin = $this->rMargin;
2576
		}
2577
	}
2578
 
2579
	/**
2580
	 * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
2581
	 * @param $margin (float) The margin.
2582
	 * @public
2583
	 * @since 1.4
2584
	 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2585
	 */
2586
	public function SetLeftMargin($margin) {
2587
		//Set left margin
2588
		$this->lMargin = $margin;
2589
		if (($this->page > 0) AND ($this->x < $margin)) {
2590
			$this->x = $margin;
2591
		}
2592
	}
2593
 
2594
	/**
2595
	 * Defines the top margin. The method can be called before creating the first page.
2596
	 * @param $margin (float) The margin.
2597
	 * @public
2598
	 * @since 1.5
2599
	 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2600
	 */
2601
	public function SetTopMargin($margin) {
2602
		//Set top margin
2603
		$this->tMargin = $margin;
2604
		if (($this->page > 0) AND ($this->y < $margin)) {
2605
			$this->y = $margin;
2606
		}
2607
	}
2608
 
2609
	/**
2610
	 * Defines the right margin. The method can be called before creating the first page.
2611
	 * @param $margin (float) The margin.
2612
	 * @public
2613
	 * @since 1.5
2614
	 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2615
	 */
2616
	public function SetRightMargin($margin) {
2617
		$this->rMargin = $margin;
2618
		if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2619
			$this->x = $this->w - $margin;
2620
		}
2621
	}
2622
 
2623
	/**
2624
	 * Set the same internal Cell padding for top, right, bottom, left-
2625
	 * @param $pad (float) internal padding.
2626
	 * @public
2627
	 * @since 2.1.000 (2008-01-09)
2628
	 * @see getCellPaddings(), setCellPaddings()
2629
	 */
2630
	public function SetCellPadding($pad) {
2631
		if ($pad >= 0) {
2632
			$this->cell_padding['L'] = $pad;
2633
			$this->cell_padding['T'] = $pad;
2634
			$this->cell_padding['R'] = $pad;
2635
			$this->cell_padding['B'] = $pad;
2636
		}
2637
	}
2638
 
2639
	/**
2640
	 * Set the internal Cell paddings.
2641
	 * @param $left (float) left padding
2642
	 * @param $top (float) top padding
2643
	 * @param $right (float) right padding
2644
	 * @param $bottom (float) bottom padding
2645
	 * @public
2646
	 * @since 5.9.000 (2010-10-03)
2647
	 * @see getCellPaddings(), SetCellPadding()
2648
	 */
2649
	public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2650
		if (($left !== '') AND ($left >= 0)) {
2651
			$this->cell_padding['L'] = $left;
2652
		}
2653
		if (($top !== '') AND ($top >= 0)) {
2654
			$this->cell_padding['T'] = $top;
2655
		}
2656
		if (($right !== '') AND ($right >= 0)) {
2657
			$this->cell_padding['R'] = $right;
2658
		}
2659
		if (($bottom !== '') AND ($bottom >= 0)) {
2660
			$this->cell_padding['B'] = $bottom;
2661
		}
2662
	}
2663
 
2664
	/**
2665
	 * Get the internal Cell padding array.
2666
	 * @return array of padding values
2667
	 * @public
2668
	 * @since 5.9.000 (2010-10-03)
2669
	 * @see setCellPaddings(), SetCellPadding()
2670
	 */
2671
	public function getCellPaddings() {
2672
		return $this->cell_padding;
2673
	}
2674
 
2675
	/**
2676
	 * Set the internal Cell margins.
2677
	 * @param $left (float) left margin
2678
	 * @param $top (float) top margin
2679
	 * @param $right (float) right margin
2680
	 * @param $bottom (float) bottom margin
2681
	 * @public
2682
	 * @since 5.9.000 (2010-10-03)
2683
	 * @see getCellMargins()
2684
	 */
2685
	public function setCellMargins($left='', $top='', $right='', $bottom='') {
2686
		if (($left !== '') AND ($left >= 0)) {
2687
			$this->cell_margin['L'] = $left;
2688
		}
2689
		if (($top !== '') AND ($top >= 0)) {
2690
			$this->cell_margin['T'] = $top;
2691
		}
2692
		if (($right !== '') AND ($right >= 0)) {
2693
			$this->cell_margin['R'] = $right;
2694
		}
2695
		if (($bottom !== '') AND ($bottom >= 0)) {
2696
			$this->cell_margin['B'] = $bottom;
2697
		}
2698
	}
2699
 
2700
	/**
2701
	 * Get the internal Cell margin array.
2702
	 * @return array of margin values
2703
	 * @public
2704
	 * @since 5.9.000 (2010-10-03)
2705
	 * @see setCellMargins()
2706
	 */
2707
	public function getCellMargins() {
2708
		return $this->cell_margin;
2709
	}
2710
 
2711
	/**
2712
	 * Adjust the internal Cell padding array to take account of the line width.
2713
	 * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
2714
	 * @return array of adjustments
2715
	 * @public
2716
	 * @since 5.9.000 (2010-10-03)
2717
	 */
2718
	protected function adjustCellPadding($brd=0) {
2719
		if (empty($brd)) {
2720
			return;
2721
		}
2722
		if (is_string($brd)) {
2723
			// convert string to array
2724
			$slen = strlen($brd);
2725
			$newbrd = array();
2726
			for ($i = 0; $i < $slen; ++$i) {
2727
				$newbrd[$brd[$i]] = true;
2728
			}
2729
			$brd = $newbrd;
2730
		} elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2731
			$brd = array('LRTB' => true);
2732
		}
2733
		if (!is_array($brd)) {
2734
			return;
2735
		}
2736
		// store current cell padding
2737
		$cp = $this->cell_padding;
2738
		// select border mode
2739
		if (isset($brd['mode'])) {
2740
			$mode = $brd['mode'];
2741
			unset($brd['mode']);
2742
		} else {
2743
			$mode = 'normal';
2744
		}
2745
		// process borders
2746
		foreach ($brd as $border => $style) {
2747
			$line_width = $this->LineWidth;
2748
			if (is_array($style) AND isset($style['width'])) {
2749
				// get border width
2750
				$line_width = $style['width'];
2751
			}
2752
			$adj = 0; // line width inside the cell
2753
			switch ($mode) {
2754
				case 'ext': {
2755
					$adj = 0;
2756
					break;
2757
				}
2758
				case 'int': {
2759
					$adj = $line_width;
2760
					break;
2761
				}
2762
				case 'normal':
2763
				default: {
2764
					$adj = ($line_width / 2);
2765
					break;
2766
				}
2767
			}
2768
			// correct internal cell padding if required to avoid overlap between text and lines
2769
			if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2770
				$this->cell_padding['T'] = $adj;
2771
			}
2772
			if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2773
				$this->cell_padding['R'] = $adj;
2774
			}
2775
			if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2776
				$this->cell_padding['B'] = $adj;
2777
			}
2778
			if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2779
				$this->cell_padding['L'] = $adj;
2780
			}
2781
		}
2782
		return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L']));
2783
	}
2784
 
2785
	/**
2786
	 * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
2787
	 * @param $auto (boolean) Boolean indicating if mode should be on or off.
2788
	 * @param $margin (float) Distance from the bottom of the page.
2789
	 * @public
2790
	 * @since 1.0
2791
	 * @see Cell(), MultiCell(), AcceptPageBreak()
2792
	 */
2793
	public function SetAutoPageBreak($auto, $margin=0) {
2794
		$this->AutoPageBreak = $auto ? true : false;
2795
		$this->bMargin = $margin;
2796
		$this->PageBreakTrigger = $this->h - $margin;
2797
	}
2798
 
2799
	/**
2800
	 * Return the auto-page-break mode (true or false).
2801
	 * @return boolean auto-page-break mode
2802
	 * @public
2803
	 * @since 5.9.088
2804
	 */
2805
	public function getAutoPageBreak() {
2806
		return $this->AutoPageBreak;
2807
	}
2808
 
2809
	/**
2810
	 * Defines the way the document is to be displayed by the viewer.
2811
	 * @param $zoom (mixed) The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
2812
	 * @param $layout (string) The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
2813
	 * @param $mode (string) A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
2814
	 * @public
2815
	 * @since 1.2
2816
	 */
2817
	public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2818
		if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2819
			$this->ZoomMode = $zoom;
2820
		} else {
2821
			$this->Error('Incorrect zoom display mode: '.$zoom);
2822
		}
2823
		$this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2824
		$this->PageMode = TCPDF_STATIC::getPageMode($mode);
2825
	}
2826
 
2827
	/**
2828
	 * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
2829
	 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
2830
	 * @param $compress (boolean) Boolean indicating if compression must be enabled.
2831
	 * @public
2832
	 * @since 1.4
2833
	 */
2834
	public function SetCompression($compress=true) {
2835
		if (function_exists('gzcompress')) {
2836
			$this->compress = $compress ? true : false;
2837
		} else {
2838
			$this->compress = false;
2839
		}
2840
	}
2841
 
2842
	/**
2843
	 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
2844
	 * @param $mode (boolean) If true force sRGB output intent.
2845
	 * @public
2846
	 * @since 5.9.121 (2011-09-28)
2847
	 */
2848
	public function setSRGBmode($mode=false) {
2849
		$this->force_srgb = $mode ? true : false;
2850
	}
2851
 
2852
	/**
2853
	 * Turn on/off Unicode mode for document information dictionary (meta tags).
2854
	 * This has effect only when unicode mode is set to false.
2855
	 * @param $unicode (boolean) if true set the meta information in Unicode
2856
	 * @since 5.9.027 (2010-12-01)
2857
	 * @public
2858
	 */
2859
	public function SetDocInfoUnicode($unicode=true) {
2860
		$this->docinfounicode = $unicode ? true : false;
2861
	}
2862
 
2863
	/**
2864
	 * Defines the title of the document.
2865
	 * @param $title (string) The title.
2866
	 * @public
2867
	 * @since 1.2
2868
	 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2869
	 */
2870
	public function SetTitle($title) {
2871
		$this->title = $title;
2872
	}
2873
 
2874
	/**
2875
	 * Defines the subject of the document.
2876
	 * @param $subject (string) The subject.
2877
	 * @public
2878
	 * @since 1.2
2879
	 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2880
	 */
2881
	public function SetSubject($subject) {
2882
		$this->subject = $subject;
2883
	}
2884
 
2885
	/**
2886
	 * Defines the author of the document.
2887
	 * @param $author (string) The name of the author.
2888
	 * @public
2889
	 * @since 1.2
2890
	 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2891
	 */
2892
	public function SetAuthor($author) {
2893
		$this->author = $author;
2894
	}
2895
 
2896
	/**
2897
	 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2898
	 * @param $keywords (string) The list of keywords.
2899
	 * @public
2900
	 * @since 1.2
2901
	 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2902
	 */
2903
	public function SetKeywords($keywords) {
2904
		$this->keywords = $keywords;
2905
	}
2906
 
2907
	/**
2908
	 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2909
	 * @param $creator (string) The name of the creator.
2910
	 * @public
2911
	 * @since 1.2
2912
	 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2913
	 */
2914
	public function SetCreator($creator) {
2915
		$this->creator = $creator;
2916
	}
2917
 
2918
	/**
2919
	 * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.
2920
	 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
2921
	 * @param $msg (string) The error message
2922
	 * @public
2923
	 * @since 1.0
2924
	 */
2925
	public function Error($msg) {
2926
		// unset all class variables
2927
		$this->_destroy(true);
2928
		$phpmainver = PHP_VERSION;
2929
		// exit program and print error
2930
		if ((intval($phpmainver[0]) < 5) OR !defined('K_TCPDF_THROW_EXCEPTION_ERROR') OR !K_TCPDF_THROW_EXCEPTION_ERROR) {
2931
			die('<strong>TCPDF ERROR: </strong>'.$msg);
2932
		} else {
2933
			throw new Exception('TCPDF ERROR: '.$msg);
2934
		}
2935
	}
2936
 
2937
	/**
2938
	 * This method begins the generation of the PDF document.
2939
	 * It is not necessary to call it explicitly because AddPage() does it automatically.
2940
	 * Note: no page is created by this method
2941
	 * @public
2942
	 * @since 1.0
2943
	 * @see AddPage(), Close()
2944
	 */
2945
	public function Open() {
2946
		$this->state = 1;
2947
	}
2948
 
2949
	/**
2950
	 * Terminates the PDF document.
2951
	 * It is not necessary to call this method explicitly because Output() does it automatically.
2952
	 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2953
	 * @public
2954
	 * @since 1.0
2955
	 * @see Open(), Output()
2956
	 */
2957
	public function Close() {
2958
		if ($this->state == 3) {
2959
			return;
2960
		}
2961
		if ($this->page == 0) {
2962
			$this->AddPage();
2963
		}
2964
		$this->endLayer();
2965
		if ($this->tcpdflink) {
2966
			// save current graphic settings
2967
			$gvars = $this->getGraphicVars();
2968
			$this->setEqualColumns();
2969
			$this->lastpage(true);
2970
			$this->SetAutoPageBreak(false);
2971
			$this->x = 0;
2972
			$this->y = $this->h - (1 / $this->k);
2973
			$this->lMargin = 0;
2974
			$this->_out('q');
2975
			$font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2976
			$this->SetFont($font, '', 1);
2977
			$this->setTextRenderingMode(0, false, false);
2978
			$msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
2979
			$lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2980
			$this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2981
			$this->_out('Q');
2982
			// restore graphic settings
2983
			$this->setGraphicVars($gvars);
2984
		}
2985
		// close page
2986
		$this->endPage();
2987
		// close document
2988
		$this->_enddoc();
2989
		// unset all class variables (except critical ones)
2990
		$this->_destroy(false);
2991
	}
2992
 
2993
	/**
2994
	 * Move pointer at the specified document page and update page dimensions.
2995
	 * @param $pnum (int) page number (1 ... numpages)
2996
	 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
2997
	 * @public
2998
	 * @since 2.1.000 (2008-01-07)
2999
	 * @see getPage(), lastpage(), getNumPages()
3000
	 */
3001
	public function setPage($pnum, $resetmargins=false) {
3002
		if (($pnum == $this->page) AND ($this->state == 2)) {
3003
			return;
3004
		}
3005
		if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3006
			$this->state = 2;
3007
			// save current graphic settings
3008
			//$gvars = $this->getGraphicVars();
3009
			$oldpage = $this->page;
3010
			$this->page = $pnum;
3011
			$this->wPt = $this->pagedim[$this->page]['w'];
3012
			$this->hPt = $this->pagedim[$this->page]['h'];
3013
			$this->w = $this->pagedim[$this->page]['wk'];
3014
			$this->h = $this->pagedim[$this->page]['hk'];
3015
			$this->tMargin = $this->pagedim[$this->page]['tm'];
3016
			$this->bMargin = $this->pagedim[$this->page]['bm'];
3017
			$this->original_lMargin = $this->pagedim[$this->page]['olm'];
3018
			$this->original_rMargin = $this->pagedim[$this->page]['orm'];
3019
			$this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3020
			$this->CurOrientation = $this->pagedim[$this->page]['or'];
3021
			$this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3022
			// restore graphic settings
3023
			//$this->setGraphicVars($gvars);
3024
			if ($resetmargins) {
3025
				$this->lMargin = $this->pagedim[$this->page]['olm'];
3026
				$this->rMargin = $this->pagedim[$this->page]['orm'];
3027
				$this->SetY($this->tMargin);
3028
			} else {
3029
				// account for booklet mode
3030
				if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3031
					$deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3032
					$this->lMargin += $deltam;
3033
					$this->rMargin -= $deltam;
3034
				}
3035
			}
3036
		} else {
3037
			$this->Error('Wrong page number on setPage() function: '.$pnum);
3038
		}
3039
	}
3040
 
3041
	/**
3042
	 * Reset pointer to the last document page.
3043
	 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3044
	 * @public
3045
	 * @since 2.0.000 (2008-01-04)
3046
	 * @see setPage(), getPage(), getNumPages()
3047
	 */
3048
	public function lastPage($resetmargins=false) {
3049
		$this->setPage($this->getNumPages(), $resetmargins);
3050
	}
3051
 
3052
	/**
3053
	 * Get current document page number.
3054
	 * @return int page number
3055
	 * @public
3056
	 * @since 2.1.000 (2008-01-07)
3057
	 * @see setPage(), lastpage(), getNumPages()
3058
	 */
3059
	public function getPage() {
3060
		return $this->page;
3061
	}
3062
 
3063
	/**
3064
	 * Get the total number of insered pages.
3065
	 * @return int number of pages
3066
	 * @public
3067
	 * @since 2.1.000 (2008-01-07)
3068
	 * @see setPage(), getPage(), lastpage()
3069
	 */
3070
	public function getNumPages() {
3071
		return $this->numpages;
3072
	}
3073
 
3074
	/**
3075
	 * Adds a new TOC (Table Of Content) page to the document.
3076
	 * @param $orientation (string) page orientation.
3077
	 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3078
	 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3079
	 * @public
3080
	 * @since 5.0.001 (2010-05-06)
3081
	 * @see AddPage(), startPage(), endPage(), endTOCPage()
3082
	 */
3083
	public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3084
		$this->AddPage($orientation, $format, $keepmargins, true);
3085
	}
3086
 
3087
	/**
3088
	 * Terminate the current TOC (Table Of Content) page
3089
	 * @public
3090
	 * @since 5.0.001 (2010-05-06)
3091
	 * @see AddPage(), startPage(), endPage(), addTOCPage()
3092
	 */
3093
	public function endTOCPage() {
3094
		$this->endPage(true);
3095
	}
3096
 
3097
	/**
3098
	 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
3099
	 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3100
	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3101
	 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3102
	 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3103
	 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3104
	 * @public
3105
	 * @since 1.0
3106
	 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3107
	 */
3108
	public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3109
		if ($this->inxobj) {
3110
			// we are inside an XObject template
3111
			return;
3112
		}
3113
		if (!isset($this->original_lMargin) OR $keepmargins) {
3114
			$this->original_lMargin = $this->lMargin;
3115
		}
3116
		if (!isset($this->original_rMargin) OR $keepmargins) {
3117
			$this->original_rMargin = $this->rMargin;
3118
		}
3119
		// terminate previous page
3120
		$this->endPage();
3121
		// start new page
3122
		$this->startPage($orientation, $format, $tocpage);
3123
	}
3124
 
3125
	/**
3126
	 * Terminate the current page
3127
	 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3128
	 * @public
3129
	 * @since 4.2.010 (2008-11-14)
3130
	 * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3131
	 */
3132
	public function endPage($tocpage=false) {
3133
		// check if page is already closed
3134
		if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3135
			return;
3136
		}
3137
		// print page footer
3138
		$this->setFooter();
3139
		// close page
3140
		$this->_endpage();
3141
		// mark page as closed
3142
		$this->pageopen[$this->page] = false;
3143
		if ($tocpage) {
3144
			$this->tocpage = false;
3145
		}
3146
	}
3147
 
3148
	/**
3149
	 * Starts a new page to the document. The page must be closed using the endPage() function.
3150
	 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3151
	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3152
	 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3153
	 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3154
	 * @since 4.2.010 (2008-11-14)
3155
	 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3156
	 * @public
3157
	 */
3158
	public function startPage($orientation='', $format='', $tocpage=false) {
3159
		if ($tocpage) {
3160
			$this->tocpage = true;
3161
		}
3162
		// move page numbers of documents to be attached
3163
		if ($this->tocpage) {
3164
			// move reference to unexistent pages (used for page attachments)
3165
			// adjust outlines
3166
			$tmpoutlines = $this->outlines;
3167
			foreach ($tmpoutlines as $key => $outline) {
3168
				if ($outline['p'] > $this->numpages) {
3169
					$this->outlines[$key]['p'] = ($outline['p'] + 1);
3170
				}
3171
			}
3172
			// adjust dests
3173
			$tmpdests = $this->dests;
3174
			foreach ($tmpdests as $key => $dest) {
3175
				if ($dest['p'] > $this->numpages) {
3176
					$this->dests[$key]['p'] = ($dest['p'] + 1);
3177
				}
3178
			}
3179
			// adjust links
3180
			$tmplinks = $this->links;
3181
			foreach ($tmplinks as $key => $link) {
3182
				if ($link[0] > $this->numpages) {
3183
					$this->links[$key][0] = ($link[0] + 1);
3184
				}
3185
			}
3186
		}
3187
		if ($this->numpages > $this->page) {
3188
			// this page has been already added
3189
			$this->setPage($this->page + 1);
3190
			$this->SetY($this->tMargin);
3191
			return;
3192
		}
3193
		// start a new page
3194
		if ($this->state == 0) {
3195
			$this->Open();
3196
		}
3197
		++$this->numpages;
3198
		$this->swapMargins($this->booklet);
3199
		// save current graphic settings
3200
		$gvars = $this->getGraphicVars();
3201
		// start new page
3202
		$this->_beginpage($orientation, $format);
3203
		// mark page as open
3204
		$this->pageopen[$this->page] = true;
3205
		// restore graphic settings
3206
		$this->setGraphicVars($gvars);
3207
		// mark this point
3208
		$this->setPageMark();
3209
		// print page header
3210
		$this->setHeader();
3211
		// restore graphic settings
3212
		$this->setGraphicVars($gvars);
3213
		// mark this point
3214
		$this->setPageMark();
3215
		// print table header (if any)
3216
		$this->setTableHeader();
3217
		// set mark for empty page check
3218
		$this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3219
	}
3220
 
3221
	/**
3222
	 * Set start-writing mark on current page stream used to put borders and fills.
3223
	 * Borders and fills are always created after content and inserted on the position marked by this method.
3224
	 * This function must be called after calling Image() function for a background image.
3225
	 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3226
	 * @public
3227
	 * @since 4.0.016 (2008-07-30)
3228
	 */
3229
	public function setPageMark() {
3230
		$this->intmrk[$this->page] = $this->pagelen[$this->page];
3231
		$this->bordermrk[$this->page] = $this->intmrk[$this->page];
3232
		$this->setContentMark();
3233
	}
3234
 
3235
	/**
3236
	 * Set start-writing mark on selected page.
3237
	 * Borders and fills are always created after content and inserted on the position marked by this method.
3238
	 * @param $page (int) page number (default is the current page)
3239
	 * @protected
3240
	 * @since 4.6.021 (2009-07-20)
3241
	 */
3242
	protected function setContentMark($page=0) {
3243
		if ($page <= 0) {
3244
			$page = $this->page;
3245
		}
3246
		if (isset($this->footerlen[$page])) {
3247
			$this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3248
		} else {
3249
			$this->cntmrk[$page] = $this->pagelen[$page];
3250
		}
3251
	}
3252
 
3253
	/**
3254
	 * Set header data.
3255
	 * @param $ln (string) header image logo
3256
	 * @param $lw (string) header image logo width in mm
3257
	 * @param $ht (string) string to print as title on document header
3258
	 * @param $hs (string) string to print on document header
3259
	 * @param $tc (array) RGB array color for text.
3260
	 * @param $lc (array) RGB array color for line.
3261
	 * @public
3262
	 */
3263
	public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3264
		$this->header_logo = $ln;
3265
		$this->header_logo_width = $lw;
3266
		$this->header_title = $ht;
3267
		$this->header_string = $hs;
3268
		$this->header_text_color = $tc;
3269
		$this->header_line_color = $lc;
3270
	}
3271
 
3272
	/**
3273
	 * Set footer data.
3274
	 * @param $tc (array) RGB array color for text.
3275
	 * @param $lc (array) RGB array color for line.
3276
	 * @public
3277
	 */
3278
	public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3279
		$this->footer_text_color = $tc;
3280
		$this->footer_line_color = $lc;
3281
	}
3282
 
3283
	/**
3284
	 * Returns header data:
3285
	 * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
3286
	 * @return array()
3287
	 * @public
3288
	 * @since 4.0.012 (2008-07-24)
3289
	 */
3290
	public function getHeaderData() {
3291
		$ret = array();
3292
		$ret['logo'] = $this->header_logo;
3293
		$ret['logo_width'] = $this->header_logo_width;
3294
		$ret['title'] = $this->header_title;
3295
		$ret['string'] = $this->header_string;
3296
		$ret['text_color'] = $this->header_text_color;
3297
		$ret['line_color'] = $this->header_line_color;
3298
		return $ret;
3299
	}
3300
 
3301
	/**
3302
	 * Set header margin.
3303
	 * (minimum distance between header and top page margin)
3304
	 * @param $hm (int) distance in user units
3305
	 * @public
3306
	 */
3307
	public function setHeaderMargin($hm=10) {
3308
		$this->header_margin = $hm;
3309
	}
3310
 
3311
	/**
3312
	 * Returns header margin in user units.
3313
	 * @return float
3314
	 * @since 4.0.012 (2008-07-24)
3315
	 * @public
3316
	 */
3317
	public function getHeaderMargin() {
3318
		return $this->header_margin;
3319
	}
3320
 
3321
	/**
3322
	 * Set footer margin.
3323
	 * (minimum distance between footer and bottom page margin)
3324
	 * @param $fm (int) distance in user units
3325
	 * @public
3326
	 */
3327
	public function setFooterMargin($fm=10) {
3328
		$this->footer_margin = $fm;
3329
	}
3330
 
3331
	/**
3332
	 * Returns footer margin in user units.
3333
	 * @return float
3334
	 * @since 4.0.012 (2008-07-24)
3335
	 * @public
3336
	 */
3337
	public function getFooterMargin() {
3338
		return $this->footer_margin;
3339
	}
3340
	/**
3341
	 * Set a flag to print page header.
3342
	 * @param $val (boolean) set to true to print the page header (default), false otherwise.
3343
	 * @public
3344
	 */
3345
	public function setPrintHeader($val=true) {
3346
		$this->print_header = $val ? true : false;
3347
	}
3348
 
3349
	/**
3350
	 * Set a flag to print page footer.
3351
	 * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3352
	 * @public
3353
	 */
3354
	public function setPrintFooter($val=true) {
3355
		$this->print_footer = $val ? true : false;
3356
	}
3357
 
3358
	/**
3359
	 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3360
	 * @return float
3361
	 * @public
3362
	 */
3363
	public function getImageRBX() {
3364
		return $this->img_rb_x;
3365
	}
3366
 
3367
	/**
3368
	 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3369
	 * @return float
3370
	 * @public
3371
	 */
3372
	public function getImageRBY() {
3373
		return $this->img_rb_y;
3374
	}
3375
 
3376
	/**
3377
	 * Reset the xobject template used by Header() method.
3378
	 * @public
3379
	 */
3380
	public function resetHeaderTemplate() {
3381
		$this->header_xobjid = -1;
3382
	}
3383
 
3384
	/**
3385
	 * Set a flag to automatically reset the xobject template used by Header() method at each page.
3386
	 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
3387
	 * @public
3388
	 */
3389
	public function setHeaderTemplateAutoreset($val=true) {
3390
		$this->header_xobj_autoreset = $val ? true : false;
3391
	}
3392
 
3393
	/**
3394
	 * This method is used to render the page header.
3395
	 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3396
	 * @public
3397
	 */
3398
	public function Header() {
3399
		if ($this->header_xobjid < 0) {
3400
			// start a new XObject Template
3401
			$this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3402
			$headerfont = $this->getHeaderFont();
3403
			$headerdata = $this->getHeaderData();
3404
			$this->y = $this->header_margin;
3405
			if ($this->rtl) {
3406
				$this->x = $this->w - $this->original_rMargin;
3407
			} else {
3408
				$this->x = $this->original_lMargin;
3409
			}
3410
			if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3411
				$imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3412
				if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3413
					$this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3414
				} elseif ($imgtype == 'svg') {
3415
					$this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3416
				} else {
3417
					$this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3418
				}
3419
				$imgy = $this->getImageRBY();
3420
			} else {
3421
				$imgy = $this->y;
3422
			}
3423
			$cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2);
3424
			// set starting margin for text data cell
3425
			if ($this->getRTL()) {
3426
				$header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3427
			} else {
3428
				$header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3429
			}
3430
			$cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3431
			$this->SetTextColorArray($this->header_text_color);
3432
			// header title
3433
			$this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3434
			$this->SetX($header_x);
3435
			$this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3436
			// header string
3437
			$this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3438
			$this->SetX($header_x);
3439
			$this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3440
			// print an ending header line
3441
			$this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3442
			$this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3443
			if ($this->rtl) {
3444
				$this->SetX($this->original_rMargin);
3445
			} else {
3446
				$this->SetX($this->original_lMargin);
3447
			}
3448
			$this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3449
			$this->endTemplate();
3450
		}
3451
		// print header template
3452
		$x = 0;
3453
		$dx = 0;
3454
		if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3455
			// adjust margins for booklet mode
3456
			$dx = ($this->original_lMargin - $this->original_rMargin);
3457
		}
3458
		if ($this->rtl) {
3459
			$x = $this->w + $dx;
3460
		} else {
3461
			$x = 0 + $dx;
3462
		}
3463
		$this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3464
		if ($this->header_xobj_autoreset) {
3465
			// reset header xobject template at each page
3466
			$this->header_xobjid = -1;
3467
		}
3468
	}
3469
 
3470
	/**
3471
	 * This method is used to render the page footer.
3472
	 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3473
	 * @public
3474
	 */
3475
	public function Footer() {
3476
		$cur_y = $this->y;
3477
		$this->SetTextColorArray($this->footer_text_color);
3478
		//set style for cell border
3479
		$line_width = (0.85 / $this->k);
3480
		$this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3481
		//print document barcode
3482
		$barcode = $this->getBarcode();
3483
		if (!empty($barcode)) {
3484
			$this->Ln($line_width);
3485
			$barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3486
			$style = array(
3487
				'position' => $this->rtl?'R':'L',
3488
				'align' => $this->rtl?'R':'L',
3489
				'stretch' => false,
3490
				'fitwidth' => true,
3491
				'cellfitalign' => '',
3492
				'border' => false,
3493
				'padding' => 0,
3494
				'fgcolor' => array(0,0,0),
3495
				'bgcolor' => false,
3496
				'text' => false
3497
			);
3498
			$this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3499
		}
3500
		$w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3501
		if (empty($this->pagegroups)) {
3502
			$pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3503
		} else {
3504
			$pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3505
		}
3506
		$this->SetY($cur_y);
3507
		//Print page number
3508
		if ($this->getRTL()) {
3509
			$this->SetX($this->original_rMargin);
3510
			$this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3511
		} else {
3512
			$this->SetX($this->original_lMargin);
3513
			$this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3514
		}
3515
	}
3516
 
3517
	/**
3518
	 * This method is used to render the page header.
3519
	 * @protected
3520
	 * @since 4.0.012 (2008-07-24)
3521
	 */
3522
	protected function setHeader() {
3523
		if (!$this->print_header OR ($this->state != 2)) {
3524
			return;
3525
		}
3526
		$this->InHeader = true;
3527
		$this->setGraphicVars($this->default_graphic_vars);
3528
		$temp_thead = $this->thead;
3529
		$temp_theadMargins = $this->theadMargins;
3530
		$lasth = $this->lasth;
3531
		$this->_out('q');
3532
		$this->rMargin = $this->original_rMargin;
3533
		$this->lMargin = $this->original_lMargin;
3534
		$this->SetCellPadding(0);
3535
		//set current position
3536
		if ($this->rtl) {
3537
			$this->SetXY($this->original_rMargin, $this->header_margin);
3538
		} else {
3539
			$this->SetXY($this->original_lMargin, $this->header_margin);
3540
		}
3541
		$this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3542
		$this->Header();
3543
		//restore position
3544
		if ($this->rtl) {
3545
			$this->SetXY($this->original_rMargin, $this->tMargin);
3546
		} else {
3547
			$this->SetXY($this->original_lMargin, $this->tMargin);
3548
		}
3549
		$this->_out('Q');
3550
		$this->lasth = $lasth;
3551
		$this->thead = $temp_thead;
3552
		$this->theadMargins = $temp_theadMargins;
3553
		$this->newline = false;
3554
		$this->InHeader = false;
3555
	}
3556
 
3557
	/**
3558
	 * This method is used to render the page footer.
3559
	 * @protected
3560
	 * @since 4.0.012 (2008-07-24)
3561
	 */
3562
	protected function setFooter() {
3563
		if ($this->state != 2) {
3564
			return;
3565
		}
3566
		$this->InFooter = true;
3567
		// save current graphic settings
3568
		$gvars = $this->getGraphicVars();
3569
		// mark this point
3570
		$this->footerpos[$this->page] = $this->pagelen[$this->page];
3571
		$this->_out("\n");
3572
		if ($this->print_footer) {
3573
			$this->setGraphicVars($this->default_graphic_vars);
3574
			$this->current_column = 0;
3575
			$this->num_columns = 1;
3576
			$temp_thead = $this->thead;
3577
			$temp_theadMargins = $this->theadMargins;
3578
			$lasth = $this->lasth;
3579
			$this->_out('q');
3580
			$this->rMargin = $this->original_rMargin;
3581
			$this->lMargin = $this->original_lMargin;
3582
			$this->SetCellPadding(0);
3583
			//set current position
3584
			$footer_y = $this->h - $this->footer_margin;
3585
			if ($this->rtl) {
3586
				$this->SetXY($this->original_rMargin, $footer_y);
3587
			} else {
3588
				$this->SetXY($this->original_lMargin, $footer_y);
3589
			}
3590
			$this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3591
			$this->Footer();
3592
			//restore position
3593
			if ($this->rtl) {
3594
				$this->SetXY($this->original_rMargin, $this->tMargin);
3595
			} else {
3596
				$this->SetXY($this->original_lMargin, $this->tMargin);
3597
			}
3598
			$this->_out('Q');
3599
			$this->lasth = $lasth;
3600
			$this->thead = $temp_thead;
3601
			$this->theadMargins = $temp_theadMargins;
3602
		}
3603
		// restore graphic settings
3604
		$this->setGraphicVars($gvars);
3605
		$this->current_column = $gvars['current_column'];
3606
		$this->num_columns = $gvars['num_columns'];
3607
		// calculate footer length
3608
		$this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3609
		$this->InFooter = false;
3610
	}
3611
 
3612
	/**
3613
	 * Check if we are on the page body (excluding page header and footer).
3614
	 * @return true if we are not in page header nor in page footer, false otherwise.
3615
	 * @protected
3616
	 * @since 5.9.091 (2011-06-15)
3617
	 */
3618
	protected function inPageBody() {
3619
		return (($this->InHeader === false) AND ($this->InFooter === false));
3620
	}
3621
 
3622
	/**
3623
	 * This method is used to render the table header on new page (if any).
3624
	 * @protected
3625
	 * @since 4.5.030 (2009-03-25)
3626
	 */
3627
	protected function setTableHeader() {
3628
		if ($this->num_columns > 1) {
3629
			// multi column mode
3630
			return;
3631
		}
3632
		if (isset($this->theadMargins['top'])) {
3633
			// restore the original top-margin
3634
			$this->tMargin = $this->theadMargins['top'];
3635
			$this->pagedim[$this->page]['tm'] = $this->tMargin;
3636
			$this->y = $this->tMargin;
3637
		}
3638
		if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3639
			// set margins
3640
			$prev_lMargin = $this->lMargin;
3641
			$prev_rMargin = $this->rMargin;
3642
			$prev_cell_padding = $this->cell_padding;
3643
			$this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3644
			$this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3645
			$this->cell_padding = $this->theadMargins['cell_padding'];
3646
			if ($this->rtl) {
3647
				$this->x = $this->w - $this->rMargin;
3648
			} else {
3649
				$this->x = $this->lMargin;
3650
			}
3651
			// account for special "cell" mode
3652
			if ($this->theadMargins['cell']) {
3653
				if ($this->rtl) {
3654
					$this->x -= $this->cell_padding['R'];
3655
				} else {
3656
					$this->x += $this->cell_padding['L'];
3657
				}
3658
			}
3659
			// print table header
3660
			$this->writeHTML($this->thead, false, false, false, false, '');
3661
			// set new top margin to skip the table headers
3662
			if (!isset($this->theadMargins['top'])) {
3663
				$this->theadMargins['top'] = $this->tMargin;
3664
			}
3665
			// store end of header position
3666
			if (!isset($this->columns[0]['th'])) {
3667
				$this->columns[0]['th'] = array();
3668
			}
3669
			$this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3670
			$this->tMargin = $this->y;
3671
			$this->pagedim[$this->page]['tm'] = $this->tMargin;
3672
			$this->lasth = 0;
3673
			$this->lMargin = $prev_lMargin;
3674
			$this->rMargin = $prev_rMargin;
3675
			$this->cell_padding = $prev_cell_padding;
3676
		}
3677
	}
3678
 
3679
	/**
3680
	 * Returns the current page number.
3681
	 * @return int page number
3682
	 * @public
3683
	 * @since 1.0
3684
	 * @see getAliasNbPages()
3685
	 */
3686
	public function PageNo() {
3687
		return $this->page;
3688
	}
3689
 
3690
	/**
3691
	 * Defines a new spot color.
3692
	 * It can be expressed in RGB components or gray scale.
3693
	 * The method can be called before the first page is created and the value is retained from page to page.
3694
	 * @param $name (string) Full name of the spot color.
3695
	 * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
3696
	 * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
3697
	 * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
3698
	 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
3699
	 * @public
3700
	 * @since 4.0.024 (2008-09-12)
3701
	 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3702
	 */
3703
	public function AddSpotColor($name, $c, $m, $y, $k) {
3704
		if (!isset($this->spot_colors[$name])) {
3705
			$i = (1 + count($this->spot_colors));
3706
			$this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3707
		}
3708
	}
3709
 
3710
	/**
3711
	 * Set the spot color for the specified type ('draw', 'fill', 'text').
3712
	 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3713
	 * @param $name (string) Name of the spot color.
3714
	 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3715
	 * @return (string) PDF color command.
3716
	 * @public
3717
	 * @since 5.9.125 (2011-10-03)
3718
	 */
3719
	public function setSpotColor($type, $name, $tint=100) {
3720
		$spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3721
		if ($spotcolor === false) {
3722
			$this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.');
3723
		}
3724
		$tint = (max(0, min(100, $tint)) / 100);
3725
		$pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3726
		switch ($type) {
3727
			case 'draw': {
3728
				$pdfcolor .= sprintf('CS %F SCN', $tint);
3729
				$this->DrawColor = $pdfcolor;
3730
				$this->strokecolor = $spotcolor;
3731
				break;
3732
			}
3733
			case 'fill': {
3734
				$pdfcolor .= sprintf('cs %F scn', $tint);
3735
				$this->FillColor = $pdfcolor;
3736
				$this->bgcolor = $spotcolor;
3737
				break;
3738
			}
3739
			case 'text': {
3740
				$pdfcolor .= sprintf('cs %F scn', $tint);
3741
				$this->TextColor = $pdfcolor;
3742
				$this->fgcolor = $spotcolor;
3743
				break;
3744
			}
3745
		}
3746
		$this->ColorFlag = ($this->FillColor != $this->TextColor);
3747
		if ($this->state == 2) {
3748
			$this->_out($pdfcolor);
3749
		}
3750
		if ($this->inxobj) {
3751
			// we are inside an XObject template
3752
			$this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3753
		}
3754
		return $pdfcolor;
3755
	}
3756
 
3757
	/**
3758
	 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3759
	 * @param $name (string) Name of the spot color.
3760
	 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3761
	 * @public
3762
	 * @since 4.0.024 (2008-09-12)
3763
	 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3764
	 */
3765
	public function SetDrawSpotColor($name, $tint=100) {
3766
		$this->setSpotColor('draw', $name, $tint);
3767
	}
3768
 
3769
	/**
3770
	 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3771
	 * @param $name (string) Name of the spot color.
3772
	 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3773
	 * @public
3774
	 * @since 4.0.024 (2008-09-12)
3775
	 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3776
	 */
3777
	public function SetFillSpotColor($name, $tint=100) {
3778
		$this->setSpotColor('fill', $name, $tint);
3779
	}
3780
 
3781
	/**
3782
	 * Defines the spot color used for text.
3783
	 * @param $name (string) Name of the spot color.
3784
	 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3785
	 * @public
3786
	 * @since 4.0.024 (2008-09-12)
3787
	 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3788
	 */
3789
	public function SetTextSpotColor($name, $tint=100) {
3790
		$this->setSpotColor('text', $name, $tint);
3791
	}
3792
 
3793
	/**
3794
	 * Set the color array for the specified type ('draw', 'fill', 'text').
3795
	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3796
	 * The method can be called before the first page is created and the value is retained from page to page.
3797
	 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3798
	 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3799
	 * @param $ret (boolean) If true do not send the PDF command.
3800
	 * @return (string) The PDF command or empty string.
3801
	 * @public
3802
	 * @since 3.1.000 (2008-06-11)
3803
	 */
3804
	public function setColorArray($type, $color, $ret=false) {
3805
		if (is_array($color)) {
3806
			$color = array_values($color);
3807
			// component: grey, RGB red or CMYK cyan
3808
			$c = isset($color[0]) ? $color[0] : -1;
3809
			// component: RGB green or CMYK magenta
3810
			$m = isset($color[1]) ? $color[1] : -1;
3811
			// component: RGB blue or CMYK yellow
3812
			$y = isset($color[2]) ? $color[2] : -1;
3813
			// component: CMYK black
3814
			$k = isset($color[3]) ? $color[3] : -1;
3815
			// color name
3816
			$name = isset($color[4]) ? $color[4] : '';
3817
			if ($c >= 0) {
3818
				return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3819
			}
3820
		}
3821
		return '';
3822
	}
3823
 
3824
	/**
3825
	 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3826
	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3827
	 * The method can be called before the first page is created and the value is retained from page to page.
3828
	 * @param $color (array) Array of colors (1, 3 or 4 values).
3829
	 * @param $ret (boolean) If true do not send the PDF command.
3830
	 * @return string the PDF command
3831
	 * @public
3832
	 * @since 3.1.000 (2008-06-11)
3833
	 * @see SetDrawColor()
3834
	 */
3835
	public function SetDrawColorArray($color, $ret=false) {
3836
		return $this->setColorArray('draw', $color, $ret);
3837
	}
3838
 
3839
	/**
3840
	 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3841
	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3842
	 * The method can be called before the first page is created and the value is retained from page to page.
3843
	 * @param $color (array) Array of colors (1, 3 or 4 values).
3844
	 * @param $ret (boolean) If true do not send the PDF command.
3845
	 * @public
3846
	 * @since 3.1.000 (2008-6-11)
3847
	 * @see SetFillColor()
3848
	 */
3849
	public function SetFillColorArray($color, $ret=false) {
3850
		return $this->setColorArray('fill', $color, $ret);
3851
	}
3852
 
3853
	/**
3854
	 * Defines the color used for text. It can be expressed in RGB components or gray scale.
3855
	 * The method can be called before the first page is created and the value is retained from page to page.
3856
	 * @param $color (array) Array of colors (1, 3 or 4 values).
3857
	 * @param $ret (boolean) If true do not send the PDF command.
3858
	 * @public
3859
	 * @since 3.1.000 (2008-6-11)
3860
	 * @see SetFillColor()
3861
	 */
3862
	public function SetTextColorArray($color, $ret=false) {
3863
		return $this->setColorArray('text', $color, $ret);
3864
	}
3865
 
3866
	/**
3867
	 * Defines the color used by the specified type ('draw', 'fill', 'text').
3868
	 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3869
	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3870
	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3871
	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3872
	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3873
	 * @param $ret (boolean) If true do not send the command.
3874
	 * @param $name (string) spot color name (if any)
3875
	 * @return (string) The PDF command or empty string.
3876
	 * @public
3877
	 * @since 5.9.125 (2011-10-03)
3878
	 */
3879
	public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3880
		// set default values
3881
		if (!is_numeric($col1)) {
3882
			$col1 = 0;
3883
		}
3884
		if (!is_numeric($col2)) {
3885
			$col2 = -1;
3886
		}
3887
		if (!is_numeric($col3)) {
3888
			$col3 = -1;
3889
		}
3890
		if (!is_numeric($col4)) {
3891
			$col4 = -1;
3892
		}
3893
		// set color by case
3894
		$suffix = '';
3895
		if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3896
			// Grey scale
3897
			$col1 = max(0, min(255, $col1));
3898
			$intcolor = array('G' => $col1);
3899
			$pdfcolor = sprintf('%F ', ($col1 / 255));
3900
			$suffix = 'g';
3901
		} elseif ($col4 == -1) {
3902
			// RGB
3903
			$col1 = max(0, min(255, $col1));
3904
			$col2 = max(0, min(255, $col2));
3905
			$col3 = max(0, min(255, $col3));
3906
			$intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3907
			$pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3908
			$suffix = 'rg';
3909
		} else {
3910
			$col1 = max(0, min(100, $col1));
3911
			$col2 = max(0, min(100, $col2));
3912
			$col3 = max(0, min(100, $col3));
3913
			$col4 = max(0, min(100, $col4));
3914
			if (empty($name)) {
3915
				// CMYK
3916
				$intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3917
				$pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3918
				$suffix = 'k';
3919
			} else {
3920
				// SPOT COLOR
3921
				$intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3922
				$this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3923
				$pdfcolor = $this->setSpotColor($type, $name, 100);
3924
			}
3925
		}
3926
		switch ($type) {
3927
			case 'draw': {
3928
				$pdfcolor .= strtoupper($suffix);
3929
				$this->DrawColor = $pdfcolor;
3930
				$this->strokecolor = $intcolor;
3931
				break;
3932
			}
3933
			case 'fill': {
3934
				$pdfcolor .= $suffix;
3935
				$this->FillColor = $pdfcolor;
3936
				$this->bgcolor = $intcolor;
3937
				break;
3938
			}
3939
			case 'text': {
3940
				$pdfcolor .= $suffix;
3941
				$this->TextColor = $pdfcolor;
3942
				$this->fgcolor = $intcolor;
3943
				break;
3944
			}
3945
		}
3946
		$this->ColorFlag = ($this->FillColor != $this->TextColor);
3947
		if (($type != 'text') AND ($this->state == 2)) {
3948
			if (!$ret) {
3949
				$this->_out($pdfcolor);
3950
			}
3951
			return $pdfcolor;
3952
		}
3953
		return '';
3954
	}
3955
 
3956
	/**
3957
	 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
3958
	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3959
	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3960
	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3961
	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3962
	 * @param $ret (boolean) If true do not send the command.
3963
	 * @param $name (string) spot color name (if any)
3964
	 * @return string the PDF command
3965
	 * @public
3966
	 * @since 1.3
3967
	 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
3968
	 */
3969
	public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3970
		return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3971
	}
3972
 
3973
	/**
3974
	 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
3975
	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3976
	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3977
	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3978
	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3979
	 * @param $ret (boolean) If true do not send the command.
3980
	 * @param $name (string) Spot color name (if any).
3981
	 * @return (string) The PDF command.
3982
	 * @public
3983
	 * @since 1.3
3984
	 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
3985
	 */
3986
	public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3987
		return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
3988
	}
3989
 
3990
	/**
3991
	 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
3992
	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3993
	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3994
	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3995
	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3996
	 * @param $ret (boolean) If true do not send the command.
3997
	 * @param $name (string) Spot color name (if any).
3998
	 * @return (string) Empty string.
3999
	 * @public
4000
	 * @since 1.3
4001
	 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4002
	 */
4003
	public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4004
		return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4005
	}
4006
 
4007
	/**
4008
	 * Returns the length of a string in user unit. A font must be selected.<br>
4009
	 * @param $s (string) The string whose length is to be computed
4010
	 * @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
4011
	 * @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4012
	 * @param $fontsize (float) Font size in points. The default value is the current size.
4013
	 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4014
	 * @return mixed int total string length or array of characted widths
4015
	 * @author Nicola Asuni
4016
	 * @public
4017
	 * @since 1.2
4018
	 */
4019
	public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4020
		return $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont), $s, $this->tmprtl, $this->isunicode, $this->CurrentFont), $fontname, $fontstyle, $fontsize, $getarray);
4021
	}
4022
 
4023
	/**
4024
	 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4025
	 * @param $sa (string) The array of chars whose total length is to be computed
4026
	 * @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
4027
	 * @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4028
	 * @param $fontsize (float) Font size in points. The default value is the current size.
4029
	 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4030
	 * @return mixed int total string length or array of characted widths
4031
	 * @author Nicola Asuni
4032
	 * @public
4033
	 * @since 2.4.000 (2008-03-06)
4034
	 */
4035
	public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4036
		// store current values
4037
		if (!TCPDF_STATIC::empty_string($fontname)) {
4038
			$prev_FontFamily = $this->FontFamily;
4039
			$prev_FontStyle = $this->FontStyle;
4040
			$prev_FontSizePt = $this->FontSizePt;
4041
			$this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4042
		}
4043
		// convert UTF-8 array to Latin1 if required
4044
		if ($this->isunicode AND (!$this->isUnicodeFont())) {
4045
			$sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa);
4046
		}
4047
		$w = 0; // total width
4048
		$wa = array(); // array of characters widths
4049
		foreach ($sa as $ck => $char) {
4050
			// character width
4051
			$cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4052
			$wa[] = $cw;
4053
			$w += $cw;
4054
		}
4055
		// restore previous values
4056
		if (!TCPDF_STATIC::empty_string($fontname)) {
4057
			$this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4058
		}
4059
		if ($getarray) {
4060
			return $wa;
4061
		}
4062
		return $w;
4063
	}
4064
 
4065
	/**
4066
	 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4067
	 * @param $char (int) The char code whose length is to be returned
4068
	 * @param $notlast (boolean) If false ignore the font-spacing.
4069
	 * @return float char width
4070
	 * @author Nicola Asuni
4071
	 * @public
4072
	 * @since 2.4.000 (2008-03-06)
4073
	 */
4074
	public function GetCharWidth($char, $notlast=true) {
4075
		// get raw width
4076
		$chw = $this->getRawCharWidth($char);
4077
		if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4078
			// increase/decrease font spacing
4079
			$chw += $this->font_spacing;
4080
		}
4081
		if ($this->font_stretching != 100) {
4082
			// fixed stretching mode
4083
			$chw *= ($this->font_stretching / 100);
4084
		}
4085
		return $chw;
4086
	}
4087
 
4088
	/**
4089
	 * Returns the length of the char in user unit for the current font.
4090
	 * @param $char (int) The char code whose length is to be returned
4091
	 * @return float char width
4092
	 * @author Nicola Asuni
4093
	 * @public
4094
	 * @since 5.9.000 (2010-09-28)
4095
	 */
4096
	public function getRawCharWidth($char) {
4097
		if ($char == 173) {
4098
			// SHY character will not be printed
4099
			return (0);
4100
		}
4101
		if (isset($this->CurrentFont['cw'][$char])) {
4102
			$w = $this->CurrentFont['cw'][$char];
4103
		} elseif (isset($this->CurrentFont['dw'])) {
4104
			// default width
4105
			$w = $this->CurrentFont['dw'];
4106
		} elseif (isset($this->CurrentFont['cw'][32])) {
4107
			// default width
4108
			$w = $this->CurrentFont['cw'][32];
4109
		} else {
4110
			$w = 600;
4111
		}
4112
		return $this->getAbsFontMeasure($w);
4113
	}
4114
 
4115
	/**
4116
	 * Returns the numbero of characters in a string.
4117
	 * @param $s (string) The input string.
4118
	 * @return int number of characters
4119
	 * @public
4120
	 * @since 2.0.0001 (2008-01-07)
4121
	 */
4122
	public function GetNumChars($s) {
4123
		if ($this->isUnicodeFont()) {
4124
			return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4125
		}
4126
		return strlen($s);
4127
	}
4128
 
4129
	/**
4130
	 * Fill the list of available fonts ($this->fontlist).
4131
	 * @protected
4132
	 * @since 4.0.013 (2008-07-28)
4133
	 */
4134
	protected function getFontsList() {
4135
		if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4136
			while (($file = readdir($fontsdir)) !== false) {
4137
				if (substr($file, -4) == '.php') {
4138
					array_push($this->fontlist, strtolower(basename($file, '.php')));
4139
				}
4140
			}
4141
			closedir($fontsdir);
4142
		}
4143
	}
4144
 
4145
	/**
4146
	 * Returns the unicode caracter specified by the value
4147
	 * @param $c (int) UTF-8 value
4148
	 * @return Returns the specified character.
4149
	 * @since 2.3.000 (2008-03-05)
4150
	 * @public
4151
	 * @deprecated
4152
	 */
4153
	public function unichr($c) {
4154
		return TCPDF_FONTS::unichr($c, $this->isunicode);
4155
	}
4156
 
4157
	/**
4158
	 * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
4159
	 * @param $fontfile (string) Font file (full path).
4160
	 * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional.
4161
	 * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats.
4162
	 * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font.
4163
	 * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
4164
	 * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1).
4165
	 * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4.
4166
	 * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
4167
	 * @return (string) TCPDF font name.
4168
	 * @author Nicola Asuni
4169
	 * @since 5.9.123 (2010-09-30)
4170
	 * @public
4171
	 * @deprecated
4172
	 */
4173
	public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4174
		return TCPDF_FONTS::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4175
	}
4176
 
4177
	/**
4178
	 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4179
	 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4180
	 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
4181
	 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4182
	 * @param $style (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
4183
	 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4184
	 * @return array containing the font data, or false in case of error.
4185
	 * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4186
	 * @public
4187
	 * @since 1.5
4188
	 * @see SetFont(), setFontSubsetting()
4189
	 */
4190
	public function AddFont($family, $style='', $fontfile='', $subset='default') {
4191
		if ($subset === 'default') {
4192
			$subset = $this->font_subsetting;
4193
		}
4194
		if ($this->pdfa_mode) {
4195
			$subset = false;
4196
		}
4197
		if (TCPDF_STATIC::empty_string($family)) {
4198
			if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4199
				$family = $this->FontFamily;
4200
			} else {
4201
				$this->Error('Empty font family');
4202
			}
4203
		}
4204
		// move embedded styles on $style
4205
		if (substr($family, -1) == 'I') {
4206
			$style .= 'I';
4207
			$family = substr($family, 0, -1);
4208
		}
4209
		if (substr($family, -1) == 'B') {
4210
			$style .= 'B';
4211
			$family = substr($family, 0, -1);
4212
		}
4213
		// normalize family name
4214
		$family = strtolower($family);
4215
		if ((!$this->isunicode) AND ($family == 'arial')) {
4216
			$family = 'helvetica';
4217
		}
4218
		if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4219
			$style = '';
4220
		}
4221
		if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4222
			// all fonts must be embedded
4223
			$family = 'pdfa'.$family;
4224
		}
4225
		$tempstyle = strtoupper($style);
4226
		$style = '';
4227
		// underline
4228
		if (strpos($tempstyle, 'U') !== false) {
4229
			$this->underline = true;
4230
		} else {
4231
			$this->underline = false;
4232
		}
4233
		// line-through (deleted)
4234
		if (strpos($tempstyle, 'D') !== false) {
4235
			$this->linethrough = true;
4236
		} else {
4237
			$this->linethrough = false;
4238
		}
4239
		// overline
4240
		if (strpos($tempstyle, 'O') !== false) {
4241
			$this->overline = true;
4242
		} else {
4243
			$this->overline = false;
4244
		}
4245
		// bold
4246
		if (strpos($tempstyle, 'B') !== false) {
4247
			$style .= 'B';
4248
		}
4249
		// oblique
4250
		if (strpos($tempstyle, 'I') !== false) {
4251
			$style .= 'I';
4252
		}
4253
		$bistyle = $style;
4254
		$fontkey = $family.$style;
4255
		$font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4256
		$fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4257
		// check if the font has been already added
4258
		$fb = $this->getFontBuffer($fontkey);
4259
		if ($fb !== false) {
4260
			if ($this->inxobj) {
4261
				// we are inside an XObject template
4262
				$this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4263
			}
4264
			return $fontdata;
4265
		}
4266
		// get specified font directory (if any)
4267
		$fontdir = false;
4268
		if (!TCPDF_STATIC::empty_string($fontfile)) {
4269
			$fontdir = dirname($fontfile);
4270
			if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4271
				$fontdir = '';
4272
			} else {
4273
				$fontdir .= '/';
4274
			}
4275
		}
4276
		$missing_style = false; // true when the font style variation is missing
4277
		// search and include font file
4278
		if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) {
4279
			// build a standard filenames for specified font
4280
			$tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4281
			// search files on various directories
4282
			if (($fontdir !== false) AND @file_exists($fontdir.$tmp_fontfile)) {
4283
				$fontfile = $fontdir.$tmp_fontfile;
4284
			} elseif (@file_exists(TCPDF_FONTS::_getfontpath().$tmp_fontfile)) {
4285
				$fontfile = TCPDF_FONTS::_getfontpath().$tmp_fontfile;
4286
			} elseif (@file_exists($tmp_fontfile)) {
4287
				$fontfile = $tmp_fontfile;
4288
			} elseif (!TCPDF_STATIC::empty_string($style)) {
4289
				$missing_style = true;
4290
				// try to remove the style part
4291
				$tmp_fontfile = str_replace(' ', '', $family).'.php';
4292
				if (($fontdir !== false) AND @file_exists($fontdir.$tmp_fontfile)) {
4293
					$fontfile = $fontdir.$tmp_fontfile;
4294
				} elseif (@file_exists(TCPDF_FONTS::_getfontpath().$tmp_fontfile)) {
4295
					$fontfile = TCPDF_FONTS::_getfontpath().$tmp_fontfile;
4296
				} else {
4297
					$fontfile = $tmp_fontfile;
4298
				}
4299
			}
4300
		}
4301
		// include font file
4302
		if (@file_exists($fontfile)) {
4303
			include($fontfile);
4304
		} else {
4305
			$this->Error('Could not include font definition file: '.$family.'');
4306
		}
4307
		// check font parameters
4308
		if ((!isset($type)) OR (!isset($cw))) {
4309
			$this->Error('The font definition file has a bad format: '.$fontfile.'');
4310
		}
4311
		// SET default parameters
4312
		if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4313
			$file = '';
4314
		}
4315
		if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4316
			$enc = '';
4317
		}
4318
		if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4319
			$cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4320
			$cidinfo['uni2cid'] = array();
4321
		}
4322
		if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4323
			$ctg = '';
4324
		}
4325
		if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4326
			$desc = array();
4327
		}
4328
		if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4329
			$up = -100;
4330
		}
4331
		if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4332
			$ut = 50;
4333
		}
4334
		if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4335
			$cw = array();
4336
		}
4337
		if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4338
			// set default width
4339
			if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4340
				$dw = $desc['MissingWidth'];
4341
			} elseif (isset($cw[32])) {
4342
				$dw = $cw[32];
4343
			} else {
4344
				$dw = 600;
4345
			}
4346
		}
4347
		++$this->numfonts;
4348
		if ($type == 'core') {
4349
			$name = $this->CoreFonts[$fontkey];
4350
			$subset = false;
4351
		} elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4352
			$subset = false;
4353
		} elseif ($type == 'TrueTypeUnicode') {
4354
			$enc = 'Identity-H';
4355
		} elseif ($type == 'cidfont0') {
4356
			if ($this->pdfa_mode) {
4357
				$this->Error('All fonts must be embedded in PDF/A mode!');
4358
			}
4359
		} else {
4360
			$this->Error('Unknow font type: '.$type.'');
4361
		}
4362
		// set name if unset
4363
		if (!isset($name) OR empty($name)) {
4364
			$name = $fontkey;
4365
		}
4366
		// create artificial font style variations if missing (only works with non-embedded fonts)
4367
		if (($type != 'core') AND $missing_style) {
4368
			// style variations
4369
			$styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4370
			$name .= $styles[$bistyle];
4371
			// artificial bold
4372
			if (strpos($bistyle, 'B') !== false) {
4373
				if (isset($desc['StemV'])) {
4374
					// from normal to bold
4375
					$desc['StemV'] = round($desc['StemV'] * 1.75);
4376
				} else {
4377
					// bold
4378
					$desc['StemV'] = 123;
4379
				}
4380
			}
4381
			// artificial italic
4382
			if (strpos($bistyle, 'I') !== false) {
4383
				if (isset($desc['ItalicAngle'])) {
4384
					$desc['ItalicAngle'] -= 11;
4385
				} else {
4386
					$desc['ItalicAngle'] = -11;
4387
				}
4388
				if (isset($desc['Flags'])) {
4389
					$desc['Flags'] |= 64; //bit 7
4390
				} else {
4391
					$desc['Flags'] = 64;
4392
				}
4393
			}
4394
		}
4395
		// check if the array of characters bounding boxes is defined
4396
		if (!isset($cbbox)) {
4397
			$cbbox = array();
4398
		}
4399
		// initialize subsetchars
4400
		$subsetchars = array_fill(0, 255, true);
4401
		$this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
4402
		if ($this->inxobj) {
4403
			// we are inside an XObject template
4404
			$this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4405
		}
4406
		if (isset($diff) AND (!empty($diff))) {
4407
			//Search existing encodings
4408
			$d = 0;
4409
			$nb = count($this->diffs);
4410
			for ($i=1; $i <= $nb; ++$i) {
4411
				if ($this->diffs[$i] == $diff) {
4412
					$d = $i;
4413
					break;
4414
				}
4415
			}
4416
			if ($d == 0) {
4417
				$d = $nb + 1;
4418
				$this->diffs[$d] = $diff;
4419
			}
4420
			$this->setFontSubBuffer($fontkey, 'diff', $d);
4421
		}
4422
		if (!TCPDF_STATIC::empty_string($file)) {
4423
			if (!isset($this->FontFiles[$file])) {
4424
				if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4425
					$this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4426
				} elseif ($type != 'core') {
4427
					$this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4428
				}
4429
			} else {
4430
				// update fontkeys that are sharing this font file
4431
				$this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4432
				if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4433
					$this->FontFiles[$file]['fontkeys'][] = $fontkey;
4434
				}
4435
			}
4436
		}
4437
		return $fontdata;
4438
	}
4439
 
4440
	/**
4441
	 * Sets the font used to print character strings.
4442
	 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4443
	 * The method can be called before the first page is created and the font is retained from page to page.
4444
	 * If you just wish to change the current font size, it is simpler to call SetFontSize().
4445
	 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
4446
	 * @param $family (string) Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
4447
	 * @param $style (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
4448
	 * @param $size (float) Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
4449
	 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4450
	 * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4451
	 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4452
	 * @author Nicola Asuni
4453
	 * @public
4454
	 * @since 1.0
4455
	 * @see AddFont(), SetFontSize()
4456
	 */
4457
	public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4458
		//Select a font; size given in points
4459
		if ($size === null) {
4460
			$size = $this->FontSizePt;
4461
		}
4462
		if ($size < 0) {
4463
			$size = 0;
4464
		}
4465
		// try to add font (if not already added)
4466
		$fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4467
		$this->FontFamily = $fontdata['family'];
4468
		$this->FontStyle = $fontdata['style'];
4469
		if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4470
			// save subset chars of the previous font
4471
			$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4472
		}
4473
		$this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4474
		$this->SetFontSize($size, $out);
4475
	}
4476
 
4477
	/**
4478
	 * Defines the size of the current font.
4479
	 * @param $size (float) The font size in points.
4480
	 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4481
	 * @public
4482
	 * @since 1.0
4483
	 * @see SetFont()
4484
	 */
4485
	public function SetFontSize($size, $out=true) {
4486
		// font size in points
4487
		$this->FontSizePt = $size;
4488
		// font size in user units
4489
		$this->FontSize = $size / $this->k;
4490
		// calculate some font metrics
4491
		if (isset($this->CurrentFont['desc']['FontBBox'])) {
4492
			$bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4493
			$font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4494
		} else {
4495
			$font_height = $size * 1.219;
4496
		}
4497
		if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4498
			$font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4499
		}
4500
		if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4501
			$font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4502
		}
4503
		if (!isset($font_ascent) AND !isset($font_descent)) {
4504
			// core font
4505
			$font_ascent = 0.76 * $font_height;
4506
			$font_descent = $font_height - $font_ascent;
4507
		} elseif (!isset($font_descent)) {
4508
			$font_descent = $font_height - $font_ascent;
4509
		} elseif (!isset($font_ascent)) {
4510
			$font_ascent = $font_height - $font_descent;
4511
		}
4512
		$this->FontAscent = ($font_ascent / $this->k);
4513
		$this->FontDescent = ($font_descent / $this->k);
4514
		if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4515
			$this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4516
		}
4517
	}
4518
 
4519
	/**
4520
	 * Returns the bounding box of the current font in user units.
4521
	 * @return array
4522
	 * @public
4523
	 * @since 5.9.152 (2012-03-23)
4524
	 */
4525
	public function getFontBBox() {
4526
		$fbbox = array();
4527
		if (isset($this->CurrentFont['desc']['FontBBox'])) {
4528
			$tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4529
			$fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4530
		} else {
4531
			// Find max width
4532
			if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4533
				$maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4534
			} else {
4535
				$maxw = 0;
4536
				if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4537
					$maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4538
				}
4539
				if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4540
					$maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4541
				}
4542
				if (isset($this->CurrentFont['dw'])) {
4543
					$maxw = max($maxw, $this->CurrentFont['dw']);
4544
				}
4545
				foreach ($this->CurrentFont['cw'] as $char => $w) {
4546
					$maxw = max($maxw, $w);
4547
				}
4548
				if ($maxw == 0) {
4549
					$maxw = 600;
4550
				}
4551
				$maxw = $this->getAbsFontMeasure($maxw);
4552
			}
4553
			$fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4554
		}
4555
		return $fbbox;
4556
	}
4557
 
4558
	/**
4559
	 * Convert a relative font measure into absolute value.
4560
	 * @param $s (int) Font measure.
4561
	 * @return float Absolute measure.
4562
	 * @since 5.9.186 (2012-09-13)
4563
	 */
4564
	public function getAbsFontMeasure($s) {
4565
		return ($s * $this->FontSize / 1000);
4566
	}
4567
 
4568
	/**
4569
	 * Returns the glyph bounding box of the specified character in the current font in user units.
4570
	 * @param $char (int) Input character code.
4571
	 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4572
	 * @since 5.9.186 (2012-09-13)
4573
	 */
4574
	public function getCharBBox($char) {
4575
		if (isset($this->CurrentFont['cbbox'][$char])) {
4576
			return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]);
4577
		}
4578
		return false;
4579
	}
4580
 
4581
	/**
4582
	 * Return the font descent value
4583
	 * @param $font (string) font name
4584
	 * @param $style (string) font style
4585
	 * @param $size (float) The size (in points)
4586
	 * @return int font descent
4587
	 * @public
4588
	 * @author Nicola Asuni
4589
	 * @since 4.9.003 (2010-03-30)
4590
	 */
4591
	public function getFontDescent($font, $style='', $size=0) {
4592
		$fontdata = $this->AddFont($font, $style);
4593
		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4594
		if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4595
			$descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4596
		} else {
4597
			$descent = (1.219 * 0.24 * $size);
4598
		}
4599
		return ($descent / $this->k);
4600
	}
4601
 
4602
	/**
4603
	 * Return the font ascent value.
4604
	 * @param $font (string) font name
4605
	 * @param $style (string) font style
4606
	 * @param $size (float) The size (in points)
4607
	 * @return int font ascent
4608
	 * @public
4609
	 * @author Nicola Asuni
4610
	 * @since 4.9.003 (2010-03-30)
4611
	 */
4612
	public function getFontAscent($font, $style='', $size=0) {
4613
		$fontdata = $this->AddFont($font, $style);
4614
		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4615
		if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4616
			$ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4617
		} else {
4618
			$ascent = 1.219 * 0.76 * $size;
4619
		}
4620
		return ($ascent / $this->k);
4621
	}
4622
 
4623
	/**
4624
	 * Return true in the character is present in the specified font.
4625
	 * @param $char (mixed) Character to check (integer value or string)
4626
	 * @param $font (string) Font name (family name).
4627
	 * @param $style (string) Font style.
4628
	 * @return (boolean) true if the char is defined, false otherwise.
4629
	 * @public
4630
	 * @since 5.9.153 (2012-03-28)
4631
	 */
4632
	public function isCharDefined($char, $font='', $style='') {
4633
		if (is_string($char)) {
4634
			// get character code
4635
			$char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4636
			$char = $char[0];
4637
		}
4638
		if (TCPDF_STATIC::empty_string($font)) {
4639
			if (TCPDF_STATIC::empty_string($style)) {
4640
				return (isset($this->CurrentFont['cw'][intval($char)]));
4641
			}
4642
			$font = $this->FontFamily;
4643
		}
4644
		$fontdata = $this->AddFont($font, $style);
4645
		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4646
		return (isset($fontinfo['cw'][intval($char)]));
4647
	}
4648
 
4649
	/**
4650
	 * Replace missing font characters on selected font with specified substitutions.
4651
	 * @param $text (string) Text to process.
4652
	 * @param $font (string) Font name (family name).
4653
	 * @param $style (string) Font style.
4654
	 * @param $subs (array) Array of possible character substitutions. The key is the character to check (integer value) and the value is a single intege value or an array of possible substitutes.
4655
	 * @return (string) Processed text.
4656
	 * @public
4657
	 * @since 5.9.153 (2012-03-28)
4658
	 */
4659
	public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4660
		if (empty($subs)) {
4661
			return $text;
4662
		}
4663
		if (TCPDF_STATIC::empty_string($font)) {
4664
			$font = $this->FontFamily;
4665
		}
4666
		$fontdata = $this->AddFont($font, $style);
4667
		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4668
		$uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4669
		foreach ($uniarr as $k => $chr) {
4670
			if (!isset($fontinfo['cw'][$chr])) {
4671
				// this character is missing on the selected font
4672
				if (isset($subs[$chr])) {
4673
					// we have available substitutions
4674
					if (is_array($subs[$chr])) {
4675
						foreach($subs[$chr] as $s) {
4676
							if (isset($fontinfo['cw'][$s])) {
4677
								$uniarr[$k] = $s;
4678
								break;
4679
							}
4680
						}
4681
					} elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4682
						$uniarr[$k] = $subs[$chr];
4683
					}
4684
				}
4685
			}
4686
		}
4687
		return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4688
	}
4689
 
4690
	/**
4691
	 * Defines the default monospaced font.
4692
	 * @param $font (string) Font name.
4693
	 * @public
4694
	 * @since 4.5.025
4695
	 */
4696
	public function SetDefaultMonospacedFont($font) {
4697
		$this->default_monospaced_font = $font;
4698
	}
4699
 
4700
	/**
4701
	 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
4702
	 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4703
	 * @public
4704
	 * @since 1.5
4705
	 * @see Cell(), Write(), Image(), Link(), SetLink()
4706
	 */
4707
	public function AddLink() {
4708
		//Create a new internal link
4709
		$n = count($this->links) + 1;
4710
		$this->links[$n] = array(0, 0);
4711
		return $n;
4712
	}
4713
 
4714
	/**
4715
	 * Defines the page and position a link points to.
4716
	 * @param $link (int) The link identifier returned by AddLink()
4717
	 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4718
	 * @param $page (int) Number of target page; -1 indicates the current page. This is the default value
4719
	 * @public
4720
	 * @since 1.5
4721
	 * @see AddLink()
4722
	 */
4723
	public function SetLink($link, $y=0, $page=-1) {
4724
		if ($y == -1) {
4725
			$y = $this->y;
4726
		}
4727
		if ($page == -1) {
4728
			$page = $this->page;
4729
		}
4730
		$this->links[$link] = array($page, $y);
4731
	}
4732
 
4733
	/**
4734
	 * Puts a link on a rectangular area of the page.
4735
	 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
4736
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4737
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4738
	 * @param $w (float) Width of the rectangle
4739
	 * @param $h (float) Height of the rectangle
4740
	 * @param $link (mixed) URL or identifier returned by AddLink()
4741
	 * @param $spaces (int) number of spaces on the text to link
4742
	 * @public
4743
	 * @since 1.5
4744
	 * @see AddLink(), Annotation(), Cell(), Write(), Image()
4745
	 */
4746
	public function Link($x, $y, $w, $h, $link, $spaces=0) {
4747
		$this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4748
	}
4749
 
4750
	/**
4751
	 * Puts a markup annotation on a rectangular area of the page.
4752
	 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
4753
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4754
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4755
	 * @param $w (float) Width of the rectangle
4756
	 * @param $h (float) Height of the rectangle
4757
	 * @param $text (string) annotation text or alternate content
4758
	 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
4759
	 * @param $spaces (int) number of spaces on the text to link
4760
	 * @public
4761
	 * @since 4.0.018 (2008-08-06)
4762
	 */
4763
	public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4764
		if ($this->inxobj) {
4765
			// store parameters for later use on template
4766
			$this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4767
			return;
4768
		}
4769
		if ($x === '') {
4770
			$x = $this->x;
4771
		}
4772
		if ($y === '') {
4773
			$y = $this->y;
4774
		}
4775
		// check page for no-write regions and adapt page margins if necessary
4776
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
4777
		// recalculate coordinates to account for graphic transformations
4778
		if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4779
			for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4780
				$maxid = count($this->transfmatrix[$i]) - 1;
4781
				for ($j=$maxid; $j >= 0; --$j) {
4782
					$ctm = $this->transfmatrix[$i][$j];
4783
					if (isset($ctm['a'])) {
4784
						$x = $x * $this->k;
4785
						$y = ($this->h - $y) * $this->k;
4786
						$w = $w * $this->k;
4787
						$h = $h * $this->k;
4788
						// top left
4789
						$xt = $x;
4790
						$yt = $y;
4791
						$x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4792
						$y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4793
						// top right
4794
						$xt = $x + $w;
4795
						$yt = $y;
4796
						$x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4797
						$y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4798
						// bottom left
4799
						$xt = $x;
4800
						$yt = $y - $h;
4801
						$x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4802
						$y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4803
						// bottom right
4804
						$xt = $x + $w;
4805
						$yt = $y - $h;
4806
						$x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4807
						$y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4808
						// new coordinates (rectangle area)
4809
						$x = min($x1, $x2, $x3, $x4);
4810
						$y = max($y1, $y2, $y3, $y4);
4811
						$w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4812
						$h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4813
						$x = $x / $this->k;
4814
						$y = $this->h - ($y / $this->k);
4815
					}
4816
				}
4817
			}
4818
		}
4819
		if ($this->page <= 0) {
4820
			$page = 1;
4821
		} else {
4822
			$page = $this->page;
4823
		}
4824
		if (!isset($this->PageAnnots[$page])) {
4825
			$this->PageAnnots[$page] = array();
4826
		}
4827
		$this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4828
		if (!$this->pdfa_mode) {
4829
			if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4830
				AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4831
				AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4832
				$this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4833
			}
4834
		}
4835
		// Add widgets annotation's icons
4836
		if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) {
4837
			$this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4838
		}
4839
		if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) {
4840
			$this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4841
		}
4842
		if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) {
4843
			$this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4844
		}
4845
	}
4846
 
4847
	/**
4848
	 * Embedd the attached files.
4849
	 * @since 4.4.000 (2008-12-07)
4850
	 * @protected
4851
	 * @see Annotation()
4852
	 */
4853
	protected function _putEmbeddedFiles() {
4854
		if ($this->pdfa_mode) {
4855
			// embedded files are not allowed in PDF/A mode
4856
			return;
4857
		}
4858
		reset($this->embeddedfiles);
4859
		foreach ($this->embeddedfiles as $filename => $filedata) {
4860
			// update name tree
4861
			$this->efnames[$filename] = $filedata['f'].' 0 R';
4862
			// embedded file specification object
4863
			$out = $this->_getobj($filedata['f'])."\n";
4864
			$out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4865
			$out .= "\n".'endobj';
4866
			$this->_out($out);
4867
			// embedded file object
4868
			$data = file_get_contents($filedata['file']);
4869
			$filter = '';
4870
			$rawsize = strlen($data);
4871
			if ($this->compress) {
4872
				$data = gzcompress($data);
4873
				$filter = ' /Filter /FlateDecode';
4874
			}
4875
			$stream = $this->_getrawstream($data, $filedata['n']);
4876
			$out = $this->_getobj($filedata['n'])."\n";
4877
			$out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4878
			$out .= ' stream'."\n".$stream."\n".'endstream';
4879
			$out .= "\n".'endobj';
4880
			$this->_out($out);
4881
		}
4882
	}
4883
 
4884
	/**
4885
	 * Prints a text cell at the specified position.
4886
	 * This method allows to place a string precisely on the page.
4887
	 * @param $x (float) Abscissa of the cell origin
4888
	 * @param $y (float) Ordinate of the cell origin
4889
	 * @param $txt (string) String to print
4890
	 * @param $fstroke (int) outline size in user units (false = disable)
4891
	 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4892
	 * @param $ffill (boolean) if true fills the text
4893
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
4894
	 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
4895
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
4896
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4897
	 * @param $link (mixed) URL or identifier returned by AddLink().
4898
	 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
4899
	 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
4900
	 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul>
4901
	 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4902
	 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
4903
	 * @public
4904
	 * @since 1.0
4905
	 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4906
	 */
4907
	public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
4908
		$textrendermode = $this->textrendermode;
4909
		$textstrokewidth = $this->textstrokewidth;
4910
		$this->setTextRenderingMode($fstroke, $ffill, $fclip);
4911
		$this->SetXY($x, $y, $rtloff);
4912
		$this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4913
		// restore previous rendering mode
4914
		$this->textrendermode = $textrendermode;
4915
		$this->textstrokewidth = $textstrokewidth;
4916
	}
4917
 
4918
	/**
4919
	 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4920
	 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4921
	 * This method is called automatically and should not be called directly by the application.
4922
	 * @return boolean
4923
	 * @public
4924
	 * @since 1.4
4925
	 * @see SetAutoPageBreak()
4926
	 */
4927
	public function AcceptPageBreak() {
4928
		if ($this->num_columns > 1) {
4929
			// multi column mode
4930
			if ($this->current_column < ($this->num_columns - 1)) {
4931
				// go to next column
4932
				$this->selectColumn($this->current_column + 1);
4933
			} elseif ($this->AutoPageBreak) {
4934
				// add a new page
4935
				$this->AddPage();
4936
				// set first column
4937
				$this->selectColumn(0);
4938
			}
4939
			// avoid page breaking from checkPageBreak()
4940
			return false;
4941
		}
4942
		return $this->AutoPageBreak;
4943
	}
4944
 
4945
	/**
4946
	 * Add page if needed.
4947
	 * @param $h (float) Cell height. Default value: 0.
4948
	 * @param $y (mixed) starting y position, leave empty for current position.
4949
	 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
4950
	 * @return boolean true in case of page break, false otherwise.
4951
	 * @since 3.2.000 (2008-07-01)
4952
	 * @protected
4953
	 */
4954
	protected function checkPageBreak($h=0, $y='', $addpage=true) {
4955
		if (TCPDF_STATIC::empty_string($y)) {
4956
			$y = $this->y;
4957
		}
4958
		$current_page = $this->page;
4959
		if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4960
			if ($addpage) {
4961
				//Automatic page break
4962
				$x = $this->x;
4963
				$this->AddPage($this->CurOrientation);
4964
				$this->y = $this->tMargin;
4965
				$oldpage = $this->page - 1;
4966
				if ($this->rtl) {
4967
					if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4968
						$this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4969
					} else {
4970
						$this->x = $x;
4971
					}
4972
				} else {
4973
					if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4974
						$this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4975
					} else {
4976
						$this->x = $x;
4977
					}
4978
				}
4979
			}
4980
			return true;
4981
		}
4982
		if ($current_page != $this->page) {
4983
			// account for columns mode
4984
			return true;
4985
		}
4986
		return false;
4987
	}
4988
 
4989
	/**
4990
	 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
4991
	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
4992
	 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
4993
	 * @param $h (float) Cell height. Default value: 0.
4994
	 * @param $txt (string) String to print. Default value: empty string.
4995
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
4996
	 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
4997
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
4998
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4999
	 * @param $link (mixed) URL or identifier returned by AddLink().
5000
	 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5001
	 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5002
	 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
5003
	 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
5004
	 * @public
5005
	 * @since 1.0
5006
	 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5007
	 */
5008
	public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5009
		$prev_cell_margin = $this->cell_margin;
5010
		$prev_cell_padding = $this->cell_padding;
5011
		$this->adjustCellPadding($border);
5012
		if (!$ignore_min_height) {
5013
			$min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5014
			if ($h < $min_cell_height) {
5015
				$h = $min_cell_height;
5016
			}
5017
		}
5018
		$this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5019
		// apply text shadow if enabled
5020
		if ($this->txtshadow['enabled']) {
5021
			// save data
5022
			$x = $this->x;
5023
			$y = $this->y;
5024
			$bc = $this->bgcolor;
5025
			$fc = $this->fgcolor;
5026
			$sc = $this->strokecolor;
5027
			$alpha = $this->alpha;
5028
			// print shadow
5029
			$this->x += $this->txtshadow['depth_w'];
5030
			$this->y += $this->txtshadow['depth_h'];
5031
			$this->SetFillColorArray($this->txtshadow['color']);
5032
			$this->SetTextColorArray($this->txtshadow['color']);
5033
			$this->SetDrawColorArray($this->txtshadow['color']);
5034
			if ($this->txtshadow['opacity'] != $alpha['CA']) {
5035
				$this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5036
			}
5037
			if ($this->state == 2) {
5038
				$this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5039
			}
5040
			//restore data
5041
			$this->x = $x;
5042
			$this->y = $y;
5043
			$this->SetFillColorArray($bc);
5044
			$this->SetTextColorArray($fc);
5045
			$this->SetDrawColorArray($sc);
5046
			if ($this->txtshadow['opacity'] != $alpha['CA']) {
5047
				$this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5048
			}
5049
		}
5050
		if ($this->state == 2) {
5051
			$this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5052
		}
5053
		$this->cell_padding = $prev_cell_padding;
5054
		$this->cell_margin = $prev_cell_margin;
5055
	}
5056
 
5057
	/**
5058
	 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
5059
	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5060
	 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5061
	 * @param $h (float) Cell height. Default value: 0.
5062
	 * @param $txt (string) String to print. Default value: empty string.
5063
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5064
	 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
5065
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
5066
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5067
	 * @param $link (mixed) URL or identifier returned by AddLink().
5068
	 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5069
	 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5070
	 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
5071
	 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul>
5072
	 * @return string containing cell code
5073
	 * @protected
5074
	 * @since 1.0
5075
	 * @see Cell()
5076
	 */
5077
	protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5078
		// replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5079
		$txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5080
		$prev_cell_margin = $this->cell_margin;
5081
		$prev_cell_padding = $this->cell_padding;
5082
		$txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5083
		$rs = ''; //string to be returned
5084
		$this->adjustCellPadding($border);
5085
		if (!$ignore_min_height) {
5086
			$min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5087
			if ($h < $min_cell_height) {
5088
				$h = $min_cell_height;
5089
			}
5090
		}
5091
		$k = $this->k;
5092
		// check page for no-write regions and adapt page margins if necessary
5093
		list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5094
		if ($this->rtl) {
5095
			$x = $this->x - $this->cell_margin['R'];
5096
		} else {
5097
			$x = $this->x + $this->cell_margin['L'];
5098
		}
5099
		$y = $this->y + $this->cell_margin['T'];
5100
		$prev_font_stretching = $this->font_stretching;
5101
		$prev_font_spacing = $this->font_spacing;
5102
		// cell vertical alignment
5103
		switch ($calign) {
5104
			case 'A': {
5105
				// font top
5106
				switch ($valign) {
5107
					case 'T': {
5108
						// top
5109
						$y -= $this->cell_padding['T'];
5110
						break;
5111
					}
5112
					case 'B': {
5113
						// bottom
5114
						$y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5115
						break;
5116
					}
5117
					default:
5118
					case 'C':
5119
					case 'M': {
5120
						// center
5121
						$y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5122
						break;
5123
					}
5124
				}
5125
				break;
5126
			}
5127
			case 'L': {
5128
				// font baseline
5129
				switch ($valign) {
5130
					case 'T': {
5131
						// top
5132
						$y -= ($this->cell_padding['T'] + $this->FontAscent);
5133
						break;
5134
					}
5135
					case 'B': {
5136
						// bottom
5137
						$y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5138
						break;
5139
					}
5140
					default:
5141
					case 'C':
5142
					case 'M': {
5143
						// center
5144
						$y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5145
						break;
5146
					}
5147
				}
5148
				break;
5149
			}
5150
			case 'D': {
5151
				// font bottom
5152
				switch ($valign) {
5153
					case 'T': {
5154
						// top
5155
						$y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5156
						break;
5157
					}
5158
					case 'B': {
5159
						// bottom
5160
						$y -= ($h - $this->cell_padding['B']);
5161
						break;
5162
					}
5163
					default:
5164
					case 'C':
5165
					case 'M': {
5166
						// center
5167
						$y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5168
						break;
5169
					}
5170
				}
5171
				break;
5172
			}
5173
			case 'B': {
5174
				// cell bottom
5175
				$y -= $h;
5176
				break;
5177
			}
5178
			case 'C':
5179
			case 'M': {
5180
				// cell center
5181
				$y -= ($h / 2);
5182
				break;
5183
			}
5184
			default:
5185
			case 'T': {
5186
				// cell top
5187
				break;
5188
			}
5189
		}
5190
		// text vertical alignment
5191
		switch ($valign) {
5192
			case 'T': {
5193
				// top
5194
				$yt = $y + $this->cell_padding['T'];
5195
				break;
5196
			}
5197
			case 'B': {
5198
				// bottom
5199
				$yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5200
				break;
5201
			}
5202
			default:
5203
			case 'C':
5204
			case 'M': {
5205
				// center
5206
				$yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5207
				break;
5208
			}
5209
		}
5210
		$basefonty = $yt + $this->FontAscent;
5211
		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5212
			if ($this->rtl) {
5213
				$w = $x - $this->lMargin;
5214
			} else {
5215
				$w = $this->w - $this->rMargin - $x;
5216
			}
5217
		}
5218
		$s = '';
5219
		// fill and borders
5220
		if (is_string($border) AND (strlen($border) == 4)) {
5221
			// full border
5222
			$border = 1;
5223
		}
5224
		if ($fill OR ($border == 1)) {
5225
			if ($fill) {
5226
				$op = ($border == 1) ? 'B' : 'f';
5227
			} else {
5228
				$op = 'S';
5229
			}
5230
			if ($this->rtl) {
5231
				$xk = (($x - $w) * $k);
5232
			} else {
5233
				$xk = ($x * $k);
5234
			}
5235
			$s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5236
		}
5237
		// draw borders
5238
		$s .= $this->getCellBorder($x, $y, $w, $h, $border);
5239
		if ($txt != '') {
5240
			$txt2 = $txt;
5241
			if ($this->isunicode) {
5242
				if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5243
					$txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5244
				} else {
5245
					$unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5246
					$unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5247
					// replace thai chars (if any)
5248
					if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5249
						// number of chars
5250
						$numchars = count($unicode);
5251
						// po pla, for far, for fan
5252
						$longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5253
						// do chada, to patak
5254
						$lowtail = array(0x0e0e, 0x0e0f);
5255
						// mai hun arkad, sara i, sara ii, sara ue, sara uee
5256
						$upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5257
						// mai ek, mai tho, mai tri, mai chattawa, karan
5258
						$tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5259
						// sara u, sara uu, pinthu
5260
						$lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5261
						$output = array();
5262
						for ($i = 0; $i < $numchars; $i++) {
5263
							if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5264
								$ch0 = $unicode[$i];
5265
								$ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5266
								$ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5267
								$chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5268
								if (in_array($ch0, $tonemark)) {
5269
									if ($chn == 0x0e33) {
5270
										// sara um
5271
										if (in_array($ch1, $longtail)) {
5272
											// tonemark at upper left
5273
											$output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5274
										} else {
5275
											// tonemark at upper right (normal position)
5276
											$output[] = $ch0;
5277
										}
5278
									} elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5279
										// tonemark at lower left
5280
										$output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5281
									} elseif (in_array($ch1, $upvowel)) {
5282
										if (in_array($ch2, $longtail)) {
5283
											// tonemark at upper left
5284
											$output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5285
										} else {
5286
											// tonemark at upper right (normal position)
5287
											$output[] = $ch0;
5288
										}
5289
									} else {
5290
										// tonemark at lower right
5291
										$output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5292
									}
5293
								} elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5294
									// add lower left nikhahit and sara aa
5295
									if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5296
										$output[] = 0xf711;
5297
										$this->CurrentFont['subsetchars'][0xf711] = true;
5298
										$output[] = 0x0e32;
5299
										$this->CurrentFont['subsetchars'][0x0e32] = true;
5300
									} else {
5301
										$output[] = $ch0;
5302
									}
5303
								} elseif (in_array($ch1, $longtail)) {
5304
									if ($ch0 == 0x0e31) {
5305
										// lower left mai hun arkad
5306
										$output[] = $this->replaceChar($ch0, 0xf710);
5307
									} elseif (in_array($ch0, $upvowel)) {
5308
										// lower left
5309
										$output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5310
									} elseif ($ch0 == 0x0e47) {
5311
										// lower left mai tai koo
5312
										$output[] = $this->replaceChar($ch0, 0xf712);
5313
									} else {
5314
										// normal character
5315
										$output[] = $ch0;
5316
									}
5317
								} elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5318
									// lower vowel
5319
									$output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5320
								} elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5321
									// yo ying without lower part
5322
									$output[] = $this->replaceChar($ch0, 0xf70f);
5323
								} elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5324
									// tho santan without lower part
5325
									$output[] = $this->replaceChar($ch0, 0xf700);
5326
								} else {
5327
									$output[] = $ch0;
5328
								}
5329
							} else {
5330
								// non-thai character
5331
								$output[] = $unicode[$i];
5332
							}
5333
						}
5334
						$unicode = $output;
5335
						// update font subsetchars
5336
						$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5337
					} // end of K_THAI_TOPCHARS
5338
					$txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5339
				}
5340
			}
5341
			$txt2 = TCPDF_STATIC::_escape($txt2);
5342
			// get current text width (considering general font stretching and spacing)
5343
			$txwidth = $this->GetStringWidth($txt);
5344
			$width = $txwidth;
5345
			// check for stretch mode
5346
			if ($stretch > 0) {
5347
				// calculate ratio between cell width and text width
5348
				if ($width <= 0) {
5349
					$ratio = 1;
5350
				} else {
5351
					$ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5352
				}
5353
				// check if stretching is required
5354
				if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5355
					// the text will be stretched to fit cell width
5356
					if ($stretch > 2) {
5357
						// set new character spacing
5358
						$this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5359
					} else {
5360
						// set new horizontal stretching
5361
						$this->font_stretching *= $ratio;
5362
					}
5363
					// recalculate text width (the text fills the entire cell)
5364
					$width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5365
					// reset alignment
5366
					$align = '';
5367
				}
5368
			}
5369
			if ($this->font_stretching != 100) {
5370
				// apply font stretching
5371
				$rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5372
			}
5373
			if ($this->font_spacing != 0) {
5374
				// increase/decrease font spacing
5375
				$rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5376
			}
5377
			if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5378
				$s .= 'q '.$this->TextColor.' ';
5379
			}
5380
			// rendering mode
5381
			$s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5382
			// count number of spaces
5383
			$ns = substr_count($txt, chr(32));
5384
			// Justification
5385
			$spacewidth = 0;
5386
			if (($align == 'J') AND ($ns > 0)) {
5387
				if ($this->isUnicodeFont()) {
5388
					// get string width without spaces
5389
					$width = $this->GetStringWidth(str_replace(' ', '', $txt));
5390
					// calculate average space width
5391
					$spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
5392
					if ($this->font_stretching != 100) {
5393
						// word spacing is affected by stretching
5394
						$spacewidth /= ($this->font_stretching / 100);
5395
					}
5396
					// set word position to be used with TJ operator
5397
					$txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5398
					$unicode_justification = true;
5399
				} else {
5400
					// get string width
5401
					$width = $txwidth;
5402
					// new space width
5403
					$spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5404
					if ($this->font_stretching != 100) {
5405
						// word spacing (Tw) is affected by stretching
5406
						$spacewidth /= ($this->font_stretching / 100);
5407
					}
5408
					// set word spacing
5409
					$rs .= sprintf('BT %F Tw ET ', $spacewidth);
5410
				}
5411
				$width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5412
			}
5413
			// replace carriage return characters
5414
			$txt2 = str_replace("\r", ' ', $txt2);
5415
			switch ($align) {
5416
				case 'C': {
5417
					$dx = ($w - $width) / 2;
5418
					break;
5419
				}
5420
				case 'R': {
5421
					if ($this->rtl) {
5422
						$dx = $this->cell_padding['R'];
5423
					} else {
5424
						$dx = $w - $width - $this->cell_padding['R'];
5425
					}
5426
					break;
5427
				}
5428
				case 'L': {
5429
					if ($this->rtl) {
5430
						$dx = $w - $width - $this->cell_padding['L'];
5431
					} else {
5432
						$dx = $this->cell_padding['L'];
5433
					}
5434
					break;
5435
				}
5436
				case 'J':
5437
				default: {
5438
					if ($this->rtl) {
5439
						$dx = $this->cell_padding['R'];
5440
					} else {
5441
						$dx = $this->cell_padding['L'];
5442
					}
5443
					break;
5444
				}
5445
			}
5446
			if ($this->rtl) {
5447
				$xdx = $x - $dx - $width;
5448
			} else {
5449
				$xdx = $x + $dx;
5450
			}
5451
			$xdk = $xdx * $k;
5452
			// print text
5453
			$s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5454
			if (isset($uniblock)) {
5455
				// print overlapping characters as separate string
5456
				$xshift = 0; // horizontal shift
5457
				$ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5458
				$spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5459
				foreach ($uniblock as $uk => $uniarr) {
5460
					if (($uk % 2) == 0) {
5461
						// x space to skip
5462
						if ($spacewidth != 0) {
5463
							// justification shift
5464
							$xshift += (count(array_keys($uniarr, 32)) * $spw);
5465
						}
5466
						$xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5467
					} else {
5468
						// character to print
5469
						$topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5470
						$topchr = TCPDF_STATIC::_escape($topchr);
5471
						$s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5472
					}
5473
				}
5474
			}
5475
			if ($this->underline) {
5476
				$s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5477
			}
5478
			if ($this->linethrough) {
5479
				$s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5480
			}
5481
			if ($this->overline) {
5482
				$s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5483
			}
5484
			if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5485
				$s .= ' Q';
5486
			}
5487
			if ($link) {
5488
				$this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5489
			}
5490
		}
5491
		// output cell
5492
		if ($s) {
5493
			// output cell
5494
			$rs .= $s;
5495
			if ($this->font_spacing != 0) {
5496
				// reset font spacing mode
5497
				$rs .= ' BT 0 Tc ET';
5498
			}
5499
			if ($this->font_stretching != 100) {
5500
				// reset font stretching mode
5501
				$rs .= ' BT 100 Tz ET';
5502
			}
5503
		}
5504
		// reset word spacing
5505
		if (!$this->isUnicodeFont() AND ($align == 'J')) {
5506
			$rs .= ' BT 0 Tw ET';
5507
		}
5508
		// reset stretching and spacing
5509
		$this->font_stretching = $prev_font_stretching;
5510
		$this->font_spacing = $prev_font_spacing;
5511
		$this->lasth = $h;
5512
		if ($ln > 0) {
5513
			//Go to the beginning of the next line
5514
			$this->y = $y + $h + $this->cell_margin['B'];
5515
			if ($ln == 1) {
5516
				if ($this->rtl) {
5517
					$this->x = $this->w - $this->rMargin;
5518
				} else {
5519
					$this->x = $this->lMargin;
5520
				}
5521
			}
5522
		} else {
5523
			// go left or right by case
5524
			if ($this->rtl) {
5525
				$this->x = $x - $w - $this->cell_margin['L'];
5526
			} else {
5527
				$this->x = $x + $w + $this->cell_margin['R'];
5528
			}
5529
		}
5530
		$gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5531
		$rs = $gstyles.$rs;
5532
		$this->cell_padding = $prev_cell_padding;
5533
		$this->cell_margin = $prev_cell_margin;
5534
		return $rs;
5535
	}
5536
 
5537
	/**
5538
	 * Replace a char if is defined on the current font.
5539
	 * @param $oldchar (int) Integer code (unicode) of the character to replace.
5540
	 * @param $newchar (int) Integer code (unicode) of the new character.
5541
	 * @return int the replaced char or the old char in case the new char i not defined
5542
	 * @protected
5543
	 * @since 5.9.167 (2012-06-22)
5544
	 */
5545
	protected function replaceChar($oldchar, $newchar) {
5546
		if ($this->isCharDefined($newchar)) {
5547
			// add the new char on the subset list
5548
			$this->CurrentFont['subsetchars'][$newchar] = true;
5549
			// return the new character
5550
			return $newchar;
5551
		}
5552
		// return the old char
5553
		return $oldchar;
5554
	}
5555
 
5556
	/**
5557
	 * Returns the code to draw the cell border
5558
	 * @param $x (float) X coordinate.
5559
	 * @param $y (float) Y coordinate.
5560
	 * @param $w (float) Cell width.
5561
	 * @param $h (float) Cell height.
5562
	 * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5563
	 * @return string containing cell border code
5564
	 * @protected
5565
	 * @see SetLineStyle()
5566
	 * @since 5.7.000 (2010-08-02)
5567
	 */
5568
	protected function getCellBorder($x, $y, $w, $h, $brd) {
5569
		$s = ''; // string to be returned
5570
		if (empty($brd)) {
5571
			return $s;
5572
		}
5573
		if ($brd == 1) {
5574
			$brd = array('LRTB' => true);
5575
		}
5576
		// calculate coordinates for border
5577
		$k = $this->k;
5578
		if ($this->rtl) {
5579
			$xeL = ($x - $w) * $k;
5580
			$xeR = $x * $k;
5581
		} else {
5582
			$xeL = $x * $k;
5583
			$xeR = ($x + $w) * $k;
5584
		}
5585
		$yeL = (($this->h - ($y + $h)) * $k);
5586
		$yeT = (($this->h - $y) * $k);
5587
		$xeT = $xeL;
5588
		$xeB = $xeR;
5589
		$yeR = $yeT;
5590
		$yeB = $yeL;
5591
		if (is_string($brd)) {
5592
			// convert string to array
5593
			$slen = strlen($brd);
5594
			$newbrd = array();
5595
			for ($i = 0; $i < $slen; ++$i) {
5596
				$newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5597
			}
5598
			$brd = $newbrd;
5599
		}
5600
		if (isset($brd['mode'])) {
5601
			$mode = $brd['mode'];
5602
			unset($brd['mode']);
5603
		} else {
5604
			$mode = 'normal';
5605
		}
5606
		foreach ($brd as $border => $style) {
5607
			if (is_array($style) AND !empty($style)) {
5608
				// apply border style
5609
				$prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5610
				$s .= $this->SetLineStyle($style, true)."\n";
5611
			}
5612
			switch ($mode) {
5613
				case 'ext': {
5614
					$off = (($this->LineWidth / 2) * $k);
5615
					$xL = $xeL - $off;
5616
					$xR = $xeR + $off;
5617
					$yT = $yeT + $off;
5618
					$yL = $yeL - $off;
5619
					$xT = $xL;
5620
					$xB = $xR;
5621
					$yR = $yT;
5622
					$yB = $yL;
5623
					$w += $this->LineWidth;
5624
					$h += $this->LineWidth;
5625
					break;
5626
				}
5627
				case 'int': {
5628
					$off = ($this->LineWidth / 2) * $k;
5629
					$xL = $xeL + $off;
5630
					$xR = $xeR - $off;
5631
					$yT = $yeT - $off;
5632
					$yL = $yeL + $off;
5633
					$xT = $xL;
5634
					$xB = $xR;
5635
					$yR = $yT;
5636
					$yB = $yL;
5637
					$w -= $this->LineWidth;
5638
					$h -= $this->LineWidth;
5639
					break;
5640
				}
5641
				case 'normal':
5642
				default: {
5643
					$xL = $xeL;
5644
					$xT = $xeT;
5645
					$xB = $xeB;
5646
					$xR = $xeR;
5647
					$yL = $yeL;
5648
					$yT = $yeT;
5649
					$yB = $yeB;
5650
					$yR = $yeR;
5651
					break;
5652
				}
5653
			}
5654
			// draw borders by case
5655
			if (strlen($border) == 4) {
5656
				$s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5657
			} elseif (strlen($border) == 3) {
5658
				if (strpos($border,'B') === false) { // LTR
5659
					$s .= sprintf('%F %F m ', $xL, $yL);
5660
					$s .= sprintf('%F %F l ', $xT, $yT);
5661
					$s .= sprintf('%F %F l ', $xR, $yR);
5662
					$s .= sprintf('%F %F l ', $xB, $yB);
5663
					$s .= 'S ';
5664
				} elseif (strpos($border,'L') === false) { // TRB
5665
					$s .= sprintf('%F %F m ', $xT, $yT);
5666
					$s .= sprintf('%F %F l ', $xR, $yR);
5667
					$s .= sprintf('%F %F l ', $xB, $yB);
5668
					$s .= sprintf('%F %F l ', $xL, $yL);
5669
					$s .= 'S ';
5670
				} elseif (strpos($border,'T') === false) { // RBL
5671
					$s .= sprintf('%F %F m ', $xR, $yR);
5672
					$s .= sprintf('%F %F l ', $xB, $yB);
5673
					$s .= sprintf('%F %F l ', $xL, $yL);
5674
					$s .= sprintf('%F %F l ', $xT, $yT);
5675
					$s .= 'S ';
5676
				} elseif (strpos($border,'R') === false) { // BLT
5677
					$s .= sprintf('%F %F m ', $xB, $yB);
5678
					$s .= sprintf('%F %F l ', $xL, $yL);
5679
					$s .= sprintf('%F %F l ', $xT, $yT);
5680
					$s .= sprintf('%F %F l ', $xR, $yR);
5681
					$s .= 'S ';
5682
				}
5683
			} elseif (strlen($border) == 2) {
5684
				if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5685
					$s .= sprintf('%F %F m ', $xL, $yL);
5686
					$s .= sprintf('%F %F l ', $xT, $yT);
5687
					$s .= sprintf('%F %F l ', $xR, $yR);
5688
					$s .= 'S ';
5689
				} elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5690
					$s .= sprintf('%F %F m ', $xT, $yT);
5691
					$s .= sprintf('%F %F l ', $xR, $yR);
5692
					$s .= sprintf('%F %F l ', $xB, $yB);
5693
					$s .= 'S ';
5694
				} elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5695
					$s .= sprintf('%F %F m ', $xR, $yR);
5696
					$s .= sprintf('%F %F l ', $xB, $yB);
5697
					$s .= sprintf('%F %F l ', $xL, $yL);
5698
					$s .= 'S ';
5699
				} elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5700
					$s .= sprintf('%F %F m ', $xB, $yB);
5701
					$s .= sprintf('%F %F l ', $xL, $yL);
5702
					$s .= sprintf('%F %F l ', $xT, $yT);
5703
					$s .= 'S ';
5704
				} elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5705
					$s .= sprintf('%F %F m ', $xL, $yL);
5706
					$s .= sprintf('%F %F l ', $xT, $yT);
5707
					$s .= 'S ';
5708
					$s .= sprintf('%F %F m ', $xR, $yR);
5709
					$s .= sprintf('%F %F l ', $xB, $yB);
5710
					$s .= 'S ';
5711
				} elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5712
					$s .= sprintf('%F %F m ', $xT, $yT);
5713
					$s .= sprintf('%F %F l ', $xR, $yR);
5714
					$s .= 'S ';
5715
					$s .= sprintf('%F %F m ', $xB, $yB);
5716
					$s .= sprintf('%F %F l ', $xL, $yL);
5717
					$s .= 'S ';
5718
				}
5719
			} else { // strlen($border) == 1
5720
				if (strpos($border,'L') !== false) { // L
5721
					$s .= sprintf('%F %F m ', $xL, $yL);
5722
					$s .= sprintf('%F %F l ', $xT, $yT);
5723
					$s .= 'S ';
5724
				} elseif (strpos($border,'T') !== false) { // T
5725
					$s .= sprintf('%F %F m ', $xT, $yT);
5726
					$s .= sprintf('%F %F l ', $xR, $yR);
5727
					$s .= 'S ';
5728
				} elseif (strpos($border,'R') !== false) { // R
5729
					$s .= sprintf('%F %F m ', $xR, $yR);
5730
					$s .= sprintf('%F %F l ', $xB, $yB);
5731
					$s .= 'S ';
5732
				} elseif (strpos($border,'B') !== false) { // B
5733
					$s .= sprintf('%F %F m ', $xB, $yB);
5734
					$s .= sprintf('%F %F l ', $xL, $yL);
5735
					$s .= 'S ';
5736
				}
5737
			}
5738
			if (is_array($style) AND !empty($style)) {
5739
				// reset border style to previous value
5740
				$s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5741
			}
5742
		}
5743
		return $s;
5744
	}
5745
 
5746
	/**
5747
	 * This method allows printing text with line breaks.
5748
	 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
5749
	 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5750
	 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5751
	 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5752
	 * @param $txt (string) String to print
5753
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5754
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
5755
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5756
	 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
5757
	 * @param $x (float) x position in user units
5758
	 * @param $y (float) y position in user units
5759
	 * @param $reseth (boolean) if true reset the last cell height (default true).
5760
	 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5761
	 * @param $ishtml (boolean) INTERNAL USE ONLY -- set to true if $txt is HTML content (default = false). Never set this parameter to true, use instead writeHTMLCell() or writeHTML() methods.
5762
	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
5763
	 * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
5764
	 * @param $valign (string) Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false and the cell must fit in a single page.
5765
	 * @param $fitcell (boolean) if true attempt to fit all the text within the cell by reducing the font size (do not work in HTML mode).
5766
	 * @return int Return the number of cells or 1 for html mode.
5767
	 * @public
5768
	 * @since 1.3
5769
	 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5770
	 */
5771
	public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
5772
		$prev_cell_margin = $this->cell_margin;
5773
		$prev_cell_padding = $this->cell_padding;
5774
		// adjust internal padding
5775
		$this->adjustCellPadding($border);
5776
		$mc_padding = $this->cell_padding;
5777
		$mc_margin = $this->cell_margin;
5778
		$this->cell_padding['T'] = 0;
5779
		$this->cell_padding['B'] = 0;
5780
		$this->setCellMargins(0, 0, 0, 0);
5781
		if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5782
			// reset row height
5783
			$this->resetLastH();
5784
		}
5785
		if (!TCPDF_STATIC::empty_string($y)) {
5786
			$this->SetY($y);
5787
		} else {
5788
			$y = $this->GetY();
5789
		}
5790
		$resth = 0;
5791
		if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5792
			// spit cell in more pages/columns
5793
			$newh = ($this->PageBreakTrigger - $y);
5794
			$resth = ($h - $newh); // cell to be printed on the next page/column
5795
			$h = $newh;
5796
		}
5797
		// get current page number
5798
		$startpage = $this->page;
5799
		// get current column
5800
		$startcolumn = $this->current_column;
5801
		if (!TCPDF_STATIC::empty_string($x)) {
5802
			$this->SetX($x);
5803
		} else {
5804
			$x = $this->GetX();
5805
		}
5806
		// check page for no-write regions and adapt page margins if necessary
5807
		list($x, $y) = $this->checkPageRegions(0, $x, $y);
5808
		// apply margins
5809
		$oy = $y + $mc_margin['T'];
5810
		if ($this->rtl) {
5811
			$ox = ($this->w - $x - $mc_margin['R']);
5812
		} else {
5813
			$ox = ($x + $mc_margin['L']);
5814
		}
5815
		$this->x = $ox;
5816
		$this->y = $oy;
5817
		// set width
5818
		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5819
			if ($this->rtl) {
5820
				$w = ($this->x - $this->lMargin - $mc_margin['L']);
5821
			} else {
5822
				$w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5823
			}
5824
		}
5825
		// store original margin values
5826
		$lMargin = $this->lMargin;
5827
		$rMargin = $this->rMargin;
5828
		if ($this->rtl) {
5829
			$this->rMargin = ($this->w - $this->x);
5830
			$this->lMargin = ($this->x - $w);
5831
		} else {
5832
			$this->lMargin = ($this->x);
5833
			$this->rMargin = ($this->w - $this->x - $w);
5834
		}
5835
		$this->clMargin = $this->lMargin;
5836
		$this->crMargin = $this->rMargin;
5837
		if ($autopadding) {
5838
			// add top padding
5839
			$this->y += $mc_padding['T'];
5840
		}
5841
		if ($ishtml) { // ******* Write HTML text
5842
			$this->writeHTML($txt, true, false, $reseth, true, $align);
5843
			$nl = 1;
5844
		} else { // ******* Write simple text
5845
			$prev_FontSizePt = $this->FontSizePt;
5846
			// vertical alignment
5847
			if ($maxh > 0) {
5848
				// get text height
5849
				$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5850
				if ($fitcell) {
5851
					// try to reduce font size to fit text on cell (use a quick search algorithm)
5852
					$fmin = 1;
5853
					$fmax = $this->FontSizePt;
5854
					$prev_text_height = $text_height;
5855
					$maxit = 100; // max number of iterations
5856
					while ($maxit > 0) {
5857
						$fmid = (($fmax + $fmin) / 2);
5858
						$this->SetFontSize($fmid, false);
5859
						$this->resetLastH();
5860
						$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5861
						if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
5862
							break;
5863
						} elseif ($text_height < $maxh) {
5864
							$fmin = $fmid;
5865
						} else {
5866
							$fmax = $fmid;
5867
						}
5868
						--$maxit;
5869
					}
5870
					$this->SetFontSize($this->FontSizePt);
5871
				}
5872
				if ($text_height < $maxh) {
5873
					if ($valign == 'M') {
5874
						// text vertically centered
5875
						$this->y += (($maxh - $text_height) / 2);
5876
					} elseif ($valign == 'B') {
5877
						// text vertically aligned on bottom
5878
						$this->y += ($maxh - $text_height);
5879
					}
5880
				}
5881
			}
5882
			$nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5883
			if ($fitcell) {
5884
				// restore font size
5885
				$this->SetFontSize($prev_FontSizePt);
5886
			}
5887
		}
5888
		if ($autopadding) {
5889
			// add bottom padding
5890
			$this->y += $mc_padding['B'];
5891
		}
5892
		// Get end-of-text Y position
5893
		$currentY = $this->y;
5894
		// get latest page number
5895
		$endpage = $this->page;
5896
		if ($resth > 0) {
5897
			$skip = ($endpage - $startpage);
5898
			$tmpresth = $resth;
5899
			while ($tmpresth > 0) {
5900
				if ($skip <= 0) {
5901
					// add a page (or trig AcceptPageBreak() for multicolumn mode)
5902
					$this->checkPageBreak($this->PageBreakTrigger + 1);
5903
				}
5904
				if ($this->num_columns > 1) {
5905
					$tmpresth -= ($this->h - $this->y - $this->bMargin);
5906
				} else {
5907
					$tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5908
				}
5909
				--$skip;
5910
			}
5911
			$currentY = $this->y;
5912
			$endpage = $this->page;
5913
		}
5914
		// get latest column
5915
		$endcolumn = $this->current_column;
5916
		if ($this->num_columns == 0) {
5917
			$this->num_columns = 1;
5918
		}
5919
		// disable page regions check
5920
		$check_page_regions = $this->check_page_regions;
5921
		$this->check_page_regions = false;
5922
		// get border modes
5923
		$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5924
		$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5925
		$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
5926
		// design borders around HTML cells.
5927
		for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
5928
			$ccode = '';
5929
			$this->setPage($page);
5930
			if ($this->num_columns < 2) {
5931
				// single-column mode
5932
				$this->SetX($x);
5933
				$this->y = $this->tMargin;
5934
			}
5935
			// account for margin changes
5936
			if ($page > $startpage) {
5937
				if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
5938
					$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
5939
				} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
5940
					$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
5941
				}
5942
			}
5943
			if ($startpage == $endpage) {
5944
				// single page
5945
				for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
5946
					$this->selectColumn($column);
5947
					if ($this->rtl) {
5948
						$this->x -= $mc_margin['R'];
5949
					} else {
5950
						$this->x += $mc_margin['L'];
5951
					}
5952
					if ($startcolumn == $endcolumn) { // single column
5953
						$cborder = $border;
5954
						$h = max($h, ($currentY - $oy));
5955
						$this->y = $oy;
5956
					} elseif ($column == $startcolumn) { // first column
5957
						$cborder = $border_start;
5958
						$this->y = $oy;
5959
						$h = $this->h - $this->y - $this->bMargin;
5960
					} elseif ($column == $endcolumn) { // end column
5961
						$cborder = $border_end;
5962
						$h = $currentY - $this->y;
5963
						if ($resth > $h) {
5964
							$h = $resth;
5965
						}
5966
					} else { // middle column
5967
						$cborder = $border_middle;
5968
						$h = $this->h - $this->y - $this->bMargin;
5969
						$resth -= $h;
5970
					}
5971
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5972
				} // end for each column
5973
			} elseif ($page == $startpage) { // first page
5974
				for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
5975
					$this->selectColumn($column);
5976
					if ($this->rtl) {
5977
						$this->x -= $mc_margin['R'];
5978
					} else {
5979
						$this->x += $mc_margin['L'];
5980
					}
5981
					if ($column == $startcolumn) { // first column
5982
						$cborder = $border_start;
5983
						$this->y = $oy;
5984
						$h = $this->h - $this->y - $this->bMargin;
5985
					} else { // middle column
5986
						$cborder = $border_middle;
5987
						$h = $this->h - $this->y - $this->bMargin;
5988
						$resth -= $h;
5989
					}
5990
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5991
				} // end for each column
5992
			} elseif ($page == $endpage) { // last page
5993
				for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
5994
					$this->selectColumn($column);
5995
					if ($this->rtl) {
5996
						$this->x -= $mc_margin['R'];
5997
					} else {
5998
						$this->x += $mc_margin['L'];
5999
					}
6000
					if ($column == $endcolumn) {
6001
						// end column
6002
						$cborder = $border_end;
6003
						$h = $currentY - $this->y;
6004
						if ($resth > $h) {
6005
							$h = $resth;
6006
						}
6007
					} else {
6008
						// middle column
6009
						$cborder = $border_middle;
6010
						$h = $this->h - $this->y - $this->bMargin;
6011
						$resth -= $h;
6012
					}
6013
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6014
				} // end for each column
6015
			} else { // middle page
6016
				for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6017
					$this->selectColumn($column);
6018
					if ($this->rtl) {
6019
						$this->x -= $mc_margin['R'];
6020
					} else {
6021
						$this->x += $mc_margin['L'];
6022
					}
6023
					$cborder = $border_middle;
6024
					$h = $this->h - $this->y - $this->bMargin;
6025
					$resth -= $h;
6026
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6027
				} // end for each column
6028
			}
6029
			if ($cborder OR $fill) {
6030
				$offsetlen = strlen($ccode);
6031
				// draw border and fill
6032
				if ($this->inxobj) {
6033
					// we are inside an XObject template
6034
					if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6035
						$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6036
						$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6037
						$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6038
					} else {
6039
						$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6040
						$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6041
					}
6042
					$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6043
					$pstart = substr($pagebuff, 0, $pagemark);
6044
					$pend = substr($pagebuff, $pagemark);
6045
					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6046
				} else {
6047
					if (end($this->transfmrk[$this->page]) !== false) {
6048
						$pagemarkkey = key($this->transfmrk[$this->page]);
6049
						$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6050
						$this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6051
					} elseif ($this->InFooter) {
6052
						$pagemark = $this->footerpos[$this->page];
6053
						$this->footerpos[$this->page] += $offsetlen;
6054
					} else {
6055
						$pagemark = $this->intmrk[$this->page];
6056
						$this->intmrk[$this->page] += $offsetlen;
6057
					}
6058
					$pagebuff = $this->getPageBuffer($this->page);
6059
					$pstart = substr($pagebuff, 0, $pagemark);
6060
					$pend = substr($pagebuff, $pagemark);
6061
					$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6062
				}
6063
			}
6064
		} // end for each page
6065
		// restore page regions check
6066
		$this->check_page_regions = $check_page_regions;
6067
		// Get end-of-cell Y position
6068
		$currentY = $this->GetY();
6069
		// restore previous values
6070
		if ($this->num_columns > 1) {
6071
			$this->selectColumn();
6072
		} else {
6073
			// restore original margins
6074
			$this->lMargin = $lMargin;
6075
			$this->rMargin = $rMargin;
6076
			if ($this->page > $startpage) {
6077
				// check for margin variations between pages (i.e. booklet mode)
6078
				$dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6079
				$dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6080
				if (($dl != 0) OR ($dr != 0)) {
6081
					$this->lMargin += $dl;
6082
					$this->rMargin += $dr;
6083
				}
6084
			}
6085
		}
6086
		if ($ln > 0) {
6087
			//Go to the beginning of the next line
6088
			$this->SetY($currentY + $mc_margin['B']);
6089
			if ($ln == 2) {
6090
				$this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6091
			}
6092
		} else {
6093
			// go left or right by case
6094
			$this->setPage($startpage);
6095
			$this->y = $y;
6096
			$this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6097
		}
6098
		$this->setContentMark();
6099
		$this->cell_padding = $prev_cell_padding;
6100
		$this->cell_margin = $prev_cell_margin;
6101
		$this->clMargin = $this->lMargin;
6102
		$this->crMargin = $this->rMargin;
6103
		return $nl;
6104
	}
6105
 
6106
	/**
6107
	 * This method return the estimated number of lines for print a simple text string using Multicell() method.
6108
	 * @param $txt (string) String for calculating his height
6109
	 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6110
	 * @param $reseth (boolean) if true reset the last cell height (default false).
6111
	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6112
	 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6113
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6114
	 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6115
	 * @author Alexander Escalona Fernández, Nicola Asuni
6116
	 * @public
6117
	 * @since 4.5.011
6118
	 */
6119
	public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6120
		if ($txt === '') {
6121
			// empty string
6122
			return 1;
6123
		}
6124
		// adjust internal padding
6125
		$prev_cell_padding = $this->cell_padding;
6126
		$prev_lasth = $this->lasth;
6127
		if (is_array($cellpadding)) {
6128
			$this->cell_padding = $cellpadding;
6129
		}
6130
		$this->adjustCellPadding($border);
6131
		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6132
			if ($this->rtl) {
6133
				$w = $this->x - $this->lMargin;
6134
			} else {
6135
				$w = $this->w - $this->rMargin - $this->x;
6136
			}
6137
		}
6138
		$wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6139
		if ($reseth) {
6140
			// reset row height
6141
			$this->resetLastH();
6142
		}
6143
		$lines = 1;
6144
		$sum = 0;
6145
		$chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6146
		$charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6147
		$length = count($chars);
6148
		$lastSeparator = -1;
6149
		for ($i = 0; $i < $length; ++$i) {
6150
			$charWidth = $charsWidth[$i];
6151
			if (preg_match($this->re_spaces, TCPDF_FONTS::unichr($chars[$i], $this->isunicode))) {
6152
				$lastSeparator = $i;
6153
			}
6154
			if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
6155
				++$lines;
6156
				if ($chars[$i] == 10) {
6157
					$lastSeparator = -1;
6158
					$sum = 0;
6159
				} elseif ($lastSeparator != -1) {
6160
					$i = $lastSeparator;
6161
					$lastSeparator = -1;
6162
					$sum = 0;
6163
				} else {
6164
					$sum = $charWidth;
6165
				}
6166
			} else {
6167
				$sum += $charWidth;
6168
			}
6169
		}
6170
		if ($chars[($length - 1)] == 10) {
6171
			--$lines;
6172
		}
6173
		$this->cell_padding = $prev_cell_padding;
6174
		$this->lasth = $prev_lasth;
6175
		return $lines;
6176
	}
6177
 
6178
	/**
6179
	 * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6180
	 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6181
	 * @pre
6182
	 *  // store current object
6183
	 *  $pdf->startTransaction();
6184
	 *  // store starting values
6185
	 *  $start_y = $pdf->GetY();
6186
	 *  $start_page = $pdf->getPage();
6187
	 *  // call your printing functions with your parameters
6188
	 *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6189
	 *  $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
6190
	 *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6191
	 *  // get the new Y
6192
	 *  $end_y = $pdf->GetY();
6193
	 *  $end_page = $pdf->getPage();
6194
	 *  // calculate height
6195
	 *  $height = 0;
6196
	 *  if ($end_page == $start_page) {
6197
	 *  	$height = $end_y - $start_y;
6198
	 *  } else {
6199
	 *  	for ($page=$start_page; $page <= $end_page; ++$page) {
6200
	 *  		$this->setPage($page);
6201
	 *  		if ($page == $start_page) {
6202
	 *  			// first page
6203
	 *  			$height = $this->h - $start_y - $this->bMargin;
6204
	 *  		} elseif ($page == $end_page) {
6205
	 *  			// last page
6206
	 *  			$height = $end_y - $this->tMargin;
6207
	 *  		} else {
6208
	 *  			$height = $this->h - $this->tMargin - $this->bMargin;
6209
	 *  		}
6210
	 *  	}
6211
	 *  }
6212
	 *  // restore previous object
6213
	 *  $pdf = $pdf->rollbackTransaction();
6214
	 *
6215
	 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6216
	 * @param $txt (string) String for calculating his height
6217
	 * @param $reseth (boolean) if true reset the last cell height (default false).
6218
	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6219
	 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6220
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6221
	 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6222
	 * @author Nicola Asuni, Alexander Escalona Fernández
6223
	 * @public
6224
	 */
6225
	public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6226
		// adjust internal padding
6227
		$prev_cell_padding = $this->cell_padding;
6228
		$prev_lasth = $this->lasth;
6229
		if (is_array($cellpadding)) {
6230
			$this->cell_padding = $cellpadding;
6231
		}
6232
		$this->adjustCellPadding($border);
6233
		$lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6234
		$height = $lines * ($this->FontSize * $this->cell_height_ratio);
6235
		if ($autopadding) {
6236
			// add top and bottom padding
6237
			$height += ($this->cell_padding['T'] + $this->cell_padding['B']);
6238
		}
6239
		$this->cell_padding = $prev_cell_padding;
6240
		$this->lasth = $prev_lasth;
6241
		return $height;
6242
	}
6243
 
6244
	/**
6245
	 * This method prints text from the current position.<br />
6246
	 * @param $h (float) Line height
6247
	 * @param $txt (string) String to print
6248
	 * @param $link (mixed) URL or identifier returned by AddLink()
6249
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6250
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
6251
	 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6252
	 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
6253
	 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6254
	 * @param $firstblock (boolean) if true the string is the starting of a line.
6255
	 * @param $maxh (float) maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
6256
	 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6257
	 * @param $margin (array) margin array of the parent container
6258
	 * @return mixed Return the number of cells or the remaining string if $firstline = true.
6259
	 * @public
6260
	 * @since 1.5
6261
	 */
6262
	public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6263
		// check page for no-write regions and adapt page margins if necessary
6264
		list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6265
		if (strlen($txt) == 0) {
6266
			// fix empty text
6267
			$txt = ' ';
6268
		}
6269
		if ($margin === '') {
6270
			// set default margins
6271
			$margin = $this->cell_margin;
6272
		}
6273
		// remove carriage returns
6274
		$s = str_replace("\r", '', $txt);
6275
		// check if string contains arabic text
6276
		if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6277
			$arabic = true;
6278
		} else {
6279
			$arabic = false;
6280
		}
6281
		// check if string contains RTL text
6282
		if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6283
			$rtlmode = true;
6284
		} else {
6285
			$rtlmode = false;
6286
		}
6287
		// get a char width
6288
		$chrwidth = $this->GetCharWidth(46); // dot character
6289
		// get array of unicode values
6290
		$chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6291
		// calculate maximum width for a single character on string
6292
		$chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6293
		array_walk($chrw, array($this, 'getRawCharWidth'));
6294
		$maxchwidth = max($chrw);
6295
		// get array of chars
6296
		$uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6297
		// get the number of characters
6298
		$nb = count($chars);
6299
		// replacement for SHY character (minus symbol)
6300
		$shy_replacement = 45;
6301
		$shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6302
		// widht for SHY replacement
6303
		$shy_replacement_width = $this->GetCharWidth($shy_replacement);
6304
		// max Y
6305
		$maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
6306
		// page width
6307
		$pw = $w = $this->w - $this->lMargin - $this->rMargin;
6308
		// calculate remaining line width ($w)
6309
		if ($this->rtl) {
6310
			$w = $this->x - $this->lMargin;
6311
		} else {
6312
			$w = $this->w - $this->rMargin - $this->x;
6313
		}
6314
		// max column width
6315
		$wmax = ($w - $wadj);
6316
		if (!$firstline) {
6317
			$wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6318
		}
6319
		if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6320
			// the maximum width character do not fit on column
6321
			return '';
6322
		}
6323
		// minimum row height
6324
		$row_height = max($h, $this->FontSize * $this->cell_height_ratio);
6325
		$start_page = $this->page;
6326
		$i = 0; // character position
6327
		$j = 0; // current starting position
6328
		$sep = -1; // position of the last blank space
6329
		$shy = false; // true if the last blank is a soft hypen (SHY)
6330
		$l = 0; // current string length
6331
		$nl = 0; //number of lines
6332
		$linebreak = false;
6333
		$pc = 0; // previous character
6334
		// for each character
6335
		while ($i < $nb) {
6336
			if (($maxh > 0) AND ($this->y >= $maxy) ) {
6337
				break;
6338
			}
6339
			//Get the current character
6340
			$c = $chars[$i];
6341
			if ($c == 10) { // 10 = "\n" = new line
6342
				//Explicit line break
6343
				if ($align == 'J') {
6344
					if ($this->rtl) {
6345
						$talign = 'R';
6346
					} else {
6347
						$talign = 'L';
6348
					}
6349
				} else {
6350
					$talign = $align;
6351
				}
6352
				$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6353
				if ($firstline) {
6354
					$startx = $this->x;
6355
					$tmparr = array_slice($chars, $j, ($i - $j));
6356
					if ($rtlmode) {
6357
						$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6358
					}
6359
					$linew = $this->GetArrStringWidth($tmparr);
6360
					unset($tmparr);
6361
					if ($this->rtl) {
6362
						$this->endlinex = $startx - $linew;
6363
					} else {
6364
						$this->endlinex = $startx + $linew;
6365
					}
6366
					$w = $linew;
6367
					$tmpcellpadding = $this->cell_padding;
6368
					if ($maxh == 0) {
6369
						$this->SetCellPadding(0);
6370
					}
6371
				}
6372
				if ($firstblock AND $this->isRTLTextDir()) {
6373
					$tmpstr = $this->stringRightTrim($tmpstr);
6374
				}
6375
				// Skip newlines at the begining of a page or column
6376
				if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6377
					$this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6378
				}
6379
				unset($tmpstr);
6380
				if ($firstline) {
6381
					$this->cell_padding = $tmpcellpadding;
6382
					return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6383
				}
6384
				++$nl;
6385
				$j = $i + 1;
6386
				$l = 0;
6387
				$sep = -1;
6388
				$shy = false;
6389
				// account for margin changes
6390
				if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6391
					$this->AcceptPageBreak();
6392
					if ($this->rtl) {
6393
						$this->x -= $margin['R'];
6394
					} else {
6395
						$this->x += $margin['L'];
6396
					}
6397
					$this->lMargin += $margin['L'];
6398
					$this->rMargin += $margin['R'];
6399
				}
6400
				$w = $this->getRemainingWidth();
6401
				$wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6402
			} else {
6403
				// 160 is the non-breaking space.
6404
				// 173 is SHY (Soft Hypen).
6405
				// \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6406
				// \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6407
				// \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6408
				if (($c != 160)
6409
					AND (($c == 173)
6410
						OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6411
						OR (($c == 45)
6412
							AND ($i < ($nb - 1))
6413
							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6414
							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6415
						)
6416
					)
6417
				) {
6418
					// update last blank space position
6419
					$sep = $i;
6420
					// check if is a SHY
6421
					if (($c == 173) OR ($c == 45)) {
6422
						$shy = true;
6423
						if ($pc == 45) {
6424
							$tmp_shy_replacement_width = 0;
6425
							$tmp_shy_replacement_char = '';
6426
						} else {
6427
							$tmp_shy_replacement_width = $shy_replacement_width;
6428
							$tmp_shy_replacement_char = $shy_replacement_char;
6429
						}
6430
					} else {
6431
						$shy = false;
6432
					}
6433
				}
6434
				// update string length
6435
				if ($this->isUnicodeFont() AND ($arabic)) {
6436
					// with bidirectional algorithm some chars may be changed affecting the line length
6437
					// *** very slow ***
6438
					$l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6439
				} else {
6440
					$l += $this->GetCharWidth($c);
6441
				}
6442
				if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
6443
					// we have reached the end of column
6444
					if ($sep == -1) {
6445
						// check if the line was already started
6446
						if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6447
							OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6448
							// print a void cell and go to next line
6449
							$this->Cell($w, $h, '', 0, 1);
6450
							$linebreak = true;
6451
							if ($firstline) {
6452
								return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6453
							}
6454
						} else {
6455
							// truncate the word because do not fit on column
6456
							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6457
							if ($firstline) {
6458
								$startx = $this->x;
6459
								$tmparr = array_slice($chars, $j, ($i - $j));
6460
								if ($rtlmode) {
6461
									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6462
								}
6463
								$linew = $this->GetArrStringWidth($tmparr);
6464
								unset($tmparr);
6465
								if ($this->rtl) {
6466
									$this->endlinex = $startx - $linew;
6467
								} else {
6468
									$this->endlinex = $startx + $linew;
6469
								}
6470
								$w = $linew;
6471
								$tmpcellpadding = $this->cell_padding;
6472
								if ($maxh == 0) {
6473
									$this->SetCellPadding(0);
6474
								}
6475
							}
6476
							if ($firstblock AND $this->isRTLTextDir()) {
6477
								$tmpstr = $this->stringRightTrim($tmpstr);
6478
							}
6479
							$this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6480
							unset($tmpstr);
6481
							if ($firstline) {
6482
								$this->cell_padding = $tmpcellpadding;
6483
								return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6484
							}
6485
							$j = $i;
6486
							--$i;
6487
						}
6488
					} else {
6489
						// word wrapping
6490
						if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6491
							$endspace = 1;
6492
						} else {
6493
							$endspace = 0;
6494
						}
6495
						// check the length of the next string
6496
						$strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6497
						$nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest));
6498
						if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6499
							// truncate the word because do not fit on a full page width
6500
							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6501
							if ($firstline) {
6502
								$startx = $this->x;
6503
								$tmparr = array_slice($chars, $j, ($i - $j));
6504
								if ($rtlmode) {
6505
									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6506
								}
6507
								$linew = $this->GetArrStringWidth($tmparr);
6508
								unset($tmparr);
6509
								if ($this->rtl) {
6510
									$this->endlinex = ($startx - $linew);
6511
								} else {
6512
									$this->endlinex = ($startx + $linew);
6513
								}
6514
								$w = $linew;
6515
								$tmpcellpadding = $this->cell_padding;
6516
								if ($maxh == 0) {
6517
									$this->SetCellPadding(0);
6518
								}
6519
							}
6520
							if ($firstblock AND $this->isRTLTextDir()) {
6521
								$tmpstr = $this->stringRightTrim($tmpstr);
6522
							}
6523
							$this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6524
							unset($tmpstr);
6525
							if ($firstline) {
6526
								$this->cell_padding = $tmpcellpadding;
6527
								return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6528
							}
6529
							$j = $i;
6530
							--$i;
6531
						} else {
6532
							// word wrapping
6533
							if ($shy) {
6534
								// add hypen (minus symbol) at the end of the line
6535
								$shy_width = $tmp_shy_replacement_width;
6536
								if ($this->rtl) {
6537
									$shy_char_left = $tmp_shy_replacement_char;
6538
									$shy_char_right = '';
6539
								} else {
6540
									$shy_char_left = '';
6541
									$shy_char_right = $tmp_shy_replacement_char;
6542
								}
6543
							} else {
6544
								$shy_width = 0;
6545
								$shy_char_left = '';
6546
								$shy_char_right = '';
6547
							}
6548
							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6549
							if ($firstline) {
6550
								$startx = $this->x;
6551
								$tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6552
								if ($rtlmode) {
6553
									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6554
								}
6555
								$linew = $this->GetArrStringWidth($tmparr);
6556
								unset($tmparr);
6557
								if ($this->rtl) {
6558
									$this->endlinex = $startx - $linew - $shy_width;
6559
								} else {
6560
									$this->endlinex = $startx + $linew + $shy_width;
6561
								}
6562
								$w = $linew;
6563
								$tmpcellpadding = $this->cell_padding;
6564
								if ($maxh == 0) {
6565
									$this->SetCellPadding(0);
6566
								}
6567
							}
6568
							// print the line
6569
							if ($firstblock AND $this->isRTLTextDir()) {
6570
								$tmpstr = $this->stringRightTrim($tmpstr);
6571
							}
6572
							$this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6573
							unset($tmpstr);
6574
							if ($firstline) {
6575
								if ($chars[$sep] == 45) {
6576
									$endspace += 1;
6577
								}
6578
								// return the remaining text
6579
								$this->cell_padding = $tmpcellpadding;
6580
								return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6581
							}
6582
							$i = $sep;
6583
							$sep = -1;
6584
							$shy = false;
6585
							$j = ($i + 1);
6586
						}
6587
					}
6588
					// account for margin changes
6589
					if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6590
						$this->AcceptPageBreak();
6591
						if ($this->rtl) {
6592
							$this->x -= $margin['R'];
6593
						} else {
6594
							$this->x += $margin['L'];
6595
						}
6596
						$this->lMargin += $margin['L'];
6597
						$this->rMargin += $margin['R'];
6598
					}
6599
					$w = $this->getRemainingWidth();
6600
					$wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6601
					if ($linebreak) {
6602
						$linebreak = false;
6603
					} else {
6604
						++$nl;
6605
						$l = 0;
6606
					}
6607
				}
6608
			}
6609
			// save last character
6610
			$pc = $c;
6611
			++$i;
6612
		} // end while i < nb
6613
		// print last substring (if any)
6614
		if ($l > 0) {
6615
			switch ($align) {
6616
				case 'J':
6617
				case 'C': {
6618
					$w = $w;
6619
					break;
6620
				}
6621
				case 'L': {
6622
					if ($this->rtl) {
6623
						$w = $w;
6624
					} else {
6625
						$w = $l;
6626
					}
6627
					break;
6628
				}
6629
				case 'R': {
6630
					if ($this->rtl) {
6631
						$w = $l;
6632
					} else {
6633
						$w = $w;
6634
					}
6635
					break;
6636
				}
6637
				default: {
6638
					$w = $l;
6639
					break;
6640
				}
6641
			}
6642
			$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6643
			if ($firstline) {
6644
				$startx = $this->x;
6645
				$tmparr = array_slice($chars, $j, ($nb - $j));
6646
				if ($rtlmode) {
6647
					$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6648
				}
6649
				$linew = $this->GetArrStringWidth($tmparr);
6650
				unset($tmparr);
6651
				if ($this->rtl) {
6652
					$this->endlinex = $startx - $linew;
6653
				} else {
6654
					$this->endlinex = $startx + $linew;
6655
				}
6656
				$w = $linew;
6657
				$tmpcellpadding = $this->cell_padding;
6658
				if ($maxh == 0) {
6659
					$this->SetCellPadding(0);
6660
				}
6661
			}
6662
			if ($firstblock AND $this->isRTLTextDir()) {
6663
				$tmpstr = $this->stringRightTrim($tmpstr);
6664
			}
6665
			$this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6666
			unset($tmpstr);
6667
			if ($firstline) {
6668
				$this->cell_padding = $tmpcellpadding;
6669
				return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6670
			}
6671
			++$nl;
6672
		}
6673
		if ($firstline) {
6674
			return '';
6675
		}
6676
		return $nl;
6677
	}
6678
 
6679
	/**
6680
	 * Returns the remaining width between the current position and margins.
6681
	 * @return int Return the remaining width
6682
	 * @protected
6683
	 */
6684
	protected function getRemainingWidth() {
6685
		list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6686
		if ($this->rtl) {
6687
			return ($this->x - $this->lMargin);
6688
		} else {
6689
			return ($this->w - $this->rMargin - $this->x);
6690
		}
6691
	}
6692
 
6693
	/**
6694
	 * Set the block dimensions accounting for page breaks and page/column fitting
6695
	 * @param $w (float) width
6696
	 * @param $h (float) height
6697
	 * @param $x (float) X coordinate
6698
	 * @param $y (float) Y coodiante
6699
	 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
6700
	 * @return array($w, $h, $x, $y)
6701
	 * @protected
6702
	 * @since 5.5.009 (2010-07-05)
6703
	 */
6704
	protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6705
		if ($w <= 0) {
6706
			// set maximum width
6707
			$w = ($this->w - $this->lMargin - $this->rMargin);
6708
		}
6709
		if ($h <= 0) {
6710
			// set maximum height
6711
			$h = ($this->PageBreakTrigger - $this->tMargin);
6712
		}
6713
		// resize the block to be vertically contained on a single page or single column
6714
		if ($fitonpage OR $this->AutoPageBreak) {
6715
			$ratio_wh = ($w / $h);
6716
			if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6717
				$h = $this->PageBreakTrigger - $this->tMargin;
6718
				$w = ($h * $ratio_wh);
6719
			}
6720
			// resize the block to be horizontally contained on a single page or single column
6721
			if ($fitonpage) {
6722
				$maxw = ($this->w - $this->lMargin - $this->rMargin);
6723
				if ($w > $maxw) {
6724
					$w = $maxw;
6725
					$h = ($w / $ratio_wh);
6726
				}
6727
			}
6728
		}
6729
		// Check whether we need a new page or new column first as this does not fit
6730
		$prev_x = $this->x;
6731
		$prev_y = $this->y;
6732
		if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6733
			$y = $this->y;
6734
			if ($this->rtl) {
6735
				$x += ($prev_x - $this->x);
6736
			} else {
6737
				$x += ($this->x - $prev_x);
6738
			}
6739
			$this->newline = true;
6740
		}
6741
		// resize the block to be contained on the remaining available page or column space
6742
		if ($fitonpage) {
6743
			$ratio_wh = ($w / $h);
6744
			if (($y + $h) > $this->PageBreakTrigger) {
6745
				$h = $this->PageBreakTrigger - $y;
6746
				$w = ($h * $ratio_wh);
6747
			}
6748
			if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6749
				$w = $this->w - $this->rMargin - $x;
6750
				$h = ($w / $ratio_wh);
6751
			} elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6752
				$w = $x - $this->lMargin;
6753
				$h = ($w / $ratio_wh);
6754
			}
6755
		}
6756
		return array($w, $h, $x, $y);
6757
	}
6758
 
6759
	/**
6760
	 * Puts an image in the page.
6761
	 * The upper-left corner must be given.
6762
	 * The dimensions can be specified in different ways:<ul>
6763
	 * <li>explicit width and height (expressed in user unit)</li>
6764
	 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6765
	 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6766
	 * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
6767
	 * The format can be specified explicitly or inferred from the file extension.<br />
6768
	 * It is possible to put a link on the image.<br />
6769
	 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6770
	 * @param $file (string) Name of the file containing the image or a '@' character followed by the image data string. To link an image without embedding it on the document, set an asterisk character before the URL (i.e.: '*http://www.example.com/image.jpg').
6771
	 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6772
	 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6773
	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6774
	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6775
	 * @param $type (string) Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
6776
	 * @param $link (mixed) URL or identifier returned by AddLink().
6777
	 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
6778
	 * @param $resize (mixed) If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling).
6779
	 * @param $dpi (int) dot-per-inch resolution used on resize
6780
	 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
6781
	 * @param $ismask (boolean) true if this image is a mask, false otherwise
6782
	 * @param $imgmask (mixed) image object returned by this function or false
6783
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6784
	 * @param $fitbox (mixed) If not false scale image dimensions proportionally to fit within the ($w, $h) box. $fitbox can be true or a 2 characters string indicating the image alignment inside the box. The first character indicate the horizontal alignment (L = left, C = center, R = right) the second character indicate the vertical algnment (T = top, M = middle, B = bottom).
6785
	 * @param $hidden (boolean) If true do not display the image.
6786
	 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
6787
	 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6788
	 * @param $altimgs (array) Array of alternate images IDs. Each alternative image must be an array with two values: an integer representing the image ID (the value returned by the Image method) and a boolean value to indicate if the image is the default for printing.
6789
	 * @return image information
6790
	 * @public
6791
	 * @since 1.1
6792
	 */
6793
	public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) {
6794
		if ($this->state != 2) {
6795
			return;
6796
		}
6797
		if ($x === '') {
6798
			$x = $this->x;
6799
		}
6800
		if ($y === '') {
6801
			$y = $this->y;
6802
		}
6803
		// check page for no-write regions and adapt page margins if necessary
6804
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
6805
		$exurl = ''; // external streams
6806
		// check if we are passing an image as file or string
6807
		if ($file[0] === '@') {
6808
			// image from string
6809
			$imgdata = substr($file, 1);
6810
		} else { // image file
6811
			if ($file{0} === '*') {
6812
				// image as external stream
6813
				$file = substr($file, 1);
6814
				$exurl = $file;
6815
			}
6816
			// check if is local file
6817
			if (!@file_exists($file)) {
6818
				// encode spaces on filename (file is probably an URL)
6819
				$file = str_replace(' ', '%20', $file);
6820
			}
6821
			if (@file_exists($file)) {
6822
				// get image dimensions
6823
				$imsize = @getimagesize($file);
6824
			} else {
6825
				$imsize = FALSE;
6826
			}
6827
			if ($imsize === FALSE) {
6828
				if (function_exists('curl_init')) {
6829
					// try to get remote file data using cURL
6830
					$cs = curl_init(); // curl session
6831
					curl_setopt($cs, CURLOPT_URL, $file);
6832
					curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
6833
					curl_setopt($cs, CURLOPT_FAILONERROR, true);
6834
					curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
6835
					if ((ini_get('open_basedir') == '') AND (!ini_get('safe_mode'))) {
6836
						curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true);
6837
					}
6838
					curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
6839
					curl_setopt($cs, CURLOPT_TIMEOUT, 30);
6840
					curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false);
6841
					curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false);
6842
					curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF');
6843
					$imgdata = curl_exec($cs);
6844
					curl_close($cs);
6845
				} else {
6846
					$imgdata = @file_get_contents($file);
6847
				}
6848
			}
6849
		}
6850
		if (isset($imgdata) AND ($imgdata !== FALSE)) {
6851
			// copy image to cache
6852
			$file = TCPDF_STATIC::getObjFilename('img');
6853
			$fp = fopen($file, 'w');
6854
			fwrite($fp, $imgdata);
6855
			fclose($fp);
6856
			unset($imgdata);
6857
			$imsize = @getimagesize($file);
6858
			if ($imsize === FALSE) {
6859
				unlink($file);
6860
			} else {
6861
				$this->cached_files[] = $file;
6862
			}
6863
		}
6864
		if ($imsize === FALSE) {
6865
			if (($w > 0) AND ($h > 0)) {
6866
				// get measures from specified data
6867
				$pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6868
				$ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6869
				$imsize = array($pw, $ph);
6870
			} else {
6871
				$this->Error('[Image] Unable to get image: '.$file);
6872
			}
6873
		}
6874
		// file hash
6875
		$filehash = md5($this->file_id.$file);
6876
		// get original image width and height in pixels
6877
		list($pixw, $pixh) = $imsize;
6878
		// calculate image width and height on document
6879
		if (($w <= 0) AND ($h <= 0)) {
6880
			// convert image size to document unit
6881
			$w = $this->pixelsToUnits($pixw);
6882
			$h = $this->pixelsToUnits($pixh);
6883
		} elseif ($w <= 0) {
6884
			$w = $h * $pixw / $pixh;
6885
		} elseif ($h <= 0) {
6886
			$h = $w * $pixh / $pixw;
6887
		} elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6888
			if (strlen($fitbox) !== 2) {
6889
				// set default alignment
6890
				$fitbox = '--';
6891
			}
6892
			// scale image dimensions proportionally to fit within the ($w, $h) box
6893
			if ((($w * $pixh) / ($h * $pixw)) < 1) {
6894
				// store current height
6895
				$oldh = $h;
6896
				// calculate new height
6897
				$h = $w * $pixh / $pixw;
6898
				// height difference
6899
				$hdiff = ($oldh - $h);
6900
				// vertical alignment
6901
				switch (strtoupper($fitbox{1})) {
6902
					case 'T': {
6903
						break;
6904
					}
6905
					case 'M': {
6906
						$y += ($hdiff / 2);
6907
						break;
6908
					}
6909
					case 'B': {
6910
						$y += $hdiff;
6911
						break;
6912
					}
6913
				}
6914
			} else {
6915
				// store current width
6916
				$oldw = $w;
6917
				// calculate new width
6918
				$w = $h * $pixw / $pixh;
6919
				// width difference
6920
				$wdiff = ($oldw - $w);
6921
				// horizontal alignment
6922
				switch (strtoupper($fitbox{0})) {
6923
					case 'L': {
6924
						if ($this->rtl) {
6925
							$x -= $wdiff;
6926
						}
6927
						break;
6928
					}
6929
					case 'C': {
6930
						if ($this->rtl) {
6931
							$x -= ($wdiff / 2);
6932
						} else {
6933
							$x += ($wdiff / 2);
6934
						}
6935
						break;
6936
					}
6937
					case 'R': {
6938
						if (!$this->rtl) {
6939
							$x += $wdiff;
6940
						}
6941
						break;
6942
					}
6943
				}
6944
			}
6945
		}
6946
		// fit the image on available space
6947
		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6948
		// calculate new minimum dimensions in pixels
6949
		$neww = round($w * $this->k * $dpi / $this->dpi);
6950
		$newh = round($h * $this->k * $dpi / $this->dpi);
6951
		// check if resize is necessary (resize is used only to reduce the image)
6952
		$newsize = ($neww * $newh);
6953
		$pixsize = ($pixw * $pixh);
6954
		if (intval($resize) == 2) {
6955
			$resize = true;
6956
		} elseif ($newsize >= $pixsize) {
6957
			$resize = false;
6958
		}
6959
		// check if image has been already added on document
6960
		$newimage = true;
6961
		if (in_array($file, $this->imagekeys)) {
6962
			$newimage = false;
6963
			// get existing image data
6964
			$info = $this->getImageBuffer($file);
6965
			if (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
6966
				// check if the newer image is larger
6967
				$oldsize = ($info['w'] * $info['h']);
6968
				if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6969
					$newimage = true;
6970
				}
6971
			}
6972
		} elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
6973
			// check for cached images with alpha channel
6974
			$tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
6975
			$tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
6976
			if (in_array($tempfile_plain, $this->imagekeys)) {
6977
				// get existing image data
6978
				$info = $this->getImageBuffer($tempfile_plain);
6979
				// check if the newer image is larger
6980
				$oldsize = ($info['w'] * $info['h']);
6981
				if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6982
					$newimage = true;
6983
				} else {
6984
					$newimage = false;
6985
					// embed mask image
6986
					$imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
6987
					// embed image, masked with previously embedded mask
6988
					return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
6989
				}
6990
			}
6991
		}
6992
		if ($newimage) {
6993
			//First use of image, get info
6994
			$type = strtolower($type);
6995
			if ($type == '') {
6996
				$type = TCPDF_IMAGES::getImageFileType($file, $imsize);
6997
			} elseif ($type == 'jpg') {
6998
				$type = 'jpeg';
6999
			}
7000
			$mqr = TCPDF_STATIC::get_mqr();
7001
			TCPDF_STATIC::set_mqr(false);
7002
			// Specific image handlers (defined on TCPDF_IMAGES CLASS)
7003
			$mtd = '_parse'.$type;
7004
			// GD image handler function
7005
			$gdfunction = 'imagecreatefrom'.$type;
7006
			$info = false;
7007
			if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7008
				// TCPDF image functions
7009
				$info = TCPDF_IMAGES::$mtd($file);
7010
				if ($info == 'pngalpha') {
7011
					return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7012
				}
7013
			}
7014
			if (!$info) {
7015
				if (function_exists($gdfunction)) {
7016
					// GD library
7017
					$img = $gdfunction($file);
7018
					if ($resize) {
7019
						$imgr = imagecreatetruecolor($neww, $newh);
7020
						if (($type == 'gif') OR ($type == 'png')) {
7021
							$imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img);
7022
						}
7023
						imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7024
						if (($type == 'gif') OR ($type == 'png')) {
7025
							$info = TCPDF_IMAGES::_toPNG($imgr);
7026
						} else {
7027
							$info = TCPDF_IMAGES::_toJPEG($imgr, $this->jpeg_quality);
7028
						}
7029
					} else {
7030
						if (($type == 'gif') OR ($type == 'png')) {
7031
							$info = TCPDF_IMAGES::_toPNG($img);
7032
						} else {
7033
							$info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality);
7034
						}
7035
					}
7036
				} elseif (extension_loaded('imagick')) {
7037
					// ImageMagick library
7038
					$img = new Imagick();
7039
					if ($type == 'SVG') {
7040
						// get SVG file content
7041
						$svgimg = file_get_contents($file);
7042
						// get width and height
7043
						$regs = array();
7044
						if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7045
							$svgtag = $regs[1];
7046
							$tmp = array();
7047
							if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7048
								$ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7049
								$owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7050
								$svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7051
							} else {
7052
								$ow = $w;
7053
							}
7054
							$tmp = array();
7055
							if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7056
								$oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7057
								$ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7058
								$svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7059
							} else {
7060
								$oh = $h;
7061
							}
7062
							$tmp = array();
7063
							if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7064
								$vbw = ($ow * $this->imgscale * $this->k);
7065
								$vbh = ($oh * $this->imgscale * $this->k);
7066
								$vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7067
								$svgtag = $vbox.$svgtag;
7068
							}
7069
							$svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7070
						}
7071
						$img->readImageBlob($svgimg);
7072
					} else {
7073
						$img->readImage($file);
7074
					}
7075
					if ($resize) {
7076
						$img->resizeImage($neww, $newh, 10, 1, false);
7077
					}
7078
					$img->setCompressionQuality($this->jpeg_quality);
7079
					$img->setImageFormat('jpeg');
7080
					$tempname = TCPDF_STATIC::getObjFilename('jpg');
7081
					$img->writeImage($tempname);
7082
					$info = TCPDF_IMAGES::_parsejpeg($tempname);
7083
					unlink($tempname);
7084
					$img->destroy();
7085
				} else {
7086
					return;
7087
				}
7088
			}
7089
			if ($info === false) {
7090
				//If false, we cannot process image
7091
				return;
7092
			}
7093
			TCPDF_STATIC::set_mqr($mqr);
7094
			if ($ismask) {
7095
				// force grayscale
7096
				$info['cs'] = 'DeviceGray';
7097
			}
7098
			if ($imgmask !== false) {
7099
				$info['masked'] = $imgmask;
7100
			}
7101
			if (!empty($exurl)) {
7102
				$info['exurl'] = $exurl;
7103
			}
7104
			// array of alternative images
7105
			$info['altimgs'] = $altimgs;
7106
			// add image to document
7107
			$info['i'] = $this->setImageBuffer($file, $info);
7108
		}
7109
		// set alignment
7110
		$this->img_rb_y = $y + $h;
7111
		// set alignment
7112
		if ($this->rtl) {
7113
			if ($palign == 'L') {
7114
				$ximg = $this->lMargin;
7115
			} elseif ($palign == 'C') {
7116
				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7117
			} elseif ($palign == 'R') {
7118
				$ximg = $this->w - $this->rMargin - $w;
7119
			} else {
7120
				$ximg = $x - $w;
7121
			}
7122
			$this->img_rb_x = $ximg;
7123
		} else {
7124
			if ($palign == 'L') {
7125
				$ximg = $this->lMargin;
7126
			} elseif ($palign == 'C') {
7127
				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7128
			} elseif ($palign == 'R') {
7129
				$ximg = $this->w - $this->rMargin - $w;
7130
			} else {
7131
				$ximg = $x;
7132
			}
7133
			$this->img_rb_x = $ximg + $w;
7134
		}
7135
		if ($ismask OR $hidden) {
7136
			// image is not displayed
7137
			return $info['i'];
7138
		}
7139
		$xkimg = $ximg * $this->k;
7140
		if (!$alt) {
7141
			// only non-alternative immages will be set
7142
			$this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
7143
		}
7144
		if (!empty($border)) {
7145
			$bx = $this->x;
7146
			$by = $this->y;
7147
			$this->x = $ximg;
7148
			if ($this->rtl) {
7149
				$this->x += $w;
7150
			}
7151
			$this->y = $y;
7152
			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7153
			$this->x = $bx;
7154
			$this->y = $by;
7155
		}
7156
		if ($link) {
7157
			$this->Link($ximg, $y, $w, $h, $link, 0);
7158
		}
7159
		// set pointer to align the next text/objects
7160
		switch($align) {
7161
			case 'T': {
7162
				$this->y = $y;
7163
				$this->x = $this->img_rb_x;
7164
				break;
7165
			}
7166
			case 'M': {
7167
				$this->y = $y + round($h/2);
7168
				$this->x = $this->img_rb_x;
7169
				break;
7170
			}
7171
			case 'B': {
7172
				$this->y = $this->img_rb_y;
7173
				$this->x = $this->img_rb_x;
7174
				break;
7175
			}
7176
			case 'N': {
7177
				$this->SetY($this->img_rb_y);
7178
				break;
7179
			}
7180
			default:{
7181
				break;
7182
			}
7183
		}
7184
		$this->endlinex = $this->img_rb_x;
7185
		if ($this->inxobj) {
7186
			// we are inside an XObject template
7187
			$this->xobjects[$this->xobjid]['images'][] = $info['i'];
7188
		}
7189
		return $info['i'];
7190
	}
7191
 
7192
	/**
7193
	 * Extract info from a PNG image with alpha channel using the GD library.
7194
	 * @param $file (string) Name of the file containing the image.
7195
	 * @param $x (float) Abscissa of the upper-left corner.
7196
	 * @param $y (float) Ordinate of the upper-left corner.
7197
	 * @param $wpx (float) Original width of the image in pixels.
7198
	 * @param $hpx (float) original height of the image in pixels.
7199
	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7200
	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7201
	 * @param $type (string) Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
7202
	 * @param $link (mixed) URL or identifier returned by AddLink().
7203
	 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
7204
	 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7205
	 * @param $dpi (int) dot-per-inch resolution used on resize
7206
	 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
7207
	 * @param $filehash (string) File hash used to build unique file names.
7208
	 * @author Nicola Asuni
7209
	 * @protected
7210
	 * @since 4.3.007 (2008-12-04)
7211
	 * @see Image()
7212
	 */
7213
	protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7214
		if (empty($filehash)) {
7215
			$filehash = md5($this->file_id.$file);
7216
		}
7217
		// create temp image file (without alpha channel)
7218
		$tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
7219
		// create temp alpha file
7220
		$tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
7221
		if (extension_loaded('imagick')) { // ImageMagick extension
7222
			// ImageMagick library
7223
			$img = new Imagick();
7224
			$img->readImage($file);
7225
			// clone image object
7226
			$imga = TCPDF_STATIC::objclone($img);
7227
			// extract alpha channel
7228
			if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7229
				$img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7230
			} else {
7231
				$img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7232
				$img->negateImage(true);
7233
			}
7234
			$img->setImageFormat('png');
7235
			$img->writeImage($tempfile_alpha);
7236
			// remove alpha channel
7237
			if (method_exists($imga, 'setImageMatte')) {
7238
				$imga->setImageMatte(false);
7239
			} else {
7240
				$imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7241
			}
7242
			$imga->setImageFormat('png');
7243
			$imga->writeImage($tempfile_plain);
7244
		} elseif (function_exists('imagecreatefrompng')) { // GD extension
7245
			// generate images
7246
			$img = imagecreatefrompng($file);
7247
			$imgalpha = imagecreate($wpx, $hpx);
7248
			// generate gray scale palette (0 -> 255)
7249
			for ($c = 0; $c < 256; ++$c) {
7250
				ImageColorAllocate($imgalpha, $c, $c, $c);
7251
			}
7252
			// extract alpha channel
7253
			for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7254
				for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7255
					$color = imagecolorat($img, $xpx, $ypx);
7256
					$alpha = $this->getGDgamma($color); // correct gamma
7257
					imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7258
				}
7259
			}
7260
			imagepng($imgalpha, $tempfile_alpha);
7261
			imagedestroy($imgalpha);
7262
			// extract image without alpha channel
7263
			$imgplain = imagecreatetruecolor($wpx, $hpx);
7264
			imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7265
			imagepng($imgplain, $tempfile_plain);
7266
			imagedestroy($imgplain);
7267
		} else {
7268
			$this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7269
		}
7270
		// embed mask image
7271
		$imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7272
		// embed image, masked with previously embedded mask
7273
		$this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7274
		// remove temp files
7275
		unlink($tempfile_alpha);
7276
		unlink($tempfile_plain);
7277
	}
7278
 
7279
	/**
7280
	 * Get the GD-corrected PNG gamma value from alpha color
7281
	 * @param $c (int) alpha color
7282
	 * @protected
7283
	 * @since 4.3.007 (2008-12-04)
7284
	 */
7285
	protected function getGDgamma($c) {
7286
		if (!isset($this->gdgammacache["'".$c."'"])) {
7287
			// shifts off the first 24 bits (where 8x3 are used for each color),
7288
			// and returns the remaining 7 allocated bits (commonly used for alpha)
7289
			$alpha = ($c >> 24);
7290
			// GD alpha is only 7 bit (0 -> 127)
7291
			$alpha = (((127 - $alpha) / 127) * 255);
7292
			// correct gamma
7293
			$this->gdgammacache["'".$c."'"] = (pow(($alpha / 255), 2.2) * 255);
7294
			// store the latest values on cache to improve performances
7295
			if (count($this->gdgammacache) > 8) {
7296
				// remove one element from the cache array
7297
				array_shift($this->gdgammacache);
7298
			}
7299
		}
7300
		return $this->gdgammacache["'".$c."'"];
7301
	}
7302
 
7303
	/**
7304
	 * Performs a line break.
7305
	 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7306
	 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7307
	 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7308
	 * @public
7309
	 * @since 1.0
7310
	 * @see Cell()
7311
	 */
7312
	public function Ln($h='', $cell=false) {
7313
		if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) {
7314
			// revove vertical space from the top of the column
7315
			return;
7316
		}
7317
		if ($cell) {
7318
			if ($this->rtl) {
7319
				$cellpadding = $this->cell_padding['R'];
7320
			} else {
7321
				$cellpadding = $this->cell_padding['L'];
7322
			}
7323
		} else {
7324
			$cellpadding = 0;
7325
		}
7326
		if ($this->rtl) {
7327
			$this->x = $this->w - $this->rMargin - $cellpadding;
7328
		} else {
7329
			$this->x = $this->lMargin + $cellpadding;
7330
		}
7331
		if (is_string($h)) {
7332
			$this->y += $this->lasth;
7333
		} else {
7334
			$this->y += $h;
7335
		}
7336
		$this->newline = true;
7337
	}
7338
 
7339
	/**
7340
	 * Returns the relative X value of current position.
7341
	 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7342
	 * @return float
7343
	 * @public
7344
	 * @since 1.2
7345
	 * @see SetX(), GetY(), SetY()
7346
	 */
7347
	public function GetX() {
7348
		//Get x position
7349
		if ($this->rtl) {
7350
			return ($this->w - $this->x);
7351
		} else {
7352
			return $this->x;
7353
		}
7354
	}
7355
 
7356
	/**
7357
	 * Returns the absolute X value of current position.
7358
	 * @return float
7359
	 * @public
7360
	 * @since 1.2
7361
	 * @see SetX(), GetY(), SetY()
7362
	 */
7363
	public function GetAbsX() {
7364
		return $this->x;
7365
	}
7366
 
7367
	/**
7368
	 * Returns the ordinate of the current position.
7369
	 * @return float
7370
	 * @public
7371
	 * @since 1.0
7372
	 * @see SetY(), GetX(), SetX()
7373
	 */
7374
	public function GetY() {
7375
		return $this->y;
7376
	}
7377
 
7378
	/**
7379
	 * Defines the abscissa of the current position.
7380
	 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7381
	 * @param $x (float) The value of the abscissa in user units.
7382
	 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7383
	 * @public
7384
	 * @since 1.2
7385
	 * @see GetX(), GetY(), SetY(), SetXY()
7386
	 */
7387
	public function SetX($x, $rtloff=false) {
7388
		$x = floatval($x);
7389
		if (!$rtloff AND $this->rtl) {
7390
			if ($x >= 0) {
7391
				$this->x = $this->w - $x;
7392
			} else {
7393
				$this->x = abs($x);
7394
			}
7395
		} else {
7396
			if ($x >= 0) {
7397
				$this->x = $x;
7398
			} else {
7399
				$this->x = $this->w + $x;
7400
			}
7401
		}
7402
		if ($this->x < 0) {
7403
			$this->x = 0;
7404
		}
7405
		if ($this->x > $this->w) {
7406
			$this->x = $this->w;
7407
		}
7408
	}
7409
 
7410
	/**
7411
	 * Moves the current abscissa back to the left margin and sets the ordinate.
7412
	 * If the passed value is negative, it is relative to the bottom of the page.
7413
	 * @param $y (float) The value of the ordinate in user units.
7414
	 * @param $resetx (bool) if true (default) reset the X position.
7415
	 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7416
	 * @public
7417
	 * @since 1.0
7418
	 * @see GetX(), GetY(), SetY(), SetXY()
7419
	 */
7420
	public function SetY($y, $resetx=true, $rtloff=false) {
7421
		$y = floatval($y);
7422
		if ($resetx) {
7423
			//reset x
7424
			if (!$rtloff AND $this->rtl) {
7425
				$this->x = $this->w - $this->rMargin;
7426
			} else {
7427
				$this->x = $this->lMargin;
7428
			}
7429
		}
7430
		if ($y >= 0) {
7431
			$this->y = $y;
7432
		} else {
7433
			$this->y = $this->h + $y;
7434
		}
7435
		if ($this->y < 0) {
7436
			$this->y = 0;
7437
		}
7438
		if ($this->y > $this->h) {
7439
			$this->y = $this->h;
7440
		}
7441
	}
7442
 
7443
	/**
7444
	 * Defines the abscissa and ordinate of the current position.
7445
	 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7446
	 * @param $x (float) The value of the abscissa.
7447
	 * @param $y (float) The value of the ordinate.
7448
	 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7449
	 * @public
7450
	 * @since 1.2
7451
	 * @see SetX(), SetY()
7452
	 */
7453
	public function SetXY($x, $y, $rtloff=false) {
7454
		$this->SetY($y, false, $rtloff);
7455
		$this->SetX($x, $rtloff);
7456
	}
7457
 
7458
	/**
7459
	 * Set the absolute X coordinate of the current pointer.
7460
	 * @param $x (float) The value of the abscissa in user units.
7461
	 * @public
7462
	 * @since 5.9.186 (2012-09-13)
7463
	 * @see setAbsX(), setAbsY(), SetAbsXY()
7464
	 */
7465
	public function SetAbsX($x) {
7466
		$this->x = floatval($x);
7467
	}
7468
 
7469
	/**
7470
	 * Set the absolute Y coordinate of the current pointer.
7471
	 * @param $y (float) (float) The value of the ordinate in user units.
7472
	 * @public
7473
	 * @since 5.9.186 (2012-09-13)
7474
	 * @see setAbsX(), setAbsY(), SetAbsXY()
7475
	 */
7476
	public function SetAbsY($y) {
7477
		$this->y = floatval($y);
7478
	}
7479
 
7480
	/**
7481
	 * Set the absolute X and Y coordinates of the current pointer.
7482
	 * @param $x (float) The value of the abscissa in user units.
7483
	 * @param $y (float) (float) The value of the ordinate in user units.
7484
	 * @public
7485
	 * @since 5.9.186 (2012-09-13)
7486
	 * @see setAbsX(), setAbsY(), SetAbsXY()
7487
	 */
7488
	public function SetAbsXY($x, $y) {
7489
		$this->SetAbsX($x);
7490
		$this->SetAbsY($y);
7491
	}
7492
 
7493
	/**
7494
	 * Send the document to a given destination: string, local file or browser.
7495
	 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7496
	 * The method first calls Close() if necessary to terminate the document.
7497
	 * @param $name (string) The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
7498
	 * @param $dest (string) Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string (name is ignored).</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li><li>E: return the document as base64 mime multi-part email attachment (RFC 2045)</li></ul>
7499
	 * @public
7500
	 * @since 1.0
7501
	 * @see Close()
7502
	 */
7503
	public function Output($name='doc.pdf', $dest='I') {
7504
		//Output PDF to some destination
7505
		//Finish document if necessary
7506
		if ($this->state < 3) {
7507
			$this->Close();
7508
		}
7509
		//Normalize parameters
7510
		if (is_bool($dest)) {
7511
			$dest = $dest ? 'D' : 'F';
7512
		}
7513
		$dest = strtoupper($dest);
7514
		if ($dest{0} != 'F') {
7515
			$name = preg_replace('/[\s]+/', '_', $name);
7516
			$name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7517
		}
7518
		if ($this->sign) {
7519
			// *** apply digital signature to the document ***
7520
			// get the document content
7521
			$pdfdoc = $this->getBuffer();
7522
			// remove last newline
7523
			$pdfdoc = substr($pdfdoc, 0, -1);
7524
			// Remove the original buffer
7525
			if (isset($this->diskcache) AND $this->diskcache) {
7526
				// remove buffer file from cache
7527
				unlink($this->buffer);
7528
			}
7529
			unset($this->buffer);
7530
			// remove filler space
7531
			$byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7532
			// define the ByteRange
7533
			$byte_range = array();
7534
			$byte_range[0] = 0;
7535
			$byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7536
			$byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7537
			$byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7538
			$pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7539
			// replace the ByteRange
7540
			$byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7541
			$byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7542
			$pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7543
			// write the document to a temporary folder
7544
			$tempdoc = TCPDF_STATIC::getObjFilename('tmppdf');
7545
			$f = fopen($tempdoc, 'wb');
7546
			if (!$f) {
7547
				$this->Error('Unable to create temporary file: '.$tempdoc);
7548
			}
7549
			$pdfdoc_length = strlen($pdfdoc);
7550
			fwrite($f, $pdfdoc, $pdfdoc_length);
7551
			fclose($f);
7552
			// get digital signature via openssl library
7553
			$tempsign = TCPDF_STATIC::getObjFilename('tmpsig');
7554
			if (empty($this->signature_data['extracerts'])) {
7555
				openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7556
			} else {
7557
				openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
7558
			}
7559
			unlink($tempdoc);
7560
			// read signature
7561
			$signature = file_get_contents($tempsign);
7562
			unlink($tempsign);
7563
			// extract signature
7564
			$signature = substr($signature, $pdfdoc_length);
7565
			$signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7566
			$tmparr = explode("\n\n", $signature);
7567
			$signature = $tmparr[1];
7568
			unset($tmparr);
7569
			// decode signature
7570
			$signature = base64_decode(trim($signature));
7571
			// convert signature to hex
7572
			$signature = current(unpack('H*', $signature));
7573
			$signature = str_pad($signature, $this->signature_max_length, '0');
7574
			// disable disk caching
7575
			$this->diskcache = false;
7576
			// Add signature to the document
7577
			$this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7578
			$this->bufferlen = strlen($this->buffer);
7579
		}
7580
		switch($dest) {
7581
			case 'I': {
7582
				// Send PDF to the standard output
7583
				if (ob_get_contents()) {
7584
					$this->Error('Some data has already been output, can\'t send PDF file');
7585
				}
7586
				if (php_sapi_name() != 'cli') {
7587
					// send output to a browser
7588
					header('Content-Type: application/pdf');
7589
					if (headers_sent()) {
7590
						$this->Error('Some data has already been output to browser, can\'t send PDF file');
7591
					}
7592
					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7593
					//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7594
					header('Pragma: public');
7595
					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7596
					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7597
					header('Content-Disposition: inline; filename="'.basename($name).'"');
7598
					TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7599
				} else {
7600
					echo $this->getBuffer();
7601
				}
7602
				break;
7603
			}
7604
			case 'D': {
7605
				// download PDF as file
7606
				if (ob_get_contents()) {
7607
					$this->Error('Some data has already been output, can\'t send PDF file');
7608
				}
7609
				header('Content-Description: File Transfer');
7610
				if (headers_sent()) {
7611
					$this->Error('Some data has already been output to browser, can\'t send PDF file');
7612
				}
7613
				header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7614
				//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7615
				header('Pragma: public');
7616
				header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7617
				header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7618
				// force download dialog
7619
				if (strpos(php_sapi_name(), 'cgi') === false) {
7620
					header('Content-Type: application/force-download');
7621
					header('Content-Type: application/octet-stream', false);
7622
					header('Content-Type: application/download', false);
7623
					header('Content-Type: application/pdf', false);
7624
				} else {
7625
					header('Content-Type: application/pdf');
7626
				}
7627
				// use the Content-Disposition header to supply a recommended filename
7628
				header('Content-Disposition: attachment; filename="'.basename($name).'"');
7629
				header('Content-Transfer-Encoding: binary');
7630
				TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7631
				break;
7632
			}
7633
			case 'F':
7634
			case 'FI':
7635
			case 'FD': {
7636
				// save PDF to a local file
7637
				if ($this->diskcache) {
7638
					copy($this->buffer, $name);
7639
				} else {
7640
					$f = fopen($name, 'wb');
7641
					if (!$f) {
7642
						$this->Error('Unable to create output file: '.$name);
7643
					}
7644
					fwrite($f, $this->getBuffer(), $this->bufferlen);
7645
					fclose($f);
7646
				}
7647
				if ($dest == 'FI') {
7648
					// send headers to browser
7649
					header('Content-Type: application/pdf');
7650
					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7651
					//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7652
					header('Pragma: public');
7653
					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7654
					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7655
					header('Content-Disposition: inline; filename="'.basename($name).'"');
7656
					TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7657
				} elseif ($dest == 'FD') {
7658
					// send headers to browser
7659
					if (ob_get_contents()) {
7660
						$this->Error('Some data has already been output, can\'t send PDF file');
7661
					}
7662
					header('Content-Description: File Transfer');
7663
					if (headers_sent()) {
7664
						$this->Error('Some data has already been output to browser, can\'t send PDF file');
7665
					}
7666
					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7667
					header('Pragma: public');
7668
					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7669
					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7670
					// force download dialog
7671
					if (strpos(php_sapi_name(), 'cgi') === false) {
7672
						header('Content-Type: application/force-download');
7673
						header('Content-Type: application/octet-stream', false);
7674
						header('Content-Type: application/download', false);
7675
						header('Content-Type: application/pdf', false);
7676
					} else {
7677
						header('Content-Type: application/pdf');
7678
					}
7679
					// use the Content-Disposition header to supply a recommended filename
7680
					header('Content-Disposition: attachment; filename="'.basename($name).'"');
7681
					header('Content-Transfer-Encoding: binary');
7682
					TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7683
				}
7684
				break;
7685
			}
7686
			case 'E': {
7687
				// return PDF as base64 mime multi-part email attachment (RFC 2045)
7688
				$retval = 'Content-Type: application/pdf;'."\r\n";
7689
				$retval .= ' name="'.$name.'"'."\r\n";
7690
				$retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7691
				$retval .= 'Content-Disposition: attachment;'."\r\n";
7692
				$retval .= ' filename="'.$name.'"'."\r\n\r\n";
7693
				$retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7694
				return $retval;
7695
			}
7696
			case 'S': {
7697
				// returns PDF as a string
7698
				return $this->getBuffer();
7699
			}
7700
			default: {
7701
				$this->Error('Incorrect output destination: '.$dest);
7702
			}
7703
		}
7704
		return '';
7705
	}
7706
 
7707
	/**
7708
	 * Unset all class variables except the following critical variables.
7709
	 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
7710
	 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
7711
	 * @public
7712
	 * @since 4.5.016 (2009-02-24)
7713
	 */
7714
	public function _destroy($destroyall=false, $preserve_objcopy=false) {
7715
		if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!TCPDF_STATIC::empty_string($this->buffer))) {
7716
			// remove buffer file from cache
7717
			unlink($this->buffer);
7718
		}
7719
		if ($destroyall AND isset($this->cached_files) AND !empty($this->cached_files)) {
7720
			// remove cached files
7721
			foreach ($this->cached_files as $cachefile) {
7722
				if (is_file($cachefile)) {
7723
					unlink($cachefile);
7724
				}
7725
			}
7726
			unset($this->cached_files);
7727
		}
7728
		foreach (array_keys(get_object_vars($this)) as $val) {
7729
			if ($destroyall OR (
7730
				($val != 'internal_encoding')
7731
				AND ($val != 'state')
7732
				AND ($val != 'bufferlen')
7733
				AND ($val != 'buffer')
7734
				AND ($val != 'diskcache')
7735
				AND ($val != 'cached_files')
7736
				AND ($val != 'sign')
7737
				AND ($val != 'signature_data')
7738
				AND ($val != 'signature_max_length')
7739
				AND ($val != 'byterange_string')
7740
				)) {
7741
				if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7742
					unset($this->$val);
7743
				}
7744
			}
7745
		}
7746
	}
7747
 
7748
	/**
7749
	 * Check for locale-related bug
7750
	 * @protected
7751
	 */
7752
	protected function _dochecks() {
7753
		//Check for locale-related bug
7754
		if (1.1 == 1) {
7755
			$this->Error('Don\'t alter the locale before including class file');
7756
		}
7757
		//Check for decimal separator
7758
		if (sprintf('%.1F', 1.0) != '1.0') {
7759
			setlocale(LC_NUMERIC, 'C');
7760
		}
7761
	}
7762
 
7763
	/**
7764
	 * Return an array containing variations for the basic page number alias.
7765
	 * @param $a (string) Base alias.
7766
	 * @return array of page number aliases
7767
	 * @protected
7768
	 */
7769
	protected function getInternalPageNumberAliases($a= '') {
7770
		$alias = array();
7771
		// build array of Unicode + ASCII variants (the order is important)
7772
		$alias = array('u' => array(), 'a' => array());
7773
		$u = '{'.$a.'}';
7774
		$alias['u'][] = TCPDF_STATIC::_escape($u);
7775
		if ($this->isunicode) {
7776
			$alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7777
			$alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7778
			$alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7779
			$alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7780
		}
7781
		$alias['a'][] = TCPDF_STATIC::_escape($a);
7782
		return $alias;
7783
	}
7784
 
7785
	/**
7786
	 * Return an array containing all internal page aliases.
7787
	 * @return array of page number aliases
7788
	 * @protected
7789
	 */
7790
	protected function getAllInternalPageNumberAliases() {
7791
		$basic_alias = array(TCPDF_STATIC::$alias_tot_pages, TCPDF_STATIC::$alias_num_page, TCPDF_STATIC::$alias_group_tot_pages, TCPDF_STATIC::$alias_group_num_page, TCPDF_STATIC::$alias_right_shift);
7792
		$pnalias = array();
7793
		foreach($basic_alias as $k => $a) {
7794
			$pnalias[$k] = $this->getInternalPageNumberAliases($a);
7795
		}
7796
		return $pnalias;
7797
	}
7798
 
7799
	/**
7800
	 * Replace right shift page number aliases with spaces to correct right alignment.
7801
	 * This works perfectly only when using monospaced fonts.
7802
	 * @param $page (string) Page content.
7803
	 * @param $aliases (array) Array of page aliases.
7804
	 * @param $diff (int) initial difference to add.
7805
	 * @return replaced page content.
7806
	 * @protected
7807
	 */
7808
	protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7809
		foreach ($aliases as $type => $alias) {
7810
			foreach ($alias as $a) {
7811
				// find position of compensation factor
7812
				$startnum = (strpos($a, ':') + 1);
7813
				$a = substr($a, 0, $startnum);
7814
				if (($pos = strpos($page, $a)) !== false) {
7815
					// end of alias
7816
					$endnum = strpos($page, '}', $pos);
7817
					// string to be replaced
7818
					$aa = substr($page, $pos, ($endnum - $pos + 1));
7819
					// get compensation factor
7820
					$ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7821
					$ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7822
					$ratio = floatval($ratio);
7823
					if ($type == 'u') {
7824
						$chrdiff = floor(($diff + 12) * $ratio);
7825
						$shift = str_repeat(' ', $chrdiff);
7826
						$shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7827
					} else {
7828
						$chrdiff = floor(($diff + 11) * $ratio);
7829
						$shift = str_repeat(' ', $chrdiff);
7830
					}
7831
					$page = str_replace($aa, $shift, $page);
7832
				}
7833
			}
7834
		}
7835
		return $page;
7836
	}
7837
 
7838
	/**
7839
	 * Set page boxes to be included on page descriptions.
7840
	 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7841
	 * @protected
7842
	 */
7843
	protected function setPageBoxTypes($boxes) {
7844
		$this->page_boxes = array();
7845
		foreach ($boxes as $box) {
7846
			if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7847
				$this->page_boxes[] = $box;
7848
			}
7849
		}
7850
	}
7851
 
7852
	/**
7853
	 * Output pages (and replace page number aliases).
7854
	 * @protected
7855
	 */
7856
	protected function _putpages() {
7857
		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7858
		// get internal aliases for page numbers
7859
		$pnalias = $this->getAllInternalPageNumberAliases();
7860
		$num_pages = $this->numpages;
7861
		$ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7862
		$ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7863
		$ptp_num_chars = $this->GetNumChars($ptpa);
7864
		$pagegroupnum = 0;
7865
		$groupnum = 0;
7866
		$ptgu = 1;
7867
		$ptga = 1;
7868
		for ($n = 1; $n <= $num_pages; ++$n) {
7869
			// get current page
7870
			$temppage = $this->getPageBuffer($n);
7871
			$pagelen = strlen($temppage);
7872
			// set replacements for total pages number
7873
			$pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7874
			$pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7875
			$pnp_num_chars = $this->GetNumChars($pnpa);
7876
			$pdiff = 0; // difference used for right shift alignment of page numbers
7877
			$gdiff = 0; // difference used for right shift alignment of page group numbers
7878
			if (!empty($this->pagegroups)) {
7879
				if (isset($this->newpagegroup[$n])) {
7880
					$pagegroupnum = 0;
7881
					++$groupnum;
7882
					$ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7883
					$ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7884
					$ptg_num_chars = $this->GetNumChars($ptga);
7885
				}
7886
				++$pagegroupnum;
7887
				$pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7888
				$pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7889
				$png_num_chars = $this->GetNumChars($pnga);
7890
				// replace page numbers
7891
				$replace = array();
7892
				$replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7893
				$replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7894
				$replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7895
				$replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7896
				list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7897
			}
7898
			// replace page numbers
7899
			$replace = array();
7900
			$replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7901
			$replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7902
			$replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7903
			$replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7904
			list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7905
			// replace right shift alias
7906
			$temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7907
			// replace EPS marker
7908
			$temppage = str_replace($this->epsmarker, '', $temppage);
7909
			//Page
7910
			$this->page_obj_id[$n] = $this->_newobj();
7911
			$out = '<<';
7912
			$out .= ' /Type /Page';
7913
			$out .= ' /Parent 1 0 R';
7914
			$out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7915
			$out .= ' /Resources 2 0 R';
7916
			foreach ($this->page_boxes as $box) {
7917
				$out .= ' /'.$box;
7918
				$out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
7919
			}
7920
			if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7921
				$out .= ' /BoxColorInfo <<';
7922
				foreach ($this->page_boxes as $box) {
7923
					if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7924
						$out .= ' /'.$box.' <<';
7925
						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7926
							$color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7927
							$out .= ' /C [';
7928
							$out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7929
							$out .= ' ]';
7930
						}
7931
						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7932
							$out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7933
						}
7934
						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7935
							$out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7936
						}
7937
						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7938
							$dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7939
							$out .= ' /D [';
7940
							foreach ($dashes as $dash) {
7941
								$out .= sprintf(' %F', ($dash * $this->k));
7942
							}
7943
							$out .= ' ]';
7944
						}
7945
						$out .= ' >>';
7946
					}
7947
				}
7948
				$out .= ' >>';
7949
			}
7950
			$out .= ' /Contents '.($this->n + 1).' 0 R';
7951
			$out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7952
			if (!$this->pdfa_mode) {
7953
				$out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7954
			}
7955
			if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7956
				// page transitions
7957
				if (isset($this->pagedim[$n]['trans']['Dur'])) {
7958
					$out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
7959
				}
7960
				$out .= ' /Trans <<';
7961
				$out .= ' /Type /Trans';
7962
				if (isset($this->pagedim[$n]['trans']['S'])) {
7963
					$out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
7964
				}
7965
				if (isset($this->pagedim[$n]['trans']['D'])) {
7966
					$out .= ' /D '.$this->pagedim[$n]['trans']['D'];
7967
				}
7968
				if (isset($this->pagedim[$n]['trans']['Dm'])) {
7969
					$out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
7970
				}
7971
				if (isset($this->pagedim[$n]['trans']['M'])) {
7972
					$out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
7973
				}
7974
				if (isset($this->pagedim[$n]['trans']['Di'])) {
7975
					$out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
7976
				}
7977
				if (isset($this->pagedim[$n]['trans']['SS'])) {
7978
					$out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
7979
				}
7980
				if (isset($this->pagedim[$n]['trans']['B'])) {
7981
					$out .= ' /B '.$this->pagedim[$n]['trans']['B'];
7982
				}
7983
				$out .= ' >>';
7984
			}
7985
			$out .= $this->_getannotsrefs($n);
7986
			$out .= ' /PZ '.$this->pagedim[$n]['PZ'];
7987
			$out .= ' >>';
7988
			$out .= "\n".'endobj';
7989
			$this->_out($out);
7990
			//Page content
7991
			$p = ($this->compress) ? gzcompress($temppage) : $temppage;
7992
			$this->_newobj();
7993
			$p = $this->_getrawstream($p);
7994
			$this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
7995
			if ($this->diskcache) {
7996
				// remove temporary files
7997
				unlink($this->pages[$n]);
7998
			}
7999
		}
8000
		//Pages root
8001
		$out = $this->_getobj(1)."\n";
8002
		$out .= '<< /Type /Pages /Kids [';
8003
		foreach($this->page_obj_id as $page_obj) {
8004
			$out .= ' '.$page_obj.' 0 R';
8005
		}
8006
		$out .= ' ] /Count '.$num_pages.' >>';
8007
		$out .= "\n".'endobj';
8008
		$this->_out($out);
8009
	}
8010
 
8011
	/**
8012
	 * Output references to page annotations
8013
	 * @param $n (int) page number
8014
	 * @protected
8015
	 * @author Nicola Asuni
8016
	 * @since 4.7.000 (2008-08-29)
8017
	 * @deprecated
8018
	 */
8019
	protected function _putannotsrefs($n) {
8020
		$this->_out($this->_getannotsrefs($n));
8021
	}
8022
 
8023
	/**
8024
	 * Get references to page annotations.
8025
	 * @param $n (int) page number
8026
	 * @return string
8027
	 * @protected
8028
	 * @author Nicola Asuni
8029
	 * @since 5.0.010 (2010-05-17)
8030
	 */
8031
	protected function _getannotsrefs($n) {
8032
		if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8033
			return '';
8034
		}
8035
		$out = ' /Annots [';
8036
		if (isset($this->PageAnnots[$n])) {
8037
			foreach ($this->PageAnnots[$n] as $key => $val) {
8038
				if (!in_array($val['n'], $this->radio_groups)) {
8039
					$out .= ' '.$val['n'].' 0 R';
8040
				}
8041
			}
8042
			// add radiobutton groups
8043
			if (isset($this->radiobutton_groups[$n])) {
8044
				foreach ($this->radiobutton_groups[$n] as $key => $data) {
8045
					if (isset($data['n'])) {
8046
						$out .= ' '.$data['n'].' 0 R';
8047
					}
8048
				}
8049
			}
8050
		}
8051
		if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8052
			// set reference for signature object
8053
			$out .= ' '.$this->sig_obj_id.' 0 R';
8054
		}
8055
		if (!empty($this->empty_signature_appearance)) {
8056
			foreach ($this->empty_signature_appearance as $esa) {
8057
				if ($esa['page'] == $n) {
8058
					// set reference for empty signature objects
8059
					$out .= ' '.$esa['objid'].' 0 R';
8060
				}
8061
			}
8062
		}
8063
		$out .= ' ]';
8064
		return $out;
8065
	}
8066
 
8067
	/**
8068
	 * Output annotations objects for all pages.
8069
	 * !!! THIS METHOD IS NOT YET COMPLETED !!!
8070
	 * See section 12.5 of PDF 32000_2008 reference.
8071
	 * @protected
8072
	 * @author Nicola Asuni
8073
	 * @since 4.0.018 (2008-08-06)
8074
	 */
8075
	protected function _putannotsobjs() {
8076
		// reset object counter
8077
		for ($n=1; $n <= $this->numpages; ++$n) {
8078
			if (isset($this->PageAnnots[$n])) {
8079
				// set page annotations
8080
				foreach ($this->PageAnnots[$n] as $key => $pl) {
8081
					$annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8082
					// create annotation object for grouping radiobuttons
8083
					if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8084
						$radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8085
						$annots = '<<';
8086
						$annots .= ' /Type /Annot';
8087
						$annots .= ' /Subtype /Widget';
8088
						$annots .= ' /Rect [0 0 0 0]';
8089
						if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8090
							// read only
8091
							$annots .= ' /F 68';
8092
							$annots .= ' /Ff 49153';
8093
						} else {
8094
							$annots .= ' /F 4'; // default print for PDF/A
8095
							$annots .= ' /Ff 49152';
8096
						}
8097
						$annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8098
						if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8099
							$annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8100
						}
8101
						$annots .= ' /FT /Btn';
8102
						$annots .= ' /Kids [';
8103
						$defval = '';
8104
						foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8105
							if (isset($data['kid'])) {
8106
								$annots .= ' '.$data['kid'].' 0 R';
8107
								if ($data['def'] !== 'Off') {
8108
									$defval = $data['def'];
8109
								}
8110
							}
8111
						}
8112
						$annots .= ' ]';
8113
						if (!empty($defval)) {
8114
							$annots .= ' /V /'.$defval;
8115
						}
8116
						$annots .= ' >>';
8117
						$this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8118
						$this->form_obj_id[] = $radio_button_obj_id;
8119
						// store object id to be used on Parent entry of Kids
8120
						$this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8121
					}
8122
					$formfield = false;
8123
					$pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8124
					$a = $pl['x'] * $this->k;
8125
					$b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8126
					$c = $pl['w'] * $this->k;
8127
					$d = $pl['h'] * $this->k;
8128
					$rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8129
					// create new annotation object
8130
					$annots = '<</Type /Annot';
8131
					$annots .= ' /Subtype /'.$pl['opt']['subtype'];
8132
					$annots .= ' /Rect ['.$rect.']';
8133
					$ft = array('Btn', 'Tx', 'Ch', 'Sig');
8134
					if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8135
						$annots .= ' /FT /'.$pl['opt']['ft'];
8136
						$formfield = true;
8137
					}
8138
					$annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8139
					$annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8140
					$annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8141
					$annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8142
					if (isset($pl['opt']['f'])) {
8143
						$fval = 0;
8144
						if (is_array($pl['opt']['f'])) {
8145
							foreach ($pl['opt']['f'] as $f) {
8146
								switch (strtolower($f)) {
8147
									case 'invisible': {
8148
										$fval += 1 << 0;
8149
										break;
8150
									}
8151
									case 'hidden': {
8152
										$fval += 1 << 1;
8153
										break;
8154
									}
8155
									case 'print': {
8156
										$fval += 1 << 2;
8157
										break;
8158
									}
8159
									case 'nozoom': {
8160
										$fval += 1 << 3;
8161
										break;
8162
									}
8163
									case 'norotate': {
8164
										$fval += 1 << 4;
8165
										break;
8166
									}
8167
									case 'noview': {
8168
										$fval += 1 << 5;
8169
										break;
8170
									}
8171
									case 'readonly': {
8172
										$fval += 1 << 6;
8173
										break;
8174
									}
8175
									case 'locked': {
8176
										$fval += 1 << 8;
8177
										break;
8178
									}
8179
									case 'togglenoview': {
8180
										$fval += 1 << 9;
8181
										break;
8182
									}
8183
									case 'lockedcontents': {
8184
										$fval += 1 << 10;
8185
										break;
8186
									}
8187
									default: {
8188
										break;
8189
									}
8190
								}
8191
							}
8192
						} else {
8193
							$fval = intval($pl['opt']['f']);
8194
						}
8195
					} else {
8196
						$fval = 4;
8197
					}
8198
					if ($this->pdfa_mode) {
8199
						// force print flag for PDF/A mode
8200
						$fval |= 4;
8201
					}
8202
					$annots .= ' /F '.intval($fval);
8203
					if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8204
						$annots .= ' /AS /'.$pl['opt']['as'];
8205
					}
8206
					if (isset($pl['opt']['ap'])) {
8207
						// appearance stream
8208
						$annots .= ' /AP <<';
8209
						if (is_array($pl['opt']['ap'])) {
8210
							foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8211
								// $apmode can be: n = normal; r = rollover; d = down;
8212
								$annots .= ' /'.strtoupper($apmode);
8213
								if (is_array($apdef)) {
8214
									$annots .= ' <<';
8215
									foreach ($apdef as $apstate => $stream) {
8216
										// reference to XObject that define the appearance for this mode-state
8217
										$apsobjid = $this->_putAPXObject($c, $d, $stream);
8218
										$annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8219
									}
8220
									$annots .= ' >>';
8221
								} else {
8222
									// reference to XObject that define the appearance for this mode
8223
									$apsobjid = $this->_putAPXObject($c, $d, $apdef);
8224
									$annots .= ' '.$apsobjid.' 0 R';
8225
								}
8226
							}
8227
						} else {
8228
							$annots .= $pl['opt']['ap'];
8229
						}
8230
						$annots .= ' >>';
8231
					}
8232
					if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8233
						$annots .= ' /BS <<';
8234
						$annots .= ' /Type /Border';
8235
						if (isset($pl['opt']['bs']['w'])) {
8236
							$annots .= ' /W '.intval($pl['opt']['bs']['w']);
8237
						}
8238
						$bstyles = array('S', 'D', 'B', 'I', 'U');
8239
						if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8240
							$annots .= ' /S /'.$pl['opt']['bs']['s'];
8241
						}
8242
						if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8243
							$annots .= ' /D [';
8244
							foreach ($pl['opt']['bs']['d'] as $cord) {
8245
								$annots .= ' '.intval($cord);
8246
							}
8247
							$annots .= ']';
8248
						}
8249
						$annots .= ' >>';
8250
					} else {
8251
						$annots .= ' /Border [';
8252
						if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8253
							$annots .= intval($pl['opt']['border'][0]).' ';
8254
							$annots .= intval($pl['opt']['border'][1]).' ';
8255
							$annots .= intval($pl['opt']['border'][2]);
8256
							if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8257
								$annots .= ' [';
8258
								foreach ($pl['opt']['border'][3] as $dash) {
8259
									$annots .= intval($dash).' ';
8260
								}
8261
								$annots .= ']';
8262
							}
8263
						} else {
8264
							$annots .= '0 0 0';
8265
						}
8266
						$annots .= ']';
8267
					}
8268
					if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8269
						$annots .= ' /BE <<';
8270
						$bstyles = array('S', 'C');
8271
						if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8272
							$annots .= ' /S /'.$pl['opt']['bs']['s'];
8273
						} else {
8274
							$annots .= ' /S /S';
8275
						}
8276
						if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8277
							$annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8278
						}
8279
						$annots .= '>>';
8280
					}
8281
					if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8282
						$annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8283
					}
8284
					//$annots .= ' /StructParent ';
8285
					//$annots .= ' /OC ';
8286
					$markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8287
					if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8288
						// this is a markup type
8289
						if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8290
							$annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8291
						}
8292
						//$annots .= ' /Popup ';
8293
						if (isset($pl['opt']['ca'])) {
8294
							$annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8295
						}
8296
						if (isset($pl['opt']['rc'])) {
8297
							$annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8298
						}
8299
						$annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8300
						//$annots .= ' /IRT ';
8301
						if (isset($pl['opt']['subj'])) {
8302
							$annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8303
						}
8304
						//$annots .= ' /RT ';
8305
						//$annots .= ' /IT ';
8306
						//$annots .= ' /ExData ';
8307
					}
8308
					$lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8309
					// Annotation types
8310
					switch (strtolower($pl['opt']['subtype'])) {
8311
						case 'text': {
8312
							if (isset($pl['opt']['open'])) {
8313
								$annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8314
							}
8315
							$iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8316
							if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8317
								$annots .= ' /Name /'.$pl['opt']['name'];
8318
							} else {
8319
								$annots .= ' /Name /Note';
8320
							}
8321
							$statemodels = array('Marked', 'Review');
8322
							if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8323
								$annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8324
							} else {
8325
								$pl['opt']['statemodel'] = 'Marked';
8326
								$annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8327
							}
8328
							if ($pl['opt']['statemodel'] == 'Marked') {
8329
								$states = array('Accepted', 'Unmarked');
8330
							} else {
8331
								$states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8332
							}
8333
							if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8334
								$annots .= ' /State /'.$pl['opt']['state'];
8335
							} else {
8336
								if ($pl['opt']['statemodel'] == 'Marked') {
8337
									$annots .= ' /State /Unmarked';
8338
								} else {
8339
									$annots .= ' /State /None';
8340
								}
8341
							}
8342
							break;
8343
						}
8344
						case 'link': {
8345
							if (is_string($pl['txt'])) {
8346
								if ($pl['txt'][0] == '#') {
8347
									// internal destination
8348
									$annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8349
								} elseif ($pl['txt'][0] == '%') {
8350
									// embedded PDF file
8351
									$filename = basename(substr($pl['txt'], 1));
8352
									$annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8353
								} elseif ($pl['txt'][0] == '*') {
8354
									// embedded generic file
8355
									$filename = basename(substr($pl['txt'], 1));
8356
									$jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
8357
									$annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8358
								} else {
8359
									// external URI link
8360
									$annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8361
								}
8362
							} elseif (isset($this->links[$pl['txt']])) {
8363
								// internal link ID
8364
								$l = $this->links[$pl['txt']];
8365
								if (isset($this->page_obj_id[($l[0])])) {
8366
									$annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
8367
								}
8368
							}
8369
							$hmodes = array('N', 'I', 'O', 'P');
8370
							if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8371
								$annots .= ' /H /'.$pl['opt']['h'];
8372
							} else {
8373
								$annots .= ' /H /I';
8374
							}
8375
							//$annots .= ' /PA ';
8376
							//$annots .= ' /Quadpoints ';
8377
							break;
8378
						}
8379
						case 'freetext': {
8380
							if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8381
								$annots .= ' /DA ('.$pl['opt']['da'].')';
8382
							}
8383
							if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8384
								$annots .= ' /Q '.intval($pl['opt']['q']);
8385
							}
8386
							if (isset($pl['opt']['rc'])) {
8387
								$annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8388
							}
8389
							if (isset($pl['opt']['ds'])) {
8390
								$annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8391
							}
8392
							if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8393
								$annots .= ' /CL [';
8394
								foreach ($pl['opt']['cl'] as $cl) {
8395
									$annots .= sprintf('%F ', $cl * $this->k);
8396
								}
8397
								$annots .= ']';
8398
							}
8399
							$tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8400
							if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8401
								$annots .= ' /IT /'.$pl['opt']['it'];
8402
							}
8403
							if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8404
								$l = $pl['opt']['rd'][0] * $this->k;
8405
								$r = $pl['opt']['rd'][1] * $this->k;
8406
								$t = $pl['opt']['rd'][2] * $this->k;
8407
								$b = $pl['opt']['rd'][3] * $this->k;
8408
								$annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8409
							}
8410
							if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8411
								$annots .= ' /LE /'.$pl['opt']['le'];
8412
							}
8413
							break;
8414
						}
8415
						case 'line': {
8416
							break;
8417
						}
8418
						case 'square': {
8419
							break;
8420
						}
8421
						case 'circle': {
8422
							break;
8423
						}
8424
						case 'polygon': {
8425
							break;
8426
						}
8427
						case 'polyline': {
8428
							break;
8429
						}
8430
						case 'highlight': {
8431
							break;
8432
						}
8433
						case 'underline': {
8434
							break;
8435
						}
8436
						case 'squiggly': {
8437
							break;
8438
						}
8439
						case 'strikeout': {
8440
							break;
8441
						}
8442
						case 'stamp': {
8443
							break;
8444
						}
8445
						case 'caret': {
8446
							break;
8447
						}
8448
						case 'ink': {
8449
							break;
8450
						}
8451
						case 'popup': {
8452
							break;
8453
						}
8454
						case 'fileattachment': {
8455
							if ($this->pdfa_mode) {
8456
								// embedded files are not allowed in PDF/A mode
8457
								break;
8458
							}
8459
							if (!isset($pl['opt']['fs'])) {
8460
								break;
8461
							}
8462
							$filename = basename($pl['opt']['fs']);
8463
							if (isset($this->embeddedfiles[$filename]['f'])) {
8464
								$annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8465
								$iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8466
								if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8467
									$annots .= ' /Name /'.$pl['opt']['name'];
8468
								} else {
8469
									$annots .= ' /Name /PushPin';
8470
								}
8471
								// index (zero-based) of the annotation in the Annots array of this page
8472
								$this->embeddedfiles[$filename]['a'] = $key;
8473
							}
8474
							break;
8475
						}
8476
						case 'sound': {
8477
							if (!isset($pl['opt']['fs'])) {
8478
								break;
8479
							}
8480
							$filename = basename($pl['opt']['fs']);
8481
							if (isset($this->embeddedfiles[$filename]['f'])) {
8482
								// ... TO BE COMPLETED ...
8483
								// /R /C /B /E /CO /CP
8484
								$annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8485
								$iconsapp = array('Speaker', 'Mic');
8486
								if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8487
									$annots .= ' /Name /'.$pl['opt']['name'];
8488
								} else {
8489
									$annots .= ' /Name /Speaker';
8490
								}
8491
							}
8492
							break;
8493
						}
8494
						case 'movie': {
8495
							break;
8496
						}
8497
						case 'widget': {
8498
							$hmode = array('N', 'I', 'O', 'P', 'T');
8499
							if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8500
								$annots .= ' /H /'.$pl['opt']['h'];
8501
							}
8502
							if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8503
								$annots .= ' /MK <<';
8504
								if (isset($pl['opt']['mk']['r'])) {
8505
									$annots .= ' /R '.$pl['opt']['mk']['r'];
8506
								}
8507
								if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8508
									$annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8509
								}
8510
								if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8511
									$annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8512
								}
8513
								if (isset($pl['opt']['mk']['ca'])) {
8514
									$annots .= ' /CA '.$pl['opt']['mk']['ca'];
8515
								}
8516
								if (isset($pl['opt']['mk']['rc'])) {
8517
									$annots .= ' /RC '.$pl['opt']['mk']['rc'];
8518
								}
8519
								if (isset($pl['opt']['mk']['ac'])) {
8520
									$annots .= ' /AC '.$pl['opt']['mk']['ac'];
8521
								}
8522
								if (isset($pl['opt']['mk']['i'])) {
8523
									$info = $this->getImageBuffer($pl['opt']['mk']['i']);
8524
									if ($info !== false) {
8525
										$annots .= ' /I '.$info['n'].' 0 R';
8526
									}
8527
								}
8528
								if (isset($pl['opt']['mk']['ri'])) {
8529
									$info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8530
									if ($info !== false) {
8531
										$annots .= ' /RI '.$info['n'].' 0 R';
8532
									}
8533
								}
8534
								if (isset($pl['opt']['mk']['ix'])) {
8535
									$info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8536
									if ($info !== false) {
8537
										$annots .= ' /IX '.$info['n'].' 0 R';
8538
									}
8539
								}
8540
								if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8541
									$annots .= ' /IF <<';
8542
									$if_sw = array('A', 'B', 'S', 'N');
8543
									if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8544
										$annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8545
									}
8546
									$if_s = array('A', 'P');
8547
									if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8548
										$annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8549
									}
8550
									if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8551
										$annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8552
									}
8553
									if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8554
										$annots .= ' /FB true';
8555
									}
8556
									$annots .= '>>';
8557
								}
8558
								if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8559
									$annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8560
								}
8561
								$annots .= '>>';
8562
							} // end MK
8563
							// --- Entries for field dictionaries ---
8564
							if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8565
								// set parent
8566
								$annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8567
							}
8568
							if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8569
								$annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8570
							}
8571
							if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8572
								$annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8573
							}
8574
							if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8575
								$annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8576
							}
8577
							if (isset($pl['opt']['ff'])) {
8578
								if (is_array($pl['opt']['ff'])) {
8579
									// array of bit settings
8580
									$flag = 0;
8581
									foreach($pl['opt']['ff'] as $val) {
8582
										$flag += 1 << ($val - 1);
8583
									}
8584
								} else {
8585
									$flag = intval($pl['opt']['ff']);
8586
								}
8587
								$annots .= ' /Ff '.$flag;
8588
							}
8589
							if (isset($pl['opt']['maxlen'])) {
8590
								$annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8591
							}
8592
							if (isset($pl['opt']['v'])) {
8593
								$annots .= ' /V';
8594
								if (is_array($pl['opt']['v'])) {
8595
									foreach ($pl['opt']['v'] AS $optval) {
8596
										if (is_float($optval)) {
8597
											$optval = sprintf('%F', $optval);
8598
										}
8599
										$annots .= ' '.$optval;
8600
									}
8601
								} else {
8602
									$annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8603
								}
8604
							}
8605
							if (isset($pl['opt']['dv'])) {
8606
								$annots .= ' /DV';
8607
								if (is_array($pl['opt']['dv'])) {
8608
									foreach ($pl['opt']['dv'] AS $optval) {
8609
										if (is_float($optval)) {
8610
											$optval = sprintf('%F', $optval);
8611
										}
8612
										$annots .= ' '.$optval;
8613
									}
8614
								} else {
8615
									$annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8616
								}
8617
							}
8618
							if (isset($pl['opt']['rv'])) {
8619
								$annots .= ' /RV';
8620
								if (is_array($pl['opt']['rv'])) {
8621
									foreach ($pl['opt']['rv'] AS $optval) {
8622
										if (is_float($optval)) {
8623
											$optval = sprintf('%F', $optval);
8624
										}
8625
										$annots .= ' '.$optval;
8626
									}
8627
								} else {
8628
									$annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8629
								}
8630
							}
8631
							if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8632
								$annots .= ' /A << '.$pl['opt']['a'].' >>';
8633
							}
8634
							if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8635
								$annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8636
							}
8637
							if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8638
								$annots .= ' /DA ('.$pl['opt']['da'].')';
8639
							}
8640
							if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8641
								$annots .= ' /Q '.intval($pl['opt']['q']);
8642
							}
8643
							if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8644
								$annots .= ' /Opt [';
8645
								foreach($pl['opt']['opt'] AS $copt) {
8646
									if (is_array($copt)) {
8647
										$annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8648
									} else {
8649
										$annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8650
									}
8651
								}
8652
								$annots .= ']';
8653
							}
8654
							if (isset($pl['opt']['ti'])) {
8655
								$annots .= ' /TI '.intval($pl['opt']['ti']);
8656
							}
8657
							if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8658
								$annots .= ' /I [';
8659
								foreach($pl['opt']['i'] AS $copt) {
8660
									$annots .= intval($copt).' ';
8661
								}
8662
								$annots .= ']';
8663
							}
8664
							break;
8665
						}
8666
						case 'screen': {
8667
							break;
8668
						}
8669
						case 'printermark': {
8670
							break;
8671
						}
8672
						case 'trapnet': {
8673
							break;
8674
						}
8675
						case 'watermark': {
8676
							break;
8677
						}
8678
						case '3d': {
8679
							break;
8680
						}
8681
						default: {
8682
							break;
8683
						}
8684
					}
8685
					$annots .= '>>';
8686
					// create new annotation object
8687
					$this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8688
					if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8689
						// store reference of form object
8690
						$this->form_obj_id[] = $annot_obj_id;
8691
					}
8692
				}
8693
			}
8694
		} // end for each page
8695
	}
8696
 
8697
	/**
8698
	 * Put appearance streams XObject used to define annotation's appearance states.
8699
	 * @param $w (int) annotation width
8700
	 * @param $h (int) annotation height
8701
	 * @param $stream (string) appearance stream
8702
	 * @return int object ID
8703
	 * @protected
8704
	 * @since 4.8.001 (2009-09-09)
8705
	 */
8706
	protected function _putAPXObject($w=0, $h=0, $stream='') {
8707
		$stream = trim($stream);
8708
		$out = $this->_getobj()."\n";
8709
		$this->xobjects['AX'.$this->n] = array('n' => $this->n);
8710
		$out .= '<<';
8711
		$out .= ' /Type /XObject';
8712
		$out .= ' /Subtype /Form';
8713
		$out .= ' /FormType 1';
8714
		if ($this->compress) {
8715
			$stream = gzcompress($stream);
8716
			$out .= ' /Filter /FlateDecode';
8717
		}
8718
		$rect = sprintf('%F %F', $w, $h);
8719
		$out .= ' /BBox [0 0 '.$rect.']';
8720
		$out .= ' /Matrix [1 0 0 1 0 0]';
8721
		$out .= ' /Resources 2 0 R';
8722
		$stream = $this->_getrawstream($stream);
8723
		$out .= ' /Length '.strlen($stream);
8724
		$out .= ' >>';
8725
		$out .= ' stream'."\n".$stream."\n".'endstream';
8726
		$out .= "\n".'endobj';
8727
		$this->_out($out);
8728
		return $this->n;
8729
	}
8730
 
8731
	/**
8732
	 * Output fonts.
8733
	 * @author Nicola Asuni
8734
	 * @protected
8735
	 */
8736
	protected function _putfonts() {
8737
		$nf = $this->n;
8738
		foreach ($this->diffs as $diff) {
8739
			//Encodings
8740
			$this->_newobj();
8741
			$this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8742
		}
8743
		$mqr = TCPDF_STATIC::get_mqr();
8744
		TCPDF_STATIC::set_mqr(false);
8745
		foreach ($this->FontFiles as $file => $info) {
8746
			// search and get font file to embedd
8747
			$fontdir = $info['fontdir'];
8748
			$file = strtolower($file);
8749
			$fontfile = '';
8750
			// search files on various directories
8751
			if (($fontdir !== false) AND @file_exists($fontdir.$file)) {
8752
				$fontfile = $fontdir.$file;
8753
			} elseif (@file_exists(TCPDF_FONTS::_getfontpath().$file)) {
8754
				$fontfile = TCPDF_FONTS::_getfontpath().$file;
8755
			} elseif (@file_exists($file)) {
8756
				$fontfile = $file;
8757
			}
8758
			if (!TCPDF_STATIC::empty_string($fontfile)) {
8759
				$font = file_get_contents($fontfile);
8760
				$compressed = (substr($file, -2) == '.z');
8761
				if ((!$compressed) AND (isset($info['length2']))) {
8762
					$header = (ord($font{0}) == 128);
8763
					if ($header) {
8764
						// strip first binary header
8765
						$font = substr($font, 6);
8766
					}
8767
					if ($header AND (ord($font[$info['length1']]) == 128)) {
8768
						// strip second binary header
8769
						$font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8770
					}
8771
				} elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8772
					if ($compressed) {
8773
						// uncompress font
8774
						$font = gzuncompress($font);
8775
					}
8776
					// merge subset characters
8777
					$subsetchars = array(); // used chars
8778
					foreach ($info['fontkeys'] as $fontkey) {
8779
						$fontinfo = $this->getFontBuffer($fontkey);
8780
						$subsetchars += $fontinfo['subsetchars'];
8781
					}
8782
					// rebuild a font subset
8783
					$font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8784
					// calculate new font length
8785
					$info['length1'] = strlen($font);
8786
					if ($compressed) {
8787
						// recompress font
8788
						$font = gzcompress($font);
8789
					}
8790
				}
8791
				$this->_newobj();
8792
				$this->FontFiles[$file]['n'] = $this->n;
8793
				$stream = $this->_getrawstream($font);
8794
				$out = '<< /Length '.strlen($stream);
8795
				if ($compressed) {
8796
					$out .= ' /Filter /FlateDecode';
8797
				}
8798
				$out .= ' /Length1 '.$info['length1'];
8799
				if (isset($info['length2'])) {
8800
					$out .= ' /Length2 '.$info['length2'].' /Length3 0';
8801
				}
8802
				$out .= ' >>';
8803
				$out .= ' stream'."\n".$stream."\n".'endstream';
8804
				$out .= "\n".'endobj';
8805
				$this->_out($out);
8806
			}
8807
		}
8808
		TCPDF_STATIC::set_mqr($mqr);
8809
		foreach ($this->fontkeys as $k) {
8810
			//Font objects
8811
			$font = $this->getFontBuffer($k);
8812
			$type = $font['type'];
8813
			$name = $font['name'];
8814
			if ($type == 'core') {
8815
				// standard core font
8816
				$out = $this->_getobj($this->font_obj_ids[$k])."\n";
8817
				$out .= '<</Type /Font';
8818
				$out .= ' /Subtype /Type1';
8819
				$out .= ' /BaseFont /'.$name;
8820
				$out .= ' /Name /F'.$font['i'];
8821
				if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8822
					$out .= ' /Encoding /WinAnsiEncoding';
8823
				}
8824
				if ($k == 'helvetica') {
8825
					// add default font for annotations
8826
					$this->annotation_fonts[$k] = $font['i'];
8827
				}
8828
				$out .= ' >>';
8829
				$out .= "\n".'endobj';
8830
				$this->_out($out);
8831
			} elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8832
				// additional Type1 or TrueType font
8833
				$out = $this->_getobj($this->font_obj_ids[$k])."\n";
8834
				$out .= '<</Type /Font';
8835
				$out .= ' /Subtype /'.$type;
8836
				$out .= ' /BaseFont /'.$name;
8837
				$out .= ' /Name /F'.$font['i'];
8838
				$out .= ' /FirstChar 32 /LastChar 255';
8839
				$out .= ' /Widths '.($this->n + 1).' 0 R';
8840
				$out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8841
				if ($font['enc']) {
8842
					if (isset($font['diff'])) {
8843
						$out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8844
					} else {
8845
						$out .= ' /Encoding /WinAnsiEncoding';
8846
					}
8847
				}
8848
				$out .= ' >>';
8849
				$out .= "\n".'endobj';
8850
				$this->_out($out);
8851
				// Widths
8852
				$this->_newobj();
8853
				$s = '[';
8854
				for ($i = 32; $i < 256; ++$i) {
8855
					if (isset($font['cw'][$i])) {
8856
						$s .= $font['cw'][$i].' ';
8857
					} else {
8858
						$s .= $font['dw'].' ';
8859
					}
8860
				}
8861
				$s .= ']';
8862
				$s .= "\n".'endobj';
8863
				$this->_out($s);
8864
				//Descriptor
8865
				$this->_newobj();
8866
				$s = '<</Type /FontDescriptor /FontName /'.$name;
8867
				foreach ($font['desc'] as $fdk => $fdv) {
8868
					if (is_float($fdv)) {
8869
						$fdv = sprintf('%F', $fdv);
8870
					}
8871
					$s .= ' /'.$fdk.' '.$fdv.'';
8872
				}
8873
				if (!TCPDF_STATIC::empty_string($font['file'])) {
8874
					$s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8875
				}
8876
				$s .= '>>';
8877
				$s .= "\n".'endobj';
8878
				$this->_out($s);
8879
			} else {
8880
				// additional types
8881
				$mtd = '_put'.strtolower($type);
8882
				if (!method_exists($this, $mtd)) {
8883
					$this->Error('Unsupported font type: '.$type);
8884
				}
8885
				$this->$mtd($font);
8886
			}
8887
		}
8888
	}
8889
 
8890
	/**
8891
	 * Adds unicode fonts.<br>
8892
	 * Based on PDF Reference 1.3 (section 5)
8893
	 * @param $font (array) font data
8894
	 * @protected
8895
	 * @author Nicola Asuni
8896
	 * @since 1.52.0.TC005 (2005-01-05)
8897
	 */
8898
	protected function _puttruetypeunicode($font) {
8899
		$fontname = '';
8900
		if ($font['subset']) {
8901
			// change name for font subsetting
8902
			$subtag = sprintf('%06u', $font['i']);
8903
			$subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8904
			$fontname .= $subtag.'+';
8905
		}
8906
		$fontname .= $font['name'];
8907
		// Type0 Font
8908
		// A composite font composed of other fonts, organized hierarchically
8909
		$out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8910
		$out .= '<< /Type /Font';
8911
		$out .= ' /Subtype /Type0';
8912
		$out .= ' /BaseFont /'.$fontname;
8913
		$out .= ' /Name /F'.$font['i'];
8914
		$out .= ' /Encoding /'.$font['enc'];
8915
		$out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8916
		$out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8917
		$out .= ' >>';
8918
		$out .= "\n".'endobj';
8919
		$this->_out($out);
8920
		// ToUnicode map for Identity-H
8921
		$stream = TCPDF_FONT_DATA::$uni_identity_h;
8922
		// ToUnicode Object
8923
		$this->_newobj();
8924
		$stream = ($this->compress) ? gzcompress($stream) : $stream;
8925
		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8926
		$stream = $this->_getrawstream($stream);
8927
		$this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8928
		// CIDFontType2
8929
		// A CIDFont whose glyph descriptions are based on TrueType font technology
8930
		$oid = $this->_newobj();
8931
		$out = '<< /Type /Font';
8932
		$out .= ' /Subtype /CIDFontType2';
8933
		$out .= ' /BaseFont /'.$fontname;
8934
		// A dictionary containing entries that define the character collection of the CIDFont.
8935
		$cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8936
		$cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8937
		$cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8938
		$out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8939
		$out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8940
		$out .= ' /DW '.$font['dw']; // default width
8941
		$out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8942
		if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8943
			$out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8944
		}
8945
		$out .= ' >>';
8946
		$out .= "\n".'endobj';
8947
		$this->_out($out);
8948
		// Font descriptor
8949
		// A font descriptor describing the CIDFont default metrics other than its glyph widths
8950
		$this->_newobj();
8951
		$out = '<< /Type /FontDescriptor';
8952
		$out .= ' /FontName /'.$fontname;
8953
		foreach ($font['desc'] as $key => $value) {
8954
			if (is_float($value)) {
8955
				$value = sprintf('%F', $value);
8956
			}
8957
			$out .= ' /'.$key.' '.$value;
8958
		}
8959
		$fontdir = false;
8960
		if (!TCPDF_STATIC::empty_string($font['file'])) {
8961
			// A stream containing a TrueType font
8962
			$out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8963
			$fontdir = $this->FontFiles[$font['file']]['fontdir'];
8964
		}
8965
		$out .= ' >>';
8966
		$out .= "\n".'endobj';
8967
		$this->_out($out);
8968
		if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8969
			$this->_newobj();
8970
			// Embed CIDToGIDMap
8971
			// A specification of the mapping from CIDs to glyph indices
8972
			// search and get CTG font file to embedd
8973
			$ctgfile = strtolower($font['ctg']);
8974
			// search and get ctg font file to embedd
8975
			$fontfile = '';
8976
			// search files on various directories
8977
			if (($fontdir !== false) AND @file_exists($fontdir.$ctgfile)) {
8978
				$fontfile = $fontdir.$ctgfile;
8979
			} elseif (@file_exists(TCPDF_FONTS::_getfontpath().$ctgfile)) {
8980
				$fontfile = TCPDF_FONTS::_getfontpath().$ctgfile;
8981
			} elseif (@file_exists($ctgfile)) {
8982
				$fontfile = $ctgfile;
8983
			}
8984
			if (TCPDF_STATIC::empty_string($fontfile)) {
8985
				$this->Error('Font file not found: '.$ctgfile);
8986
			}
8987
			$stream = $this->_getrawstream(file_get_contents($fontfile));
8988
			$out = '<< /Length '.strlen($stream).'';
8989
			if (substr($fontfile, -2) == '.z') { // check file extension
8990
				// Decompresses data encoded using the public-domain
8991
				// zlib/deflate compression method, reproducing the
8992
				// original text or binary data
8993
				$out .= ' /Filter /FlateDecode';
8994
			}
8995
			$out .= ' >>';
8996
			$out .= ' stream'."\n".$stream."\n".'endstream';
8997
			$out .= "\n".'endobj';
8998
			$this->_out($out);
8999
		}
9000
	}
9001
 
9002
	/**
9003
	 * Output CID-0 fonts.
9004
	 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9005
	 * @param $font (array) font data
9006
	 * @protected
9007
	 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9008
	 * @since 3.2.000 (2008-06-23)
9009
	 */
9010
	protected function _putcidfont0($font) {
9011
		$cidoffset = 0;
9012
		if (!isset($font['cw'][1])) {
9013
			$cidoffset = 31;
9014
		}
9015
		if (isset($font['cidinfo']['uni2cid'])) {
9016
			// convert unicode to cid.
9017
			$uni2cid = $font['cidinfo']['uni2cid'];
9018
			$cw = array();
9019
			foreach ($font['cw'] as $uni => $width) {
9020
				if (isset($uni2cid[$uni])) {
9021
					$cw[($uni2cid[$uni] + $cidoffset)] = $width;
9022
				} elseif ($uni < 256) {
9023
					$cw[$uni] = $width;
9024
				} // else unknown character
9025
			}
9026
			$font = array_merge($font, array('cw' => $cw));
9027
		}
9028
		$name = $font['name'];
9029
		$enc = $font['enc'];
9030
		if ($enc) {
9031
			$longname = $name.'-'.$enc;
9032
		} else {
9033
			$longname = $name;
9034
		}
9035
		$out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9036
		$out .= '<</Type /Font';
9037
		$out .= ' /Subtype /Type0';
9038
		$out .= ' /BaseFont /'.$longname;
9039
		$out .= ' /Name /F'.$font['i'];
9040
		if ($enc) {
9041
			$out .= ' /Encoding /'.$enc;
9042
		}
9043
		$out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9044
		$out .= ' >>';
9045
		$out .= "\n".'endobj';
9046
		$this->_out($out);
9047
		$oid = $this->_newobj();
9048
		$out = '<</Type /Font';
9049
		$out .= ' /Subtype /CIDFontType0';
9050
		$out .= ' /BaseFont /'.$name;
9051
		$cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9052
		$cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9053
		$cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9054
		$out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9055
		$out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9056
		$out .= ' /DW '.$font['dw'];
9057
		$out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9058
		$out .= ' >>';
9059
		$out .= "\n".'endobj';
9060
		$this->_out($out);
9061
		$this->_newobj();
9062
		$s = '<</Type /FontDescriptor /FontName /'.$name;
9063
		foreach ($font['desc'] as $k => $v) {
9064
			if ($k != 'Style') {
9065
				if (is_float($v)) {
9066
					$v = sprintf('%F', $v);
9067
				}
9068
				$s .= ' /'.$k.' '.$v.'';
9069
			}
9070
		}
9071
		$s .= '>>';
9072
		$s .= "\n".'endobj';
9073
		$this->_out($s);
9074
	}
9075
 
9076
	/**
9077
	 * Output images.
9078
	 * @protected
9079
	 */
9080
	protected function _putimages() {
9081
		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9082
		foreach ($this->imagekeys as $file) {
9083
			$info = $this->getImageBuffer($file);
9084
			// set object for alternate images array
9085
			if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9086
				$altoid = $this->_newobj();
9087
				$out = '[';
9088
				foreach ($info['altimgs'] as $altimage) {
9089
					if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9090
						$out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9091
						$out .= ' /DefaultForPrinting';
9092
						if ($altimage[1] === true) {
9093
							$out .= ' true';
9094
						} else {
9095
							$out .= ' false';
9096
						}
9097
						$out .= ' >>';
9098
					}
9099
				}
9100
				$out .= ' ]';
9101
				$out .= "\n".'endobj';
9102
				$this->_out($out);
9103
			}
9104
			// set image object
9105
			$oid = $this->_newobj();
9106
			$this->xobjects['I'.$info['i']] = array('n' => $oid);
9107
			$this->setImageSubBuffer($file, 'n', $this->n);
9108
			$out = '<</Type /XObject';
9109
			$out .= ' /Subtype /Image';
9110
			$out .= ' /Width '.$info['w'];
9111
			$out .= ' /Height '.$info['h'];
9112
			if (array_key_exists('masked', $info)) {
9113
				$out .= ' /SMask '.($this->n - 1).' 0 R';
9114
			}
9115
			// set color space
9116
			$icc = false;
9117
			if (isset($info['icc']) AND ($info['icc'] !== false)) {
9118
				// ICC Colour Space
9119
				$icc = true;
9120
				$out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9121
			} elseif ($info['cs'] == 'Indexed') {
9122
				// Indexed Colour Space
9123
				$out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9124
			} else {
9125
				// Device Colour Space
9126
				$out .= ' /ColorSpace /'.$info['cs'];
9127
			}
9128
			if ($info['cs'] == 'DeviceCMYK') {
9129
				$out .= ' /Decode [1 0 1 0 1 0 1 0]';
9130
			}
9131
			$out .= ' /BitsPerComponent '.$info['bpc'];
9132
			if (isset($altoid) AND ($altoid > 0)) {
9133
				// reference to alternate images dictionary
9134
				$out .= ' /Alternates '.$altoid.' 0 R';
9135
			}
9136
			if (isset($info['exurl']) AND !empty($info['exurl'])) {
9137
				// external stream
9138
				$out .= ' /Length 0';
9139
				$out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9140
				if (isset($info['f'])) {
9141
					$out .= ' /FFilter /'.$info['f'];
9142
				}
9143
				$out .= ' >>';
9144
				$out .= ' stream'."\n".'endstream';
9145
			} else {
9146
				if (isset($info['f'])) {
9147
					$out .= ' /Filter /'.$info['f'];
9148
				}
9149
				if (isset($info['parms'])) {
9150
					$out .= ' '.$info['parms'];
9151
				}
9152
				if (isset($info['trns']) AND is_array($info['trns'])) {
9153
					$trns = '';
9154
					$count_info = count($info['trns']);
9155
					for ($i=0; $i < $count_info; ++$i) {
9156
						$trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9157
					}
9158
					$out .= ' /Mask ['.$trns.']';
9159
				}
9160
				$stream = $this->_getrawstream($info['data']);
9161
				$out .= ' /Length '.strlen($stream).' >>';
9162
				$out .= ' stream'."\n".$stream."\n".'endstream';
9163
			}
9164
			$out .= "\n".'endobj';
9165
			$this->_out($out);
9166
			if ($icc) {
9167
				// ICC colour profile
9168
				$this->_newobj();
9169
				$icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9170
				$icc = $this->_getrawstream($icc);
9171
				$this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9172
			} elseif ($info['cs'] == 'Indexed') {
9173
				// colour palette
9174
				$this->_newobj();
9175
				$pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9176
				$pal = $this->_getrawstream($pal);
9177
				$this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9178
			}
9179
		}
9180
	}
9181
 
9182
	/**
9183
	 * Output Form XObjects Templates.
9184
	 * @author Nicola Asuni
9185
	 * @since 5.8.017 (2010-08-24)
9186
	 * @protected
9187
	 * @see startTemplate(), endTemplate(), printTemplate()
9188
	 */
9189
	protected function _putxobjects() {
9190
		foreach ($this->xobjects as $key => $data) {
9191
			if (isset($data['outdata'])) {
9192
				$stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9193
				$out = $this->_getobj($data['n'])."\n";
9194
				$out .= '<<';
9195
				$out .= ' /Type /XObject';
9196
				$out .= ' /Subtype /Form';
9197
				$out .= ' /FormType 1';
9198
				if ($this->compress) {
9199
					$stream = gzcompress($stream);
9200
					$out .= ' /Filter /FlateDecode';
9201
				}
9202
				$out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
9203
				$out .= ' /Matrix [1 0 0 1 0 0]';
9204
				$out .= ' /Resources <<';
9205
				$out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9206
				if (!$this->pdfa_mode) {
9207
					// transparency
9208
					if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9209
						$out .= ' /ExtGState <<';
9210
						foreach ($data['extgstates'] as $k => $extgstate) {
9211
							if (isset($this->extgstates[$k]['name'])) {
9212
								$out .= ' /'.$this->extgstates[$k]['name'];
9213
							} else {
9214
								$out .= ' /GS'.$k;
9215
							}
9216
							$out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9217
						}
9218
						$out .= ' >>';
9219
					}
9220
					if (isset($data['gradients']) AND !empty($data['gradients'])) {
9221
						$gp = '';
9222
						$gs = '';
9223
						foreach ($data['gradients'] as $id => $grad) {
9224
							// gradient patterns
9225
							$gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9226
							// gradient shadings
9227
							$gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9228
						}
9229
						$out .= ' /Pattern <<'.$gp.' >>';
9230
						$out .= ' /Shading <<'.$gs.' >>';
9231
					}
9232
				}
9233
				// spot colors
9234
				if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9235
					$out .= ' /ColorSpace <<';
9236
					foreach ($data['spot_colors'] as $name => $color) {
9237
						$out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9238
					}
9239
					$out .= ' >>';
9240
				}
9241
				// fonts
9242
				if (!empty($data['fonts'])) {
9243
					$out .= ' /Font <<';
9244
					foreach ($data['fonts'] as $fontkey => $fontid) {
9245
						$out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9246
					}
9247
					$out .= ' >>';
9248
				}
9249
				// images or nested xobjects
9250
				if (!empty($data['images']) OR !empty($data['xobjects'])) {
9251
					$out .= ' /XObject <<';
9252
					foreach ($data['images'] as $imgid) {
9253
						$out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9254
					}
9255
					foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9256
						$out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9257
					}
9258
					$out .= ' >>';
9259
				}
9260
				$out .= ' >>'; //end resources
9261
				if (isset($data['group']) AND ($data['group'] !== false)) {
9262
					// set transparency group
9263
					$out .= ' /Group << /Type /Group /S /Transparency';
9264
					if (is_array($data['group'])) {
9265
						if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9266
							$out .= ' /CS /'.$data['group']['CS'];
9267
						}
9268
						if (isset($data['group']['I'])) {
9269
							$out .= ' /I /'.($data['group']['I']===true?'true':'false');
9270
						}
9271
						if (isset($data['group']['K'])) {
9272
							$out .= ' /K /'.($data['group']['K']===true?'true':'false');
9273
						}
9274
					}
9275
					$out .= ' >>';
9276
				}
9277
				$stream = $this->_getrawstream($stream, $data['n']);
9278
				$out .= ' /Length '.strlen($stream);
9279
				$out .= ' >>';
9280
				$out .= ' stream'."\n".$stream."\n".'endstream';
9281
				$out .= "\n".'endobj';
9282
				$this->_out($out);
9283
			}
9284
		}
9285
	}
9286
 
9287
	/**
9288
	 * Output Spot Colors Resources.
9289
	 * @protected
9290
	 * @since 4.0.024 (2008-09-12)
9291
	 */
9292
	protected function _putspotcolors() {
9293
		foreach ($this->spot_colors as $name => $color) {
9294
			$this->_newobj();
9295
			$this->spot_colors[$name]['n'] = $this->n;
9296
			$out = '[/Separation /'.str_replace(' ', '#20', $name);
9297
			$out .= ' /DeviceCMYK <<';
9298
			$out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9299
			$out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9300
			$out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9301
			$out .= "\n".'endobj';
9302
			$this->_out($out);
9303
		}
9304
	}
9305
 
9306
	/**
9307
	 * Return XObjects Dictionary.
9308
	 * @return string XObjects dictionary
9309
	 * @protected
9310
	 * @since 5.8.014 (2010-08-23)
9311
	 */
9312
	protected function _getxobjectdict() {
9313
		$out = '';
9314
		foreach ($this->xobjects as $id => $objid) {
9315
			$out .= ' /'.$id.' '.$objid['n'].' 0 R';
9316
		}
9317
		return $out;
9318
	}
9319
 
9320
	/**
9321
	 * Output Resources Dictionary.
9322
	 * @protected
9323
	 */
9324
	protected function _putresourcedict() {
9325
		$out = $this->_getobj(2)."\n";
9326
		$out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9327
		$out .= ' /Font <<';
9328
		foreach ($this->fontkeys as $fontkey) {
9329
			$font = $this->getFontBuffer($fontkey);
9330
			$out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9331
		}
9332
		$out .= ' >>';
9333
		$out .= ' /XObject <<';
9334
		$out .= $this->_getxobjectdict();
9335
		$out .= ' >>';
9336
		// layers
9337
		if (!empty($this->pdflayers)) {
9338
			$out .= ' /Properties <<';
9339
			foreach ($this->pdflayers as $layer) {
9340
				$out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9341
			}
9342
			$out .= ' >>';
9343
		}
9344
		if (!$this->pdfa_mode) {
9345
			// transparency
9346
			if (isset($this->extgstates) AND !empty($this->extgstates)) {
9347
				$out .= ' /ExtGState <<';
9348
				foreach ($this->extgstates as $k => $extgstate) {
9349
					if (isset($extgstate['name'])) {
9350
						$out .= ' /'.$extgstate['name'];
9351
					} else {
9352
						$out .= ' /GS'.$k;
9353
					}
9354
					$out .= ' '.$extgstate['n'].' 0 R';
9355
				}
9356
				$out .= ' >>';
9357
			}
9358
			if (isset($this->gradients) AND !empty($this->gradients)) {
9359
				$gp = '';
9360
				$gs = '';
9361
				foreach ($this->gradients as $id => $grad) {
9362
					// gradient patterns
9363
					$gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9364
					// gradient shadings
9365
					$gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9366
				}
9367
				$out .= ' /Pattern <<'.$gp.' >>';
9368
				$out .= ' /Shading <<'.$gs.' >>';
9369
			}
9370
		}
9371
		// spot colors
9372
		if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9373
			$out .= ' /ColorSpace <<';
9374
			foreach ($this->spot_colors as $color) {
9375
				$out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9376
			}
9377
			$out .= ' >>';
9378
		}
9379
		$out .= ' >>';
9380
		$out .= "\n".'endobj';
9381
		$this->_out($out);
9382
	}
9383
 
9384
	/**
9385
	 * Output Resources.
9386
	 * @protected
9387
	 */
9388
	protected function _putresources() {
9389
		$this->_putextgstates();
9390
		$this->_putocg();
9391
		$this->_putfonts();
9392
		$this->_putimages();
9393
		$this->_putspotcolors();
9394
		$this->_putshaders();
9395
		$this->_putxobjects();
9396
		$this->_putresourcedict();
9397
		$this->_putdests();
9398
		$this->_putEmbeddedFiles();
9399
		$this->_putannotsobjs();
9400
		$this->_putjavascript();
9401
		$this->_putbookmarks();
9402
		$this->_putencryption();
9403
	}
9404
 
9405
	/**
9406
	 * Adds some Metadata information (Document Information Dictionary)
9407
	 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9408
	 * @return int object id
9409
	 * @protected
9410
	 */
9411
	protected function _putinfo() {
9412
		$oid = $this->_newobj();
9413
		$out = '<<';
9414
		// store current isunicode value
9415
		$prev_isunicode = $this->isunicode;
9416
		if ($this->docinfounicode) {
9417
			$this->isunicode = true;
9418
		}
9419
		if (!TCPDF_STATIC::empty_string($this->title)) {
9420
			// The document's title.
9421
			$out .= ' /Title '.$this->_textstring($this->title, $oid);
9422
		}
9423
		if (!TCPDF_STATIC::empty_string($this->author)) {
9424
			// The name of the person who created the document.
9425
			$out .= ' /Author '.$this->_textstring($this->author, $oid);
9426
		}
9427
		if (!TCPDF_STATIC::empty_string($this->subject)) {
9428
			// The subject of the document.
9429
			$out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9430
		}
9431
		if (!TCPDF_STATIC::empty_string($this->keywords)) {
9432
			// Keywords associated with the document.
9433
			$out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid);
9434
		}
9435
		if (!TCPDF_STATIC::empty_string($this->creator)) {
9436
			// If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
9437
			$out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9438
		}
9439
		// restore previous isunicode value
9440
		$this->isunicode = $prev_isunicode;
9441
		// default producer
9442
		$out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9443
		// The date and time the document was created, in human-readable form
9444
		$out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9445
		// The date and time the document was most recently modified, in human-readable form
9446
		$out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9447
		// A name object indicating whether the document has been modified to include trapping information
9448
		$out .= ' /Trapped /False';
9449
		$out .= ' >>';
9450
		$out .= "\n".'endobj';
9451
		$this->_out($out);
9452
		return $oid;
9453
	}
9454
 
9455
	/**
9456
	 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9457
	 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9458
	 * @param $xmp (string) Custom XMP data.
9459
	 * @since 5.9.128 (2011-10-06)
9460
	 * @public
9461
	 */
9462
	public function setExtraXMP($xmp) {
9463
		$this->custom_xmp = $xmp;
9464
	}
9465
 
9466
	/**
9467
	 * Put XMP data object and return ID.
9468
	 * @return (int) The object ID.
9469
	 * @since 5.9.121 (2011-09-28)
9470
	 * @protected
9471
	 */
9472
	protected function _putXMP() {
9473
		$oid = $this->_newobj();
9474
		// store current isunicode value
9475
		$prev_isunicode = $this->isunicode;
9476
		$this->isunicode = true;
9477
		$prev_encrypted = $this->encrypted;
9478
		$this->encrypted = false;
9479
		// set XMP data
9480
		$xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9481
		$xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n";
9482
		$xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9483
		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9484
		$xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9485
		$xmp .= "\t\t\t".'<dc:title>'."\n";
9486
		$xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9487
		$xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9488
		$xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9489
		$xmp .= "\t\t\t".'</dc:title>'."\n";
9490
		$xmp .= "\t\t\t".'<dc:creator>'."\n";
9491
		$xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9492
		$xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9493
		$xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9494
		$xmp .= "\t\t\t".'</dc:creator>'."\n";
9495
		$xmp .= "\t\t\t".'<dc:description>'."\n";
9496
		$xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9497
		$xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9498
		$xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9499
		$xmp .= "\t\t\t".'</dc:description>'."\n";
9500
		$xmp .= "\t\t\t".'<dc:subject>'."\n";
9501
		$xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9502
		$xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).' TCPDF</rdf:li>'."\n";
9503
		$xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9504
		$xmp .= "\t\t\t".'</dc:subject>'."\n";
9505
		$xmp .= "\t\t".'</rdf:Description>'."\n";
9506
		// convert doc creation date format
9507
		$dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9508
		$doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9509
		$doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9510
		$doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9511
		$doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9512
		// convert doc modification date format
9513
		$dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9514
		$docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9515
		$docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9516
		$docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9517
		$docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9518
		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9519
		$xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9520
		$xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9521
		$xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9522
		$xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9523
		$xmp .= "\t\t".'</rdf:Description>'."\n";
9524
		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9525
		$xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n";
9526
		$xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9527
		$xmp .= "\t\t".'</rdf:Description>'."\n";
9528
		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9529
		$uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12);
9530
		$xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9531
		$xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9532
		$xmp .= "\t\t".'</rdf:Description>'."\n";
9533
		if ($this->pdfa_mode) {
9534
			$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9535
			$xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9536
			$xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9537
			$xmp .= "\t\t".'</rdf:Description>'."\n";
9538
		}
9539
		// XMP extension schemas
9540
		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n";
9541
		$xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9542
		$xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9543
		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9544
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9545
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9546
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9547
		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9548
		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9549
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9550
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9551
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9552
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9553
		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9554
		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9555
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9556
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9557
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9558
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9559
		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9560
		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9561
		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9562
		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9563
		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9564
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9565
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9566
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9567
		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9568
		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9569
		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9570
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9571
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9572
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9573
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9574
		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9575
		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9576
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9577
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9578
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9579
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9580
		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9581
		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9582
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9583
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9584
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9585
		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9586
		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9587
		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9588
		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9589
		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9590
		$xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9591
		$xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9592
		$xmp .= "\t\t".'</rdf:Description>'."\n";
9593
		$xmp .= "\t".'</rdf:RDF>'."\n";
9594
		$xmp .= $this->custom_xmp;
9595
		$xmp .= '</x:xmpmeta>'."\n";
9596
		$xmp .= '<?xpacket end="w"?>';
9597
		$out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9598
		// restore previous isunicode value
9599
		$this->isunicode = $prev_isunicode;
9600
		$this->encrypted = $prev_encrypted;
9601
		$this->_out($out);
9602
		return $oid;
9603
	}
9604
 
9605
	/**
9606
	 * Output Catalog.
9607
	 * @return int object id
9608
	 * @protected
9609
	 */
9610
	protected function _putcatalog() {
9611
		// put XMP
9612
		$xmpobj = $this->_putXMP();
9613
		// if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9614
		if ($this->pdfa_mode OR $this->force_srgb) {
9615
			$iccobj = $this->_newobj();
9616
			$icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9617
			$filter = '';
9618
			if ($this->compress) {
9619
				$filter = ' /Filter /FlateDecode';
9620
				$icc = gzcompress($icc);
9621
			}
9622
			$icc = $this->_getrawstream($icc);
9623
			$this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9624
		}
9625
		// start catalog
9626
		$oid = $this->_newobj();
9627
		$out = '<< /Type /Catalog';
9628
		$out .= ' /Version /'.$this->PDFVersion;
9629
		//$out .= ' /Extensions <<>>';
9630
		$out .= ' /Pages 1 0 R';
9631
		//$out .= ' /PageLabels ' //...;
9632
		$out .= ' /Names <<';
9633
		if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9634
			$out .= ' /JavaScript '.$this->n_js;
9635
		}
9636
		if (!empty($this->efnames)) {
9637
			$out .= ' /EmbeddedFiles <</Names [';
9638
			foreach ($this->efnames AS $fn => $fref) {
9639
				$out .= ' '.$this->_datastring($fn).' '.$fref;
9640
			}
9641
			$out .= ' ]>>';
9642
		}
9643
		$out .= ' >>';
9644
		if (!empty($this->dests)) {
9645
			$out .= ' /Dests '.($this->n_dests).' 0 R';
9646
		}
9647
		$out .= $this->_putviewerpreferences();
9648
		if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9649
			$out .= ' /PageLayout /'.$this->LayoutMode;
9650
		}
9651
		if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9652
			$out .= ' /PageMode /'.$this->PageMode;
9653
		}
9654
		if (count($this->outlines) > 0) {
9655
			$out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9656
			$out .= ' /PageMode /UseOutlines';
9657
		}
9658
		//$out .= ' /Threads []';
9659
		if ($this->ZoomMode == 'fullpage') {
9660
			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9661
		} elseif ($this->ZoomMode == 'fullwidth') {
9662
			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9663
		} elseif ($this->ZoomMode == 'real') {
9664
			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9665
		} elseif (!is_string($this->ZoomMode)) {
9666
			$out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9667
		}
9668
		//$out .= ' /AA <<>>';
9669
		//$out .= ' /URI <<>>';
9670
		$out .= ' /Metadata '.$xmpobj.' 0 R';
9671
		//$out .= ' /StructTreeRoot <<>>';
9672
		//$out .= ' /MarkInfo <<>>';
9673
		if (isset($this->l['a_meta_language'])) {
9674
			$out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9675
		}
9676
		//$out .= ' /SpiderInfo <<>>';
9677
		// set OutputIntent to sRGB IEC61966-2.1 if required
9678
		if ($this->pdfa_mode OR $this->force_srgb) {
9679
			$out .= ' /OutputIntents [<<';
9680
			$out .= ' /Type /OutputIntent';
9681
			$out .= ' /S /GTS_PDFA1';
9682
			$out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9683
			$out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9684
			$out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9685
			$out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9686
			$out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9687
			$out .= ' >>]';
9688
		}
9689
		//$out .= ' /PieceInfo <<>>';
9690
		if (!empty($this->pdflayers)) {
9691
			$lyrobjs = '';
9692
			$lyrobjs_print = '';
9693
			$lyrobjs_view = '';
9694
			foreach ($this->pdflayers as $layer) {
9695
				$lyrobjs .= ' '.$layer['objid'].' 0 R';
9696
				if ($layer['print']) {
9697
					$lyrobjs_print .= ' '.$layer['objid'].' 0 R';
9698
				}
9699
				if ($layer['view']) {
9700
					$lyrobjs_view .= ' '.$layer['objid'].' 0 R';
9701
				}
9702
			}
9703
			$out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9704
			$out .= ' /D <<';
9705
			$out .= ' /Name '.$this->_textstring('Layers', $oid);
9706
			$out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9707
			$out .= ' /BaseState /ON';
9708
			$out .= ' /ON ['.$lyrobjs_print.']';
9709
			$out .= ' /OFF ['.$lyrobjs_view.']';
9710
			$out .= ' /Intent /View';
9711
			$out .= ' /AS [';
9712
			$out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9713
			$out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9714
			$out .= ' ]';
9715
			$out .= ' /Order ['.$lyrobjs.']';
9716
			$out .= ' /ListMode /AllPages';
9717
			//$out .= ' /RBGroups ['..']';
9718
			//$out .= ' /Locked ['..']';
9719
			$out .= ' >>';
9720
			$out .= ' >>';
9721
		}
9722
		// AcroForm
9723
		if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
9724
			$out .= ' /AcroForm <<';
9725
			$objrefs = '';
9726
			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9727
				// set reference for signature object
9728
				$objrefs .= $this->sig_obj_id.' 0 R';
9729
			}
9730
			if (!empty($this->empty_signature_appearance)) {
9731
				foreach ($this->empty_signature_appearance as $esa) {
9732
					// set reference for empty signature objects
9733
					$objrefs .= ' '.$esa['objid'].' 0 R';
9734
				}
9735
			}
9736
			if (!empty($this->form_obj_id)) {
9737
				foreach($this->form_obj_id as $objid) {
9738
					$objrefs .= ' '.$objid.' 0 R';
9739
				}
9740
			}
9741
			$out .= ' /Fields ['.$objrefs.']';
9742
			// It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9743
			$out .= ' /NeedAppearances false';
9744
			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9745
				if ($this->signature_data['cert_type'] > 0) {
9746
					$out .= ' /SigFlags 3';
9747
				} else {
9748
					$out .= ' /SigFlags 1';
9749
				}
9750
			}
9751
			//$out .= ' /CO ';
9752
			if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9753
				$out .= ' /DR <<';
9754
				$out .= ' /Font <<';
9755
				foreach ($this->annotation_fonts as $fontkey => $fontid) {
9756
					$out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9757
				}
9758
				$out .= ' >> >>';
9759
			}
9760
			$font = $this->getFontBuffer('helvetica');
9761
			$out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9762
			$out .= ' /Q '.(($this->rtl)?'2':'0');
9763
			//$out .= ' /XFA ';
9764
			$out .= ' >>';
9765
			// signatures
9766
			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9767
				if ($this->signature_data['cert_type'] > 0) {
9768
					$out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9769
				} else {
9770
					$out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9771
				}
9772
			}
9773
		}
9774
		//$out .= ' /Legal <<>>';
9775
		//$out .= ' /Requirements []';
9776
		//$out .= ' /Collection <<>>';
9777
		//$out .= ' /NeedsRendering true';
9778
		$out .= ' >>';
9779
		$out .= "\n".'endobj';
9780
		$this->_out($out);
9781
		return $oid;
9782
	}
9783
 
9784
	/**
9785
	 * Output viewer preferences.
9786
	 * @return string for viewer preferences
9787
	 * @author Nicola asuni
9788
	 * @since 3.1.000 (2008-06-09)
9789
	 * @protected
9790
	 */
9791
	protected function _putviewerpreferences() {
9792
		$vp = $this->viewer_preferences;
9793
		$out = ' /ViewerPreferences <<';
9794
		if ($this->rtl) {
9795
			$out .= ' /Direction /R2L';
9796
		} else {
9797
			$out .= ' /Direction /L2R';
9798
		}
9799
		if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9800
			$out .= ' /HideToolbar true';
9801
		}
9802
		if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9803
			$out .= ' /HideMenubar true';
9804
		}
9805
		if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9806
			$out .= ' /HideWindowUI true';
9807
		}
9808
		if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9809
			$out .= ' /FitWindow true';
9810
		}
9811
		if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9812
			$out .= ' /CenterWindow true';
9813
		}
9814
		if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9815
			$out .= ' /DisplayDocTitle true';
9816
		}
9817
		if (isset($vp['NonFullScreenPageMode'])) {
9818
			$out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9819
		}
9820
		if (isset($vp['ViewArea'])) {
9821
			$out .= ' /ViewArea /'.$vp['ViewArea'];
9822
		}
9823
		if (isset($vp['ViewClip'])) {
9824
			$out .= ' /ViewClip /'.$vp['ViewClip'];
9825
		}
9826
		if (isset($vp['PrintArea'])) {
9827
			$out .= ' /PrintArea /'.$vp['PrintArea'];
9828
		}
9829
		if (isset($vp['PrintClip'])) {
9830
			$out .= ' /PrintClip /'.$vp['PrintClip'];
9831
		}
9832
		if (isset($vp['PrintScaling'])) {
9833
			$out .= ' /PrintScaling /'.$vp['PrintScaling'];
9834
		}
9835
		if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9836
			$out .= ' /Duplex /'.$vp['Duplex'];
9837
		}
9838
		if (isset($vp['PickTrayByPDFSize'])) {
9839
			if ($vp['PickTrayByPDFSize']) {
9840
				$out .= ' /PickTrayByPDFSize true';
9841
			} else {
9842
				$out .= ' /PickTrayByPDFSize false';
9843
			}
9844
		}
9845
		if (isset($vp['PrintPageRange'])) {
9846
			$PrintPageRangeNum = '';
9847
			foreach ($vp['PrintPageRange'] as $k => $v) {
9848
				$PrintPageRangeNum .= ' '.($v - 1).'';
9849
			}
9850
			$out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9851
		}
9852
		if (isset($vp['NumCopies'])) {
9853
			$out .= ' /NumCopies '.intval($vp['NumCopies']);
9854
		}
9855
		$out .= ' >>';
9856
		return $out;
9857
	}
9858
 
9859
	/**
9860
	 * Output PDF File Header (7.5.2).
9861
	 * @protected
9862
	 */
9863
	protected function _putheader() {
9864
		$this->_out('%PDF-'.$this->PDFVersion);
9865
		$this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9866
	}
9867
 
9868
	/**
9869
	 * Output end of document (EOF).
9870
	 * @protected
9871
	 */
9872
	protected function _enddoc() {
9873
		if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9874
			// save subset chars of the previous font
9875
			$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9876
		}
9877
		$this->state = 1;
9878
		$this->_putheader();
9879
		$this->_putpages();
9880
		$this->_putresources();
9881
		// empty signature fields
9882
		if (!empty($this->empty_signature_appearance)) {
9883
			foreach ($this->empty_signature_appearance as $key => $esa) {
9884
				// widget annotation for empty signature
9885
				$out = $this->_getobj($esa['objid'])."\n";
9886
				$out .= '<< /Type /Annot';
9887
				$out .= ' /Subtype /Widget';
9888
				$out .= ' /Rect ['.$esa['rect'].']';
9889
				$out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9890
				$out .= ' /F 4';
9891
				$out .= ' /FT /Sig';
9892
				$signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9893
				$out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9894
				$out .= ' /Ff 0';
9895
				$out .= ' >>';
9896
				$out .= "\n".'endobj';
9897
				$this->_out($out);
9898
			}
9899
		}
9900
		// Signature
9901
		if ($this->sign AND isset($this->signature_data['cert_type'])) {
9902
			// widget annotation for signature
9903
			$out = $this->_getobj($this->sig_obj_id)."\n";
9904
			$out .= '<< /Type /Annot';
9905
			$out .= ' /Subtype /Widget';
9906
			$out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9907
			$out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9908
			$out .= ' /F 4';
9909
			$out .= ' /FT /Sig';
9910
			$out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9911
			$out .= ' /Ff 0';
9912
			$out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9913
			$out .= ' >>';
9914
			$out .= "\n".'endobj';
9915
			$this->_out($out);
9916
			// signature
9917
			$this->_putsignature();
9918
		}
9919
		// Info
9920
		$objid_info = $this->_putinfo();
9921
		// Catalog
9922
		$objid_catalog = $this->_putcatalog();
9923
		// Cross-ref
9924
		$o = $this->bufferlen;
9925
		// XREF section
9926
		$this->_out('xref');
9927
		$this->_out('0 '.($this->n + 1));
9928
		$this->_out('0000000000 65535 f ');
9929
		$freegen = ($this->n + 2);
9930
		for ($i=1; $i <= $this->n; ++$i) {
9931
			if (!isset($this->offsets[$i]) AND ($i > 1)) {
9932
				$this->_out(sprintf('0000000000 %05d f ', $freegen));
9933
				++$freegen;
9934
			} else {
9935
				$this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9936
			}
9937
		}
9938
		// TRAILER
9939
		$out = 'trailer'."\n";
9940
		$out .= '<<';
9941
		$out .= ' /Size '.($this->n + 1);
9942
		$out .= ' /Root '.$objid_catalog.' 0 R';
9943
		$out .= ' /Info '.$objid_info.' 0 R';
9944
		if ($this->encrypted) {
9945
			$out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9946
		}
9947
		$out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9948
		$out .= ' >>';
9949
		$this->_out($out);
9950
		$this->_out('startxref');
9951
		$this->_out($o);
9952
		$this->_out('%%EOF');
9953
		$this->state = 3; // end-of-doc
9954
		if ($this->diskcache) {
9955
			// remove temporary files used for images
9956
			foreach ($this->imagekeys as $key) {
9957
				// remove temporary files
9958
				unlink($this->images[$key]);
9959
			}
9960
			foreach ($this->fontkeys as $key) {
9961
				// remove temporary files
9962
				unlink($this->fonts[$key]);
9963
			}
9964
		}
9965
	}
9966
 
9967
	/**
9968
	 * Initialize a new page.
9969
	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
9970
	 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
9971
	 * @protected
9972
	 * @see getPageSizeFromFormat(), setPageFormat()
9973
	 */
9974
	protected function _beginpage($orientation='', $format='') {
9975
		++$this->page;
9976
		$this->pageobjects[$this->page] = array();
9977
		$this->setPageBuffer($this->page, '');
9978
		// initialize array for graphics tranformation positions inside a page buffer
9979
		$this->transfmrk[$this->page] = array();
9980
		$this->state = 2;
9981
		if (TCPDF_STATIC::empty_string($orientation)) {
9982
			if (isset($this->CurOrientation)) {
9983
				$orientation = $this->CurOrientation;
9984
			} elseif ($this->fwPt > $this->fhPt) {
9985
				// landscape
9986
				$orientation = 'L';
9987
			} else {
9988
				// portrait
9989
				$orientation = 'P';
9990
			}
9991
		}
9992
		if (TCPDF_STATIC::empty_string($format)) {
9993
			$this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
9994
			$this->setPageOrientation($orientation);
9995
		} else {
9996
			$this->setPageFormat($format, $orientation);
9997
		}
9998
		if ($this->rtl) {
9999
			$this->x = $this->w - $this->rMargin;
10000
		} else {
10001
			$this->x = $this->lMargin;
10002
		}
10003
		$this->y = $this->tMargin;
10004
		if (isset($this->newpagegroup[$this->page])) {
10005
			// start a new group
10006
			$this->currpagegroup = $this->newpagegroup[$this->page];
10007
			$this->pagegroups[$this->currpagegroup] = 1;
10008
		} elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10009
			++$this->pagegroups[$this->currpagegroup];
10010
		}
10011
	}
10012
 
10013
	/**
10014
	 * Mark end of page.
10015
	 * @protected
10016
	 */
10017
	protected function _endpage() {
10018
		$this->setVisibility('all');
10019
		$this->state = 1;
10020
	}
10021
 
10022
	/**
10023
	 * Begin a new object and return the object number.
10024
	 * @return int object number
10025
	 * @protected
10026
	 */
10027
	protected function _newobj() {
10028
		$this->_out($this->_getobj());
10029
		return $this->n;
10030
	}
10031
 
10032
	/**
10033
	 * Return the starting object string for the selected object ID.
10034
	 * @param $objid (int) Object ID (leave empty to get a new ID).
10035
	 * @return string the starting object string
10036
	 * @protected
10037
	 * @since 5.8.009 (2010-08-20)
10038
	 */
10039
	protected function _getobj($objid='') {
10040
		if ($objid === '') {
10041
			++$this->n;
10042
			$objid = $this->n;
10043
		}
10044
		$this->offsets[$objid] = $this->bufferlen;
10045
		$this->pageobjects[$this->page][] = $objid;
10046
		return $objid.' 0 obj';
10047
	}
10048
 
10049
	/**
10050
	 * Underline text.
10051
	 * @param $x (int) X coordinate
10052
	 * @param $y (int) Y coordinate
10053
	 * @param $txt (string) text to underline
10054
	 * @protected
10055
	 */
10056
	protected function _dounderline($x, $y, $txt) {
10057
		$w = $this->GetStringWidth($txt);
10058
		return $this->_dounderlinew($x, $y, $w);
10059
	}
10060
 
10061
	/**
10062
	 * Underline for rectangular text area.
10063
	 * @param $x (int) X coordinate
10064
	 * @param $y (int) Y coordinate
10065
	 * @param $w (int) width to underline
10066
	 * @protected
10067
	 * @since 4.8.008 (2009-09-29)
10068
	 */
10069
	protected function _dounderlinew($x, $y, $w) {
10070
		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10071
		return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10072
	}
10073
 
10074
	/**
10075
	 * Line through text.
10076
	 * @param $x (int) X coordinate
10077
	 * @param $y (int) Y coordinate
10078
	 * @param $txt (string) text to linethrough
10079
	 * @protected
10080
	 */
10081
	protected function _dolinethrough($x, $y, $txt) {
10082
		$w = $this->GetStringWidth($txt);
10083
		return $this->_dolinethroughw($x, $y, $w);
10084
	}
10085
 
10086
	/**
10087
	 * Line through for rectangular text area.
10088
	 * @param $x (int) X coordinate
10089
	 * @param $y (int) Y coordinate
10090
	 * @param $w (int) line length (width)
10091
	 * @protected
10092
	 * @since 4.9.008 (2009-09-29)
10093
	 */
10094
	protected function _dolinethroughw($x, $y, $w) {
10095
		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10096
		return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10097
	}
10098
 
10099
	/**
10100
	 * Overline text.
10101
	 * @param $x (int) X coordinate
10102
	 * @param $y (int) Y coordinate
10103
	 * @param $txt (string) text to overline
10104
	 * @protected
10105
	 * @since 4.9.015 (2010-04-19)
10106
	 */
10107
	protected function _dooverline($x, $y, $txt) {
10108
		$w = $this->GetStringWidth($txt);
10109
		return $this->_dooverlinew($x, $y, $w);
10110
	}
10111
 
10112
	/**
10113
	 * Overline for rectangular text area.
10114
	 * @param $x (int) X coordinate
10115
	 * @param $y (int) Y coordinate
10116
	 * @param $w (int) width to overline
10117
	 * @protected
10118
	 * @since 4.9.015 (2010-04-19)
10119
	 */
10120
	protected function _dooverlinew($x, $y, $w) {
10121
		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10122
		return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10123
 
10124
	}
10125
 
10126
	/**
10127
	 * Format a data string for meta information
10128
	 * @param $s (string) data string to escape.
10129
	 * @param $n (int) object ID
10130
	 * @return string escaped string.
10131
	 * @protected
10132
	 */
10133
	protected function _datastring($s, $n=0) {
10134
		if ($n == 0) {
10135
			$n = $this->n;
10136
		}
10137
		$s = $this->_encrypt_data($n, $s);
10138
		return '('. TCPDF_STATIC::_escape($s).')';
10139
	}
10140
 
10141
	/**
10142
	 * Set the document creation timestamp
10143
	 * @param $time (mixed) Document creation timestamp in seconds or date-time string.
10144
	 * @public
10145
	 * @since 5.9.152 (2012-03-23)
10146
	 */
10147
	public function setDocCreationTimestamp($time) {
10148
		if (is_string($time)) {
10149
			$time = TCPDF_STATIC::getTimestamp($time);
10150
		}
10151
		$this->doc_creation_timestamp = intval($time);
10152
	}
10153
 
10154
	/**
10155
	 * Set the document modification timestamp
10156
	 * @param $time (mixed) Document modification timestamp in seconds or date-time string.
10157
	 * @public
10158
	 * @since 5.9.152 (2012-03-23)
10159
	 */
10160
	public function setDocModificationTimestamp($time) {
10161
		if (is_string($time)) {
10162
			$time = TCPDF_STATIC::getTimestamp($time);
10163
		}
10164
		$this->doc_modification_timestamp = intval($time);
10165
	}
10166
 
10167
	/**
10168
	 * Returns document creation timestamp in seconds.
10169
	 * @return (int) Creation timestamp in seconds.
10170
	 * @public
10171
	 * @since 5.9.152 (2012-03-23)
10172
	 */
10173
	public function getDocCreationTimestamp() {
10174
		return $this->doc_creation_timestamp;
10175
	}
10176
 
10177
	/**
10178
	 * Returns document modification timestamp in seconds.
10179
	 * @return (int) Modfication timestamp in seconds.
10180
	 * @public
10181
	 * @since 5.9.152 (2012-03-23)
10182
	 */
10183
	public function getDocModificationTimestamp() {
10184
		return $this->doc_modification_timestamp;
10185
	}
10186
 
10187
	/**
10188
	 * Returns a formatted date for meta information
10189
	 * @param $n (int) Object ID.
10190
	 * @param $timestamp (int) Timestamp to convert.
10191
	 * @return string escaped date string.
10192
	 * @protected
10193
	 * @since 4.6.028 (2009-08-25)
10194
	 */
10195
	protected function _datestring($n=0, $timestamp=0) {
10196
		if ((empty($timestamp)) OR ($timestamp < 0)) {
10197
			$timestamp = $this->doc_creation_timestamp;
10198
		}
10199
		return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n);
10200
	}
10201
 
10202
	/**
10203
	 * Format a text string for meta information
10204
	 * @param $s (string) string to escape.
10205
	 * @param $n (int) object ID
10206
	 * @return string escaped string.
10207
	 * @protected
10208
	 */
10209
	protected function _textstring($s, $n=0) {
10210
		if ($this->isunicode) {
10211
			//Convert string to UTF-16BE
10212
			$s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10213
		}
10214
		return $this->_datastring($s, $n);
10215
	}
10216
 
10217
	/**
10218
	 * THIS METHOD IS DEPRECATED
10219
	 * Format a text string
10220
	 * @param $s (string) string to escape.
10221
	 * @return string escaped string.
10222
	 * @protected
10223
	 * @deprecated
10224
	 */
10225
	protected function _escapetext($s) {
10226
		if ($this->isunicode) {
10227
			if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
10228
				$s = TCPDF_FONTS::UTF8ToLatin1($s, $this->isunicode, $this->CurrentFont);
10229
			} else {
10230
				//Convert string to UTF-16BE and reverse RTL language
10231
				$s = TCPDF_FONTS::utf8StrRev($s, false, $this->tmprtl, $this->isunicode, $this->CurrentFont);
10232
			}
10233
		}
10234
		return TCPDF_STATIC::_escape($s);
10235
	}
10236
 
10237
	/**
10238
	 * get raw output stream.
10239
	 * @param $s (string) string to output.
10240
	 * @param $n (int) object reference for encryption mode
10241
	 * @protected
10242
	 * @author Nicola Asuni
10243
	 * @since 5.5.000 (2010-06-22)
10244
	 */
10245
	protected function _getrawstream($s, $n=0) {
10246
		if ($n <= 0) {
10247
			// default to current object
10248
			$n = $this->n;
10249
		}
10250
		return $this->_encrypt_data($n, $s);
10251
	}
10252
 
10253
	/**
10254
	 * Format output stream (DEPRECATED).
10255
	 * @param $s (string) string to output.
10256
	 * @param $n (int) object reference for encryption mode
10257
	 * @protected
10258
	 * @deprecated
10259
	 */
10260
	protected function _getstream($s, $n=0) {
10261
		return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10262
	}
10263
 
10264
	/**
10265
	 * Output a stream (DEPRECATED).
10266
	 * @param $s (string) string to output.
10267
	 * @param $n (int) object reference for encryption mode
10268
	 * @protected
10269
	 * @deprecated
10270
	 */
10271
	protected function _putstream($s, $n=0) {
10272
		$this->_out($this->_getstream($s, $n));
10273
	}
10274
 
10275
	/**
10276
	 * Output a string to the document.
10277
	 * @param $s (string) string to output.
10278
	 * @protected
10279
	 */
10280
	protected function _out($s) {
10281
		if ($this->state == 2) {
10282
			if ($this->inxobj) {
10283
				// we are inside an XObject template
10284
				$this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10285
			} elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10286
				// puts data before page footer
10287
				$pagebuff = $this->getPageBuffer($this->page);
10288
				$page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10289
				$footer = substr($pagebuff, -$this->footerlen[$this->page]);
10290
				$this->setPageBuffer($this->page, $page.$s."\n".$footer);
10291
				// update footer position
10292
				$this->footerpos[$this->page] += strlen($s."\n");
10293
			} else {
10294
				// set page data
10295
				$this->setPageBuffer($this->page, $s."\n", true);
10296
			}
10297
		} elseif ($this->state > 0) {
10298
			// set general data
10299
			$this->setBuffer($s."\n");
10300
		}
10301
	}
10302
 
10303
	/**
10304
	 * Set header font.
10305
	 * @param $font (array) font
10306
	 * @public
10307
	 * @since 1.1
10308
	 */
10309
	public function setHeaderFont($font) {
10310
		$this->header_font = $font;
10311
	}
10312
 
10313
	/**
10314
	 * Get header font.
10315
	 * @return array()
10316
	 * @public
10317
	 * @since 4.0.012 (2008-07-24)
10318
	 */
10319
	public function getHeaderFont() {
10320
		return $this->header_font;
10321
	}
10322
 
10323
	/**
10324
	 * Set footer font.
10325
	 * @param $font (array) font
10326
	 * @public
10327
	 * @since 1.1
10328
	 */
10329
	public function setFooterFont($font) {
10330
		$this->footer_font = $font;
10331
	}
10332
 
10333
	/**
10334
	 * Get Footer font.
10335
	 * @return array()
10336
	 * @public
10337
	 * @since 4.0.012 (2008-07-24)
10338
	 */
10339
	public function getFooterFont() {
10340
		return $this->footer_font;
10341
	}
10342
 
10343
	/**
10344
	 * Set language array.
10345
	 * @param $language (array)
10346
	 * @public
10347
	 * @since 1.1
10348
	 */
10349
	public function setLanguageArray($language) {
10350
		$this->l = $language;
10351
		if (isset($this->l['a_meta_dir'])) {
10352
			$this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10353
		} else {
10354
			$this->rtl = false;
10355
		}
10356
	}
10357
 
10358
	/**
10359
	 * Returns the PDF data.
10360
	 * @public
10361
	 */
10362
	public function getPDFData() {
10363
		if ($this->state < 3) {
10364
			$this->Close();
10365
		}
10366
		return $this->buffer;
10367
	}
10368
 
10369
	/**
10370
	 * Output anchor link.
10371
	 * @param $url (string) link URL or internal link (i.e.: &lt;a href="#23,4.5"&gt;link to page 23 at 4.5 Y position&lt;/a&gt;)
10372
	 * @param $name (string) link name
10373
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
10374
	 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
10375
	 * @param $color (array) array of RGB text color
10376
	 * @param $style (string) font style (U, D, B, I)
10377
	 * @param $firstblock (boolean) if true the string is the starting of a line.
10378
	 * @return the number of cells used or the remaining text if $firstline = true;
10379
	 * @public
10380
	 */
10381
	public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10382
		if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10383
			// convert url to internal link
10384
			$lnkdata = explode(',', $url);
10385
			if (isset($lnkdata[0])) {
10386
				$page = intval(substr($lnkdata[0], 1));
10387
				if (empty($page) OR ($page <= 0)) {
10388
					$page = $this->page;
10389
				}
10390
				if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10391
					$lnky = floatval($lnkdata[1]);
10392
				} else {
10393
					$lnky = 0;
10394
				}
10395
				$url = $this->AddLink();
10396
				$this->SetLink($url, $lnky, $page);
10397
			}
10398
		}
10399
		// store current settings
10400
		$prevcolor = $this->fgcolor;
10401
		$prevstyle = $this->FontStyle;
10402
		if (empty($color)) {
10403
			$this->SetTextColorArray($this->htmlLinkColorArray);
10404
		} else {
10405
			$this->SetTextColorArray($color);
10406
		}
10407
		if ($style == -1) {
10408
			$this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10409
		} else {
10410
			$this->SetFont('', $this->FontStyle.$style);
10411
		}
10412
		$ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10413
		// restore settings
10414
		$this->SetFont('', $prevstyle);
10415
		$this->SetTextColorArray($prevcolor);
10416
		return $ret;
10417
	}
10418
 
10419
	/**
10420
	 * Converts pixels to User's Units.
10421
	 * @param $px (int) pixels
10422
	 * @return float value in user's unit
10423
	 * @public
10424
	 * @see setImageScale(), getImageScale()
10425
	 */
10426
	public function pixelsToUnits($px) {
10427
		return ($px / ($this->imgscale * $this->k));
10428
	}
10429
 
10430
	/**
10431
	 * Reverse function for htmlentities.
10432
	 * Convert entities in UTF-8.
10433
	 * @param $text_to_convert (string) Text to convert.
10434
	 * @return string converted text string
10435
	 * @public
10436
	 */
10437
	public function unhtmlentities($text_to_convert) {
10438
		return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10439
	}
10440
 
10441
	// ENCRYPTION METHODS ----------------------------------
10442
 
10443
	/**
10444
	 * Compute encryption key depending on object number where the encrypted data is stored.
10445
	 * This is used for all strings and streams without crypt filter specifier.
10446
	 * @param $n (int) object number
10447
	 * @return int object key
10448
	 * @protected
10449
	 * @author Nicola Asuni
10450
	 * @since 2.0.000 (2008-01-02)
10451
	 */
10452
	protected function _objectkey($n) {
10453
		$objkey = $this->encryptdata['key'].pack('VXxx', $n);
10454
		if ($this->encryptdata['mode'] == 2) { // AES-128
10455
			// AES padding
10456
			$objkey .= "\x73\x41\x6C\x54"; // sAlT
10457
		}
10458
		$objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10459
		$objkey = substr($objkey, 0, 16);
10460
		return $objkey;
10461
	}
10462
 
10463
	/**
10464
	 * Encrypt the input string.
10465
	 * @param $n (int) object number
10466
	 * @param $s (string) data string to encrypt
10467
	 * @return encrypted string
10468
	 * @protected
10469
	 * @author Nicola Asuni
10470
	 * @since 5.0.005 (2010-05-11)
10471
	 */
10472
	protected function _encrypt_data($n, $s) {
10473
		if (!$this->encrypted) {
10474
			return $s;
10475
		}
10476
		switch ($this->encryptdata['mode']) {
10477
			case 0:   // RC4-40
10478
			case 1: { // RC4-128
10479
				$s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10480
				break;
10481
			}
10482
			case 2: { // AES-128
10483
				$s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10484
				break;
10485
			}
10486
			case 3: { // AES-256
10487
				$s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10488
				break;
10489
			}
10490
		}
10491
		return $s;
10492
	}
10493
 
10494
	/**
10495
	 * Put encryption on PDF document.
10496
	 * @protected
10497
	 * @author Nicola Asuni
10498
	 * @since 2.0.000 (2008-01-02)
10499
	 */
10500
	protected function _putencryption() {
10501
		if (!$this->encrypted) {
10502
			return;
10503
		}
10504
		$this->encryptdata['objid'] = $this->_newobj();
10505
		$out = '<<';
10506
		if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10507
			$this->encryptdata['Filter'] = 'Standard';
10508
		}
10509
		$out .= ' /Filter /'.$this->encryptdata['Filter'];
10510
		if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10511
			$out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10512
		}
10513
		if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10514
			$this->encryptdata['V'] = 1;
10515
		}
10516
		// V is a code specifying the algorithm to be used in encrypting and decrypting the document
10517
		$out .= ' /V '.$this->encryptdata['V'];
10518
		if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10519
			// The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10520
			$out .= ' /Length '.$this->encryptdata['Length'];
10521
		} else {
10522
			$out .= ' /Length 40';
10523
		}
10524
		if ($this->encryptdata['V'] >= 4) {
10525
			if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10526
				$this->encryptdata['StmF'] = 'Identity';
10527
			}
10528
			if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10529
				// The name of the crypt filter that shall be used when decrypting all strings in the document.
10530
				$this->encryptdata['StrF'] = 'Identity';
10531
			}
10532
			// A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10533
			if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10534
				$out .= ' /CF <<';
10535
				$out .= ' /'.$this->encryptdata['StmF'].' <<';
10536
				$out .= ' /Type /CryptFilter';
10537
				if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10538
					// The method used
10539
					$out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10540
					if ($this->encryptdata['pubkey']) {
10541
						$out .= ' /Recipients [';
10542
						foreach ($this->encryptdata['Recipients'] as $rec) {
10543
							$out .= ' <'.$rec.'>';
10544
						}
10545
						$out .= ' ]';
10546
						if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10547
							$out .= ' /EncryptMetadata false';
10548
						} else {
10549
							$out .= ' /EncryptMetadata true';
10550
						}
10551
					}
10552
				} else {
10553
					$out .= ' /CFM /None';
10554
				}
10555
				if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10556
					// The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10557
					$out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10558
				} else {
10559
					$out .= ' /AuthEvent /DocOpen';
10560
				}
10561
				if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10562
					// The bit length of the encryption key.
10563
					$out .= ' /Length '.$this->encryptdata['CF']['Length'];
10564
				}
10565
				$out .= ' >> >>';
10566
			}
10567
			// The name of the crypt filter that shall be used by default when decrypting streams.
10568
			$out .= ' /StmF /'.$this->encryptdata['StmF'];
10569
			// The name of the crypt filter that shall be used when decrypting all strings in the document.
10570
			$out .= ' /StrF /'.$this->encryptdata['StrF'];
10571
			if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10572
				// The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10573
				$out .= ' /EFF /'.$this->encryptdata[''];
10574
			}
10575
		}
10576
		// Additional encryption dictionary entries for the standard security handler
10577
		if ($this->encryptdata['pubkey']) {
10578
			if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10579
				$out .= ' /Recipients [';
10580
				foreach ($this->encryptdata['Recipients'] as $rec) {
10581
					$out .= ' <'.$rec.'>';
10582
				}
10583
				$out .= ' ]';
10584
			}
10585
		} else {
10586
			$out .= ' /R';
10587
			if ($this->encryptdata['V'] == 5) { // AES-256
10588
				$out .= ' 5';
10589
				$out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10590
				$out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10591
				$out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10592
			} elseif ($this->encryptdata['V'] == 4) { // AES-128
10593
				$out .= ' 4';
10594
			} elseif ($this->encryptdata['V'] < 2) { // RC-40
10595
				$out .= ' 2';
10596
			} else { // RC-128
10597
				$out .= ' 3';
10598
			}
10599
			$out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10600
			$out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10601
			$out .= ' /P '.$this->encryptdata['P'];
10602
			if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10603
				$out .= ' /EncryptMetadata false';
10604
			} else {
10605
				$out .= ' /EncryptMetadata true';
10606
			}
10607
		}
10608
		$out .= ' >>';
10609
		$out .= "\n".'endobj';
10610
		$this->_out($out);
10611
	}
10612
 
10613
	/**
10614
	 * Compute U value (used for encryption)
10615
	 * @return string U value
10616
	 * @protected
10617
	 * @since 2.0.000 (2008-01-02)
10618
	 * @author Nicola Asuni
10619
	 */
10620
	protected function _Uvalue() {
10621
		if ($this->encryptdata['mode'] == 0) { // RC4-40
10622
			return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10623
		} elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10624
			$tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10625
			$enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10626
			$len = strlen($tmp);
10627
			for ($i = 1; $i <= 19; ++$i) {
10628
				$ek = '';
10629
				for ($j = 0; $j < $len; ++$j) {
10630
					$ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10631
				}
10632
				$enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10633
			}
10634
			$enc .= str_repeat("\x00", 16);
10635
			return substr($enc, 0, 32);
10636
		} elseif ($this->encryptdata['mode'] == 3) { // AES-256
10637
			$seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10638
			// User Validation Salt
10639
			$this->encryptdata['UVS'] = substr($seed, 0, 8);
10640
			// User Key Salt
10641
			$this->encryptdata['UKS'] = substr($seed, 8, 16);
10642
			return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10643
		}
10644
	}
10645
 
10646
	/**
10647
	 * Compute UE value (used for encryption)
10648
	 * @return string UE value
10649
	 * @protected
10650
	 * @since 5.9.006 (2010-10-19)
10651
	 * @author Nicola Asuni
10652
	 */
10653
	protected function _UEvalue() {
10654
		$hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10655
		$iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10656
		return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10657
	}
10658
 
10659
	/**
10660
	 * Compute O value (used for encryption)
10661
	 * @return string O value
10662
	 * @protected
10663
	 * @since 2.0.000 (2008-01-02)
10664
	 * @author Nicola Asuni
10665
	 */
10666
	protected function _Ovalue() {
10667
		if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10668
			$tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10669
			if ($this->encryptdata['mode'] > 0) {
10670
				for ($i = 0; $i < 50; ++$i) {
10671
					$tmp = TCPDF_STATIC::_md5_16($tmp);
10672
				}
10673
			}
10674
			$owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10675
			$enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10676
			if ($this->encryptdata['mode'] > 0) {
10677
				$len = strlen($owner_key);
10678
				for ($i = 1; $i <= 19; ++$i) {
10679
					$ek = '';
10680
					for ($j = 0; $j < $len; ++$j) {
10681
						$ek .= chr(ord($owner_key[$j]) ^ $i);
10682
					}
10683
					$enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10684
				}
10685
			}
10686
			return $enc;
10687
		} elseif ($this->encryptdata['mode'] == 3) { // AES-256
10688
			$seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10689
			// Owner Validation Salt
10690
			$this->encryptdata['OVS'] = substr($seed, 0, 8);
10691
			// Owner Key Salt
10692
			$this->encryptdata['OKS'] = substr($seed, 8, 16);
10693
			return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10694
		}
10695
	}
10696
 
10697
	/**
10698
	 * Compute OE value (used for encryption)
10699
	 * @return string OE value
10700
	 * @protected
10701
	 * @since 5.9.006 (2010-10-19)
10702
	 * @author Nicola Asuni
10703
	 */
10704
	protected function _OEvalue() {
10705
		$hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10706
		$iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10707
		return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10708
	}
10709
 
10710
	/**
10711
	 * Convert password for AES-256 encryption mode
10712
	 * @param $password (string) password
10713
	 * @return string password
10714
	 * @protected
10715
	 * @since 5.9.006 (2010-10-19)
10716
	 * @author Nicola Asuni
10717
	 */
10718
	protected function _fixAES256Password($password) {
10719
		$psw = ''; // password to be returned
10720
		$psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10721
		foreach ($psw_array as $c) {
10722
			$psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10723
		}
10724
		return substr($psw, 0, 127);
10725
	}
10726
 
10727
	/**
10728
	 * Compute encryption key
10729
	 * @protected
10730
	 * @since 2.0.000 (2008-01-02)
10731
	 * @author Nicola Asuni
10732
	 */
10733
	protected function _generateencryptionkey() {
10734
		$keybytelen = ($this->encryptdata['Length'] / 8);
10735
		if (!$this->encryptdata['pubkey']) { // standard mode
10736
			if ($this->encryptdata['mode'] == 3) { // AES-256
10737
				// generate 256 bit random key
10738
				$this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10739
				// truncate passwords
10740
				$this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10741
				$this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10742
				// Compute U value
10743
				$this->encryptdata['U'] = $this->_Uvalue();
10744
				// Compute UE value
10745
				$this->encryptdata['UE'] = $this->_UEvalue();
10746
				// Compute O value
10747
				$this->encryptdata['O'] = $this->_Ovalue();
10748
				// Compute OE value
10749
				$this->encryptdata['OE'] = $this->_OEvalue();
10750
				// Compute P value
10751
				$this->encryptdata['P'] = $this->encryptdata['protection'];
10752
				// Computing the encryption dictionary's Perms (permissions) value
10753
				$perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10754
				$perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10755
				if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10756
					$perms .= 'F';
10757
				} else {
10758
					$perms .= 'T';
10759
				}
10760
				$perms .= 'adb'; // bytes 9-11
10761
				$perms .= 'nick'; // bytes 12-15
10762
				$iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
10763
				$this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
10764
			} else { // RC4-40, RC4-128, AES-128
10765
				// Pad passwords
10766
				$this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10767
				$this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10768
				// Compute O value
10769
				$this->encryptdata['O'] = $this->_Ovalue();
10770
				// get default permissions (reverse byte order)
10771
				$permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10772
				// Compute encryption key
10773
				$tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10774
				if ($this->encryptdata['mode'] > 0) {
10775
					for ($i = 0; $i < 50; ++$i) {
10776
						$tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10777
					}
10778
				}
10779
				$this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10780
				// Compute U value
10781
				$this->encryptdata['U'] = $this->_Uvalue();
10782
				// Compute P value
10783
				$this->encryptdata['P'] = $this->encryptdata['protection'];
10784
			}
10785
		} else { // Public-Key mode
10786
			// random 20-byte seed
10787
			$seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10788
			$recipient_bytes = '';
10789
			foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10790
				// for each public certificate
10791
				if (isset($pubkey['p'])) {
10792
					$pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10793
				} else {
10794
					$pkprotection = $this->encryptdata['protection'];
10795
				}
10796
				// get default permissions (reverse byte order)
10797
				$pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10798
				// envelope data
10799
				$envelope = $seed.$pkpermissions;
10800
				// write the envelope data to a temporary file
10801
				$tempkeyfile = TCPDF_STATIC::getObjFilename('tmpkey');
10802
				$f = fopen($tempkeyfile, 'wb');
10803
				if (!$f) {
10804
					$this->Error('Unable to create temporary key file: '.$tempkeyfile);
10805
				}
10806
				$envelope_length = strlen($envelope);
10807
				fwrite($f, $envelope, $envelope_length);
10808
				fclose($f);
10809
				$tempencfile = TCPDF_STATIC::getObjFilename('tmpenc');
10810
				if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10811
					$this->Error('Unable to encrypt the file: '.$tempkeyfile);
10812
				}
10813
				unlink($tempkeyfile);
10814
				// read encryption signature
10815
				$signature = file_get_contents($tempencfile, false, null, $envelope_length);
10816
				unlink($tempencfile);
10817
				// extract signature
10818
				$signature = substr($signature, strpos($signature, 'Content-Disposition'));
10819
				$tmparr = explode("\n\n", $signature);
10820
				$signature = trim($tmparr[1]);
10821
				unset($tmparr);
10822
				// decode signature
10823
				$signature = base64_decode($signature);
10824
				// convert signature to hex
10825
				$hexsignature = current(unpack('H*', $signature));
10826
				// store signature on recipients array
10827
				$this->encryptdata['Recipients'][] = $hexsignature;
10828
				// The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10829
				$recipient_bytes .= $signature;
10830
			}
10831
			// calculate encryption key
10832
			if ($this->encryptdata['mode'] == 3) { // AES-256
10833
				$this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10834
			} else { // RC4-40, RC4-128, AES-128
10835
				$this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10836
			}
10837
		}
10838
	}
10839
 
10840
	/**
10841
	 * Set document protection
10842
	 * Remark: the protection against modification is for people who have the full Acrobat product.
10843
	 * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access.
10844
	 * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts.
10845
	 * @param $permissions (Array) the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul>
10846
	 * @param $user_pass (String) user password. Empty by default.
10847
	 * @param $owner_pass (String) owner password. If not specified, a random value is used.
10848
	 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10849
	 * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../examples/data/cert/tcpdf.crt', 'p' => array('print')))
10850
	 * @public
10851
	 * @since 2.0.000 (2008-01-02)
10852
	 * @author Nicola Asuni
10853
	 */
10854
	public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
10855
		if ($this->pdfa_mode) {
10856
			// encryption is not allowed in PDF/A mode
10857
			return;
10858
		}
10859
		$this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10860
		if (($pubkeys !== null) AND (is_array($pubkeys))) {
10861
			// public-key mode
10862
			$this->encryptdata['pubkeys'] = $pubkeys;
10863
			if ($mode == 0) {
10864
				// public-Key Security requires at least 128 bit
10865
				$mode = 1;
10866
			}
10867
			if (!function_exists('openssl_pkcs7_encrypt')) {
10868
				$this->Error('Public-Key Security requires openssl library.');
10869
			}
10870
			// Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10871
			$this->encryptdata['pubkey'] = true;
10872
			$this->encryptdata['Filter'] = 'Adobe.PubSec';
10873
			$this->encryptdata['StmF'] = 'DefaultCryptFilter';
10874
			$this->encryptdata['StrF'] = 'DefaultCryptFilter';
10875
		} else {
10876
			// standard mode (password mode)
10877
			$this->encryptdata['pubkey'] = false;
10878
			$this->encryptdata['Filter'] = 'Standard';
10879
			$this->encryptdata['StmF'] = 'StdCF';
10880
			$this->encryptdata['StrF'] = 'StdCF';
10881
		}
10882
		if ($mode > 1) { // AES
10883
			if (!extension_loaded('mcrypt')) {
10884
				$this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10885
			}
10886
			if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10887
				$this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10888
			}
10889
			if (($mode == 3) AND !function_exists('hash')) {
10890
				// the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10891
				$this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10892
			}
10893
		}
10894
		if ($owner_pass === null) {
10895
			$owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10896
		}
10897
		$this->encryptdata['user_password'] = $user_pass;
10898
		$this->encryptdata['owner_password'] = $owner_pass;
10899
		$this->encryptdata['mode'] = $mode;
10900
		switch ($mode) {
10901
			case 0: { // RC4 40 bit
10902
				$this->encryptdata['V'] = 1;
10903
				$this->encryptdata['Length'] = 40;
10904
				$this->encryptdata['CF']['CFM'] = 'V2';
10905
				break;
10906
			}
10907
			case 1: { // RC4 128 bit
10908
				$this->encryptdata['V'] = 2;
10909
				$this->encryptdata['Length'] = 128;
10910
				$this->encryptdata['CF']['CFM'] = 'V2';
10911
				if ($this->encryptdata['pubkey']) {
10912
					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10913
					$this->encryptdata['Recipients'] = array();
10914
				}
10915
				break;
10916
			}
10917
			case 2: { // AES 128 bit
10918
				$this->encryptdata['V'] = 4;
10919
				$this->encryptdata['Length'] = 128;
10920
				$this->encryptdata['CF']['CFM'] = 'AESV2';
10921
				$this->encryptdata['CF']['Length'] = 128;
10922
				if ($this->encryptdata['pubkey']) {
10923
					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10924
					$this->encryptdata['Recipients'] = array();
10925
				}
10926
				break;
10927
			}
10928
			case 3: { // AES 256 bit
10929
				$this->encryptdata['V'] = 5;
10930
				$this->encryptdata['Length'] = 256;
10931
				$this->encryptdata['CF']['CFM'] = 'AESV3';
10932
				$this->encryptdata['CF']['Length'] = 256;
10933
				if ($this->encryptdata['pubkey']) {
10934
					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10935
					$this->encryptdata['Recipients'] = array();
10936
				}
10937
				break;
10938
			}
10939
		}
10940
		$this->encrypted = true;
10941
		$this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10942
		$this->_generateencryptionkey();
10943
	}
10944
 
10945
	// END OF ENCRYPTION FUNCTIONS -------------------------
10946
 
10947
	// START TRANSFORMATIONS SECTION -----------------------
10948
 
10949
	/**
10950
	 * Starts a 2D tranformation saving current graphic state.
10951
	 * This function must be called before scaling, mirroring, translation, rotation and skewing.
10952
	 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
10953
	 * @public
10954
	 * @since 2.1.000 (2008-01-07)
10955
	 * @see StartTransform(), StopTransform()
10956
	 */
10957
	public function StartTransform() {
10958
		if ($this->state != 2) {
10959
			return;
10960
		}
10961
		$this->_out('q');
10962
		if ($this->inxobj) {
10963
			// we are inside an XObject template
10964
			$this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10965
		} else {
10966
			$this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10967
		}
10968
		++$this->transfmatrix_key;
10969
		$this->transfmatrix[$this->transfmatrix_key] = array();
10970
	}
10971
 
10972
	/**
10973
	 * Stops a 2D tranformation restoring previous graphic state.
10974
	 * This function must be called after scaling, mirroring, translation, rotation and skewing.
10975
	 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
10976
	 * @public
10977
	 * @since 2.1.000 (2008-01-07)
10978
	 * @see StartTransform(), StopTransform()
10979
	 */
10980
	public function StopTransform() {
10981
		if ($this->state != 2) {
10982
			return;
10983
		}
10984
		$this->_out('Q');
10985
		if (isset($this->transfmatrix[$this->transfmatrix_key])) {
10986
			array_pop($this->transfmatrix[$this->transfmatrix_key]);
10987
			--$this->transfmatrix_key;
10988
		}
10989
		if ($this->inxobj) {
10990
			// we are inside an XObject template
10991
			array_pop($this->xobjects[$this->xobjid]['transfmrk']);
10992
		} else {
10993
			array_pop($this->transfmrk[$this->page]);
10994
		}
10995
	}
10996
	/**
10997
	 * Horizontal Scaling.
10998
	 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
10999
	 * @param $x (int) abscissa of the scaling center. Default is current x position
11000
	 * @param $y (int) ordinate of the scaling center. Default is current y position
11001
	 * @public
11002
	 * @since 2.1.000 (2008-01-07)
11003
	 * @see StartTransform(), StopTransform()
11004
	 */
11005
	public function ScaleX($s_x, $x='', $y='') {
11006
		$this->Scale($s_x, 100, $x, $y);
11007
	}
11008
 
11009
	/**
11010
	 * Vertical Scaling.
11011
	 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11012
	 * @param $x (int) abscissa of the scaling center. Default is current x position
11013
	 * @param $y (int) ordinate of the scaling center. Default is current y position
11014
	 * @public
11015
	 * @since 2.1.000 (2008-01-07)
11016
	 * @see StartTransform(), StopTransform()
11017
	 */
11018
	public function ScaleY($s_y, $x='', $y='') {
11019
		$this->Scale(100, $s_y, $x, $y);
11020
	}
11021
 
11022
	/**
11023
	 * Vertical and horizontal proportional Scaling.
11024
	 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
11025
	 * @param $x (int) abscissa of the scaling center. Default is current x position
11026
	 * @param $y (int) ordinate of the scaling center. Default is current y position
11027
	 * @public
11028
	 * @since 2.1.000 (2008-01-07)
11029
	 * @see StartTransform(), StopTransform()
11030
	 */
11031
	public function ScaleXY($s, $x='', $y='') {
11032
		$this->Scale($s, $s, $x, $y);
11033
	}
11034
 
11035
	/**
11036
	 * Vertical and horizontal non-proportional Scaling.
11037
	 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11038
	 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11039
	 * @param $x (int) abscissa of the scaling center. Default is current x position
11040
	 * @param $y (int) ordinate of the scaling center. Default is current y position
11041
	 * @public
11042
	 * @since 2.1.000 (2008-01-07)
11043
	 * @see StartTransform(), StopTransform()
11044
	 */
11045
	public function Scale($s_x, $s_y, $x='', $y='') {
11046
		if ($x === '') {
11047
			$x = $this->x;
11048
		}
11049
		if ($y === '') {
11050
			$y = $this->y;
11051
		}
11052
		if (($s_x == 0) OR ($s_y == 0)) {
11053
			$this->Error('Please do not use values equal to zero for scaling');
11054
		}
11055
		$y = ($this->h - $y) * $this->k;
11056
		$x *= $this->k;
11057
		//calculate elements of transformation matrix
11058
		$s_x /= 100;
11059
		$s_y /= 100;
11060
		$tm = array();
11061
		$tm[0] = $s_x;
11062
		$tm[1] = 0;
11063
		$tm[2] = 0;
11064
		$tm[3] = $s_y;
11065
		$tm[4] = $x * (1 - $s_x);
11066
		$tm[5] = $y * (1 - $s_y);
11067
		//scale the coordinate system
11068
		$this->Transform($tm);
11069
	}
11070
 
11071
	/**
11072
	 * Horizontal Mirroring.
11073
	 * @param $x (int) abscissa of the point. Default is current x position
11074
	 * @public
11075
	 * @since 2.1.000 (2008-01-07)
11076
	 * @see StartTransform(), StopTransform()
11077
	 */
11078
	public function MirrorH($x='') {
11079
		$this->Scale(-100, 100, $x);
11080
	}
11081
 
11082
	/**
11083
	 * Verical Mirroring.
11084
	 * @param $y (int) ordinate of the point. Default is current y position
11085
	 * @public
11086
	 * @since 2.1.000 (2008-01-07)
11087
	 * @see StartTransform(), StopTransform()
11088
	 */
11089
	public function MirrorV($y='') {
11090
		$this->Scale(100, -100, '', $y);
11091
	}
11092
 
11093
	/**
11094
	 * Point reflection mirroring.
11095
	 * @param $x (int) abscissa of the point. Default is current x position
11096
	 * @param $y (int) ordinate of the point. Default is current y position
11097
	 * @public
11098
	 * @since 2.1.000 (2008-01-07)
11099
	 * @see StartTransform(), StopTransform()
11100
	 */
11101
	public function MirrorP($x='',$y='') {
11102
		$this->Scale(-100, -100, $x, $y);
11103
	}
11104
 
11105
	/**
11106
	 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11107
	 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
11108
	 * @param $x (int) abscissa of the point. Default is current x position
11109
	 * @param $y (int) ordinate of the point. Default is current y position
11110
	 * @public
11111
	 * @since 2.1.000 (2008-01-07)
11112
	 * @see StartTransform(), StopTransform()
11113
	 */
11114
	public function MirrorL($angle=0, $x='',$y='') {
11115
		$this->Scale(-100, 100, $x, $y);
11116
		$this->Rotate(-2*($angle-90), $x, $y);
11117
	}
11118
 
11119
	/**
11120
	 * Translate graphic object horizontally.
11121
	 * @param $t_x (int) movement to the right (or left for RTL)
11122
	 * @public
11123
	 * @since 2.1.000 (2008-01-07)
11124
	 * @see StartTransform(), StopTransform()
11125
	 */
11126
	public function TranslateX($t_x) {
11127
		$this->Translate($t_x, 0);
11128
	}
11129
 
11130
	/**
11131
	 * Translate graphic object vertically.
11132
	 * @param $t_y (int) movement to the bottom
11133
	 * @public
11134
	 * @since 2.1.000 (2008-01-07)
11135
	 * @see StartTransform(), StopTransform()
11136
	 */
11137
	public function TranslateY($t_y) {
11138
		$this->Translate(0, $t_y);
11139
	}
11140
 
11141
	/**
11142
	 * Translate graphic object horizontally and vertically.
11143
	 * @param $t_x (int) movement to the right
11144
	 * @param $t_y (int) movement to the bottom
11145
	 * @public
11146
	 * @since 2.1.000 (2008-01-07)
11147
	 * @see StartTransform(), StopTransform()
11148
	 */
11149
	public function Translate($t_x, $t_y) {
11150
		//calculate elements of transformation matrix
11151
		$tm = array();
11152
		$tm[0] = 1;
11153
		$tm[1] = 0;
11154
		$tm[2] = 0;
11155
		$tm[3] = 1;
11156
		$tm[4] = $t_x * $this->k;
11157
		$tm[5] = -$t_y * $this->k;
11158
		//translate the coordinate system
11159
		$this->Transform($tm);
11160
	}
11161
 
11162
	/**
11163
	 * Rotate object.
11164
	 * @param $angle (float) angle in degrees for counter-clockwise rotation
11165
	 * @param $x (int) abscissa of the rotation center. Default is current x position
11166
	 * @param $y (int) ordinate of the rotation center. Default is current y position
11167
	 * @public
11168
	 * @since 2.1.000 (2008-01-07)
11169
	 * @see StartTransform(), StopTransform()
11170
	 */
11171
	public function Rotate($angle, $x='', $y='') {
11172
		if ($x === '') {
11173
			$x = $this->x;
11174
		}
11175
		if ($y === '') {
11176
			$y = $this->y;
11177
		}
11178
		$y = ($this->h - $y) * $this->k;
11179
		$x *= $this->k;
11180
		//calculate elements of transformation matrix
11181
		$tm = array();
11182
		$tm[0] = cos(deg2rad($angle));
11183
		$tm[1] = sin(deg2rad($angle));
11184
		$tm[2] = -$tm[1];
11185
		$tm[3] = $tm[0];
11186
		$tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11187
		$tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11188
		//rotate the coordinate system around ($x,$y)
11189
		$this->Transform($tm);
11190
	}
11191
 
11192
	/**
11193
	 * Skew horizontally.
11194
	 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11195
	 * @param $x (int) abscissa of the skewing center. default is current x position
11196
	 * @param $y (int) ordinate of the skewing center. default is current y position
11197
	 * @public
11198
	 * @since 2.1.000 (2008-01-07)
11199
	 * @see StartTransform(), StopTransform()
11200
	 */
11201
	public function SkewX($angle_x, $x='', $y='') {
11202
		$this->Skew($angle_x, 0, $x, $y);
11203
	}
11204
 
11205
	/**
11206
	 * Skew vertically.
11207
	 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11208
	 * @param $x (int) abscissa of the skewing center. default is current x position
11209
	 * @param $y (int) ordinate of the skewing center. default is current y position
11210
	 * @public
11211
	 * @since 2.1.000 (2008-01-07)
11212
	 * @see StartTransform(), StopTransform()
11213
	 */
11214
	public function SkewY($angle_y, $x='', $y='') {
11215
		$this->Skew(0, $angle_y, $x, $y);
11216
	}
11217
 
11218
	/**
11219
	 * Skew.
11220
	 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11221
	 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11222
	 * @param $x (int) abscissa of the skewing center. default is current x position
11223
	 * @param $y (int) ordinate of the skewing center. default is current y position
11224
	 * @public
11225
	 * @since 2.1.000 (2008-01-07)
11226
	 * @see StartTransform(), StopTransform()
11227
	 */
11228
	public function Skew($angle_x, $angle_y, $x='', $y='') {
11229
		if ($x === '') {
11230
			$x = $this->x;
11231
		}
11232
		if ($y === '') {
11233
			$y = $this->y;
11234
		}
11235
		if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11236
			$this->Error('Please use values between -90 and +90 degrees for Skewing.');
11237
		}
11238
		$x *= $this->k;
11239
		$y = ($this->h - $y) * $this->k;
11240
		//calculate elements of transformation matrix
11241
		$tm = array();
11242
		$tm[0] = 1;
11243
		$tm[1] = tan(deg2rad($angle_y));
11244
		$tm[2] = tan(deg2rad($angle_x));
11245
		$tm[3] = 1;
11246
		$tm[4] = -$tm[2] * $y;
11247
		$tm[5] = -$tm[1] * $x;
11248
		//skew the coordinate system
11249
		$this->Transform($tm);
11250
	}
11251
 
11252
	/**
11253
	 * Apply graphic transformations.
11254
	 * @param $tm (array) transformation matrix
11255
	 * @protected
11256
	 * @since 2.1.000 (2008-01-07)
11257
	 * @see StartTransform(), StopTransform()
11258
	 */
11259
	protected function Transform($tm) {
11260
		if ($this->state != 2) {
11261
			return;
11262
		}
11263
		$this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11264
		// add tranformation matrix
11265
		$this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11266
		// update transformation mark
11267
		if ($this->inxobj) {
11268
			// we are inside an XObject template
11269
			if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11270
				$key = key($this->xobjects[$this->xobjid]['transfmrk']);
11271
				$this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11272
			}
11273
		} elseif (end($this->transfmrk[$this->page]) !== false) {
11274
			$key = key($this->transfmrk[$this->page]);
11275
			$this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11276
		}
11277
	}
11278
 
11279
	// END TRANSFORMATIONS SECTION -------------------------
11280
 
11281
	// START GRAPHIC FUNCTIONS SECTION ---------------------
11282
	// The following section is based on the code provided by David Hernandez Sanz
11283
 
11284
	/**
11285
	 * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page.
11286
	 * @param $width (float) The width.
11287
	 * @public
11288
	 * @since 1.0
11289
	 * @see Line(), Rect(), Cell(), MultiCell()
11290
	 */
11291
	public function SetLineWidth($width) {
11292
		//Set line width
11293
		$this->LineWidth = $width;
11294
		$this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11295
		if ($this->state == 2) {
11296
			$this->_out($this->linestyleWidth);
11297
		}
11298
	}
11299
 
11300
	/**
11301
	 * Returns the current the line width.
11302
	 * @return int Line width
11303
	 * @public
11304
	 * @since 2.1.000 (2008-01-07)
11305
	 * @see Line(), SetLineWidth()
11306
	 */
11307
	public function GetLineWidth() {
11308
		return $this->LineWidth;
11309
	}
11310
 
11311
	/**
11312
	 * Set line style.
11313
	 * @param $style (array) Line style. Array with keys among the following:
11314
	 * <ul>
11315
	 *	 <li>width (float): Width of the line in user units.</li>
11316
	 *	 <li>cap (string): Type of cap to put on the line. Possible values are:
11317
	 * butt, round, square. The difference between "square" and "butt" is that
11318
	 * "square" projects a flat end past the end of the line.</li>
11319
	 *	 <li>join (string): Type of join. Possible values are: miter, round,
11320
	 * bevel.</li>
11321
	 *	 <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11322
	 * series of length values, which are the lengths of the on and off dashes.
11323
	 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11324
	 * 1 off, 2 on, 1 off, ...</li>
11325
	 *	 <li>phase (integer): Modifier on the dash pattern which is used to shift
11326
	 * the point at which the pattern starts.</li>
11327
	 *	 <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName).</li>
11328
	 * </ul>
11329
	 * @param $ret (boolean) if true do not send the command.
11330
	 * @return string the PDF command
11331
	 * @public
11332
	 * @since 2.1.000 (2008-01-08)
11333
	 */
11334
	public function SetLineStyle($style, $ret=false) {
11335
		$s = ''; // string to be returned
11336
		if (!is_array($style)) {
11337
			return;
11338
		}
11339
		if (isset($style['width'])) {
11340
			$this->LineWidth = $style['width'];
11341
			$this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11342
			$s .= $this->linestyleWidth.' ';
11343
		}
11344
		if (isset($style['cap'])) {
11345
			$ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11346
			if (isset($ca[$style['cap']])) {
11347
				$this->linestyleCap = $ca[$style['cap']].' J';
11348
				$s .= $this->linestyleCap.' ';
11349
			}
11350
		}
11351
		if (isset($style['join'])) {
11352
			$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11353
			if (isset($ja[$style['join']])) {
11354
				$this->linestyleJoin = $ja[$style['join']].' j';
11355
				$s .= $this->linestyleJoin.' ';
11356
			}
11357
		}
11358
		if (isset($style['dash'])) {
11359
			$dash_string = '';
11360
			if ($style['dash']) {
11361
				if (preg_match('/^.+,/', $style['dash']) > 0) {
11362
					$tab = explode(',', $style['dash']);
11363
				} else {
11364
					$tab = array($style['dash']);
11365
				}
11366
				$dash_string = '';
11367
				foreach ($tab as $i => $v) {
11368
					if ($i) {
11369
						$dash_string .= ' ';
11370
					}
11371
					$dash_string .= sprintf('%F', $v);
11372
				}
11373
			}
11374
			if (!isset($style['phase']) OR !$style['dash']) {
11375
				$style['phase'] = 0;
11376
			}
11377
			$this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11378
			$s .= $this->linestyleDash.' ';
11379
		}
11380
		if (isset($style['color'])) {
11381
			$s .= $this->SetDrawColorArray($style['color'], true).' ';
11382
		}
11383
		if (!$ret AND ($this->state == 2)) {
11384
			$this->_out($s);
11385
		}
11386
		return $s;
11387
	}
11388
 
11389
	/**
11390
	 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11391
	 * @param $x (float) Abscissa of point.
11392
	 * @param $y (float) Ordinate of point.
11393
	 * @protected
11394
	 * @since 2.1.000 (2008-01-08)
11395
	 */
11396
	protected function _outPoint($x, $y) {
11397
		if ($this->state == 2) {
11398
			$this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11399
		}
11400
	}
11401
 
11402
	/**
11403
	 * Append a straight line segment from the current point to the point (x, y).
11404
	 * The new current point shall be (x, y).
11405
	 * @param $x (float) Abscissa of end point.
11406
	 * @param $y (float) Ordinate of end point.
11407
	 * @protected
11408
	 * @since 2.1.000 (2008-01-08)
11409
	 */
11410
	protected function _outLine($x, $y) {
11411
		if ($this->state == 2) {
11412
			$this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11413
		}
11414
	}
11415
 
11416
	/**
11417
	 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11418
	 * @param $x (float) Abscissa of upper-left corner.
11419
	 * @param $y (float) Ordinate of upper-left corner.
11420
	 * @param $w (float) Width.
11421
	 * @param $h (float) Height.
11422
	 * @param $op (string) options
11423
	 * @protected
11424
	 * @since 2.1.000 (2008-01-08)
11425
	 */
11426
	protected function _outRect($x, $y, $w, $h, $op) {
11427
		if ($this->state == 2) {
11428
			$this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11429
		}
11430
	}
11431
 
11432
	/**
11433
	 * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bézier control points.
11434
	 * The new current point shall be (x3, y3).
11435
	 * @param $x1 (float) Abscissa of control point 1.
11436
	 * @param $y1 (float) Ordinate of control point 1.
11437
	 * @param $x2 (float) Abscissa of control point 2.
11438
	 * @param $y2 (float) Ordinate of control point 2.
11439
	 * @param $x3 (float) Abscissa of end point.
11440
	 * @param $y3 (float) Ordinate of end point.
11441
	 * @protected
11442
	 * @since 2.1.000 (2008-01-08)
11443
	 */
11444
	protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11445
		if ($this->state == 2) {
11446
			$this->_out(sprintf('%F %F %F %F %F %F c', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11447
		}
11448
	}
11449
 
11450
	/**
11451
	 * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bézier control points.
11452
	 * The new current point shall be (x3, y3).
11453
	 * @param $x2 (float) Abscissa of control point 2.
11454
	 * @param $y2 (float) Ordinate of control point 2.
11455
	 * @param $x3 (float) Abscissa of end point.
11456
	 * @param $y3 (float) Ordinate of end point.
11457
	 * @protected
11458
	 * @since 4.9.019 (2010-04-26)
11459
	 */
11460
	protected function _outCurveV($x2, $y2, $x3, $y3) {
11461
		if ($this->state == 2) {
11462
			$this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11463
		}
11464
	}
11465
 
11466
	/**
11467
	 * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bézier control points.
11468
	 * The new current point shall be (x3, y3).
11469
	 * @param $x1 (float) Abscissa of control point 1.
11470
	 * @param $y1 (float) Ordinate of control point 1.
11471
	 * @param $x3 (float) Abscissa of end point.
11472
	 * @param $y3 (float) Ordinate of end point.
11473
	 * @protected
11474
	 * @since 2.1.000 (2008-01-08)
11475
	 */
11476
	protected function _outCurveY($x1, $y1, $x3, $y3) {
11477
		if ($this->state == 2) {
11478
			$this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11479
		}
11480
	}
11481
 
11482
	/**
11483
	 * Draws a line between two points.
11484
	 * @param $x1 (float) Abscissa of first point.
11485
	 * @param $y1 (float) Ordinate of first point.
11486
	 * @param $x2 (float) Abscissa of second point.
11487
	 * @param $y2 (float) Ordinate of second point.
11488
	 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11489
	 * @public
11490
	 * @since 1.0
11491
	 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11492
	 */
11493
	public function Line($x1, $y1, $x2, $y2, $style=array()) {
11494
		if ($this->state != 2) {
11495
			return;
11496
		}
11497
		if (is_array($style)) {
11498
			$this->SetLineStyle($style);
11499
		}
11500
		$this->_outPoint($x1, $y1);
11501
		$this->_outLine($x2, $y2);
11502
		$this->_out('S');
11503
	}
11504
 
11505
	/**
11506
	 * Draws a rectangle.
11507
	 * @param $x (float) Abscissa of upper-left corner.
11508
	 * @param $y (float) Ordinate of upper-left corner.
11509
	 * @param $w (float) Width.
11510
	 * @param $h (float) Height.
11511
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11512
	 * @param $border_style (array) Border style of rectangle. Array with keys among the following:
11513
	 * <ul>
11514
	 *	 <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11515
	 *	 <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11516
	 * </ul>
11517
	 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11518
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11519
	 * @public
11520
	 * @since 1.0
11521
	 * @see SetLineStyle()
11522
	 */
11523
	public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11524
		if ($this->state != 2) {
11525
			return;
11526
		}
11527
		if (empty($style)) {
11528
			$style = 'S';
11529
		}
11530
		if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11531
			// set background color
11532
			$this->SetFillColorArray($fill_color);
11533
		}
11534
		if (!empty($border_style)) {
11535
			if (isset($border_style['all']) AND !empty($border_style['all'])) {
11536
				//set global style for border
11537
				$this->SetLineStyle($border_style['all']);
11538
				$border_style = array();
11539
			} else {
11540
				// remove stroke operator from style
11541
				$opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' );
11542
				if (isset($opnostroke[$style])) {
11543
					$style = $opnostroke[$style];
11544
				}
11545
			}
11546
		}
11547
		if (!empty($style)) {
11548
			$op = TCPDF_STATIC::getPathPaintOperator($style);
11549
			$this->_outRect($x, $y, $w, $h, $op);
11550
		}
11551
		if (!empty($border_style)) {
11552
			$border_style2 = array();
11553
			foreach ($border_style as $line => $value) {
11554
				$length = strlen($line);
11555
				for ($i = 0; $i < $length; ++$i) {
11556
					$border_style2[$line[$i]] = $value;
11557
				}
11558
			}
11559
			$border_style = $border_style2;
11560
			if (isset($border_style['L']) AND $border_style['L']) {
11561
				$this->Line($x, $y, $x, $y + $h, $border_style['L']);
11562
			}
11563
			if (isset($border_style['T']) AND $border_style['T']) {
11564
				$this->Line($x, $y, $x + $w, $y, $border_style['T']);
11565
			}
11566
			if (isset($border_style['R']) AND $border_style['R']) {
11567
				$this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11568
			}
11569
			if (isset($border_style['B']) AND $border_style['B']) {
11570
				$this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11571
			}
11572
		}
11573
	}
11574
 
11575
	/**
11576
	 * Draws a Bezier curve.
11577
	 * The Bezier curve is a tangent to the line between the control points at
11578
	 * either end of the curve.
11579
	 * @param $x0 (float) Abscissa of start point.
11580
	 * @param $y0 (float) Ordinate of start point.
11581
	 * @param $x1 (float) Abscissa of control point 1.
11582
	 * @param $y1 (float) Ordinate of control point 1.
11583
	 * @param $x2 (float) Abscissa of control point 2.
11584
	 * @param $y2 (float) Ordinate of control point 2.
11585
	 * @param $x3 (float) Abscissa of end point.
11586
	 * @param $y3 (float) Ordinate of end point.
11587
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11588
	 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11589
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11590
	 * @public
11591
	 * @see SetLineStyle()
11592
	 * @since 2.1.000 (2008-01-08)
11593
	 */
11594
	public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11595
		if ($this->state != 2) {
11596
			return;
11597
		}
11598
		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11599
			$this->SetFillColorArray($fill_color);
11600
		}
11601
		$op = TCPDF_STATIC::getPathPaintOperator($style);
11602
		if ($line_style) {
11603
			$this->SetLineStyle($line_style);
11604
		}
11605
		$this->_outPoint($x0, $y0);
11606
		$this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11607
		$this->_out($op);
11608
	}
11609
 
11610
	/**
11611
	 * Draws a poly-Bezier curve.
11612
	 * Each Bezier curve segment is a tangent to the line between the control points at
11613
	 * either end of the curve.
11614
	 * @param $x0 (float) Abscissa of start point.
11615
	 * @param $y0 (float) Ordinate of start point.
11616
	 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11617
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11618
	 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11619
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11620
	 * @public
11621
	 * @see SetLineStyle()
11622
	 * @since 3.0008 (2008-05-12)
11623
	 */
11624
	public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11625
		if ($this->state != 2) {
11626
			return;
11627
		}
11628
		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11629
			$this->SetFillColorArray($fill_color);
11630
		}
11631
		$op = TCPDF_STATIC::getPathPaintOperator($style);
11632
		if ($op == 'f') {
11633
			$line_style = array();
11634
		}
11635
		if ($line_style) {
11636
			$this->SetLineStyle($line_style);
11637
		}
11638
		$this->_outPoint($x0, $y0);
11639
		foreach ($segments as $segment) {
11640
			list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11641
			$this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11642
		}
11643
		$this->_out($op);
11644
	}
11645
 
11646
	/**
11647
	 * Draws an ellipse.
11648
	 * An ellipse is formed from n Bezier curves.
11649
	 * @param $x0 (float) Abscissa of center point.
11650
	 * @param $y0 (float) Ordinate of center point.
11651
	 * @param $rx (float) Horizontal radius.
11652
	 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11653
	 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11654
	 * @param $astart: (float) Angle start of draw line. Default value: 0.
11655
	 * @param $afinish: (float) Angle finish of draw line. Default value: 360.
11656
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11657
	 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11658
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11659
	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11660
	 * @author Nicola Asuni
11661
	 * @public
11662
	 * @since 2.1.000 (2008-01-08)
11663
	 */
11664
	public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11665
		if ($this->state != 2) {
11666
			return;
11667
		}
11668
		if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11669
			$ry = $rx;
11670
		}
11671
		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11672
			$this->SetFillColorArray($fill_color);
11673
		}
11674
		$op = TCPDF_STATIC::getPathPaintOperator($style);
11675
		if ($op == 'f') {
11676
			$line_style = array();
11677
		}
11678
		if ($line_style) {
11679
			$this->SetLineStyle($line_style);
11680
		}
11681
		$this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11682
		$this->_out($op);
11683
	}
11684
 
11685
	/**
11686
	 * Append an elliptical arc to the current path.
11687
	 * An ellipse is formed from n Bezier curves.
11688
	 * @param $xc (float) Abscissa of center point.
11689
	 * @param $yc (float) Ordinate of center point.
11690
	 * @param $rx (float) Horizontal radius.
11691
	 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11692
	 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11693
	 * @param $angs: (float) Angle start of draw line. Default value: 0.
11694
	 * @param $angf: (float) Angle finish of draw line. Default value: 360.
11695
	 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
11696
	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11697
	 * @param $startpoint (boolean) if true output a starting point.
11698
	 * @param $ccw (boolean) if true draws in counter-clockwise.
11699
	 * @param $svg (boolean) if true the angles are in svg mode (already calculated).
11700
	 * @return array bounding box coordinates (x min, y min, x max, y max)
11701
	 * @author Nicola Asuni
11702
	 * @protected
11703
	 * @since 4.9.019 (2010-04-26)
11704
	 */
11705
	protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11706
		$k = $this->k;
11707
		if ($nc < 2) {
11708
			$nc = 2;
11709
		}
11710
		$xmin = 2147483647;
11711
		$ymin = 2147483647;
11712
		$xmax = 0;
11713
		$ymax = 0;
11714
		if ($pie) {
11715
			// center of the arc
11716
			$this->_outPoint($xc, $yc);
11717
		}
11718
		$xang = deg2rad((float) $xang);
11719
		$angs = deg2rad((float) $angs);
11720
		$angf = deg2rad((float) $angf);
11721
		if ($svg) {
11722
			$as = $angs;
11723
			$af = $angf;
11724
		} else {
11725
			$as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11726
			$af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11727
		}
11728
		if ($as < 0) {
11729
			$as += (2 * M_PI);
11730
		}
11731
		if ($af < 0) {
11732
			$af += (2 * M_PI);
11733
		}
11734
		if ($ccw AND ($as > $af)) {
11735
			// reverse rotation
11736
			$as -= (2 * M_PI);
11737
		} elseif (!$ccw AND ($as < $af)) {
11738
			// reverse rotation
11739
			$af -= (2 * M_PI);
11740
		}
11741
		$total_angle = ($af - $as);
11742
		if ($nc < 2) {
11743
			$nc = 2;
11744
		}
11745
		// total arcs to draw
11746
		$nc *= (2 * abs($total_angle) / M_PI);
11747
		$nc = round($nc) + 1;
11748
		// angle of each arc
11749
		$arcang = ($total_angle / $nc);
11750
		// center point in PDF coordinates
11751
		$x0 = $xc;
11752
		$y0 = ($this->h - $yc);
11753
		// starting angle
11754
		$ang = $as;
11755
		$alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11756
		$cos_xang = cos($xang);
11757
		$sin_xang = sin($xang);
11758
		$cos_ang = cos($ang);
11759
		$sin_ang = sin($ang);
11760
		// first arc point
11761
		$px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11762
		$py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11763
		// first Bezier control point
11764
		$qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11765
		$qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11766
		if ($pie) {
11767
			// line from center to arc starting point
11768
			$this->_outLine($px1, $this->h - $py1);
11769
		} elseif ($startpoint) {
11770
			// arc starting point
11771
			$this->_outPoint($px1, $this->h - $py1);
11772
		}
11773
		// draw arcs
11774
		for ($i = 1; $i <= $nc; ++$i) {
11775
			// starting angle
11776
			$ang = $as + ($i * $arcang);
11777
			if ($i == $nc) {
11778
				$ang = $af;
11779
			}
11780
			$cos_ang = cos($ang);
11781
			$sin_ang = sin($ang);
11782
			// second arc point
11783
			$px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11784
			$py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11785
			// second Bezier control point
11786
			$qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11787
			$qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11788
			// draw arc
11789
			$cx1 = ($px1 + $qx1);
11790
			$cy1 = ($this->h - ($py1 + $qy1));
11791
			$cx2 = ($px2 - $qx2);
11792
			$cy2 = ($this->h - ($py2 - $qy2));
11793
			$cx3 = $px2;
11794
			$cy3 = ($this->h - $py2);
11795
			$this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11796
			// get bounding box coordinates
11797
			$xmin = min($xmin, $cx1, $cx2, $cx3);
11798
			$ymin = min($ymin, $cy1, $cy2, $cy3);
11799
			$xmax = max($xmax, $cx1, $cx2, $cx3);
11800
			$ymax = max($ymax, $cy1, $cy2, $cy3);
11801
			// move to next point
11802
			$px1 = $px2;
11803
			$py1 = $py2;
11804
			$qx1 = $qx2;
11805
			$qy1 = $qy2;
11806
		}
11807
		if ($pie) {
11808
			$this->_outLine($xc, $yc);
11809
			// get bounding box coordinates
11810
			$xmin = min($xmin, $xc);
11811
			$ymin = min($ymin, $yc);
11812
			$xmax = max($xmax, $xc);
11813
			$ymax = max($ymax, $yc);
11814
		}
11815
		return array($xmin, $ymin, $xmax, $ymax);
11816
	}
11817
 
11818
	/**
11819
	 * Draws a circle.
11820
	 * A circle is formed from n Bezier curves.
11821
	 * @param $x0 (float) Abscissa of center point.
11822
	 * @param $y0 (float) Ordinate of center point.
11823
	 * @param $r (float) Radius.
11824
	 * @param $angstr: (float) Angle start of draw line. Default value: 0.
11825
	 * @param $angend: (float) Angle finish of draw line. Default value: 360.
11826
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11827
	 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11828
	 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11829
	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
11830
	 * @public
11831
	 * @since 2.1.000 (2008-01-08)
11832
	 */
11833
	public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11834
		$this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11835
	}
11836
 
11837
	/**
11838
	 * Draws a polygonal line
11839
	 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11840
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11841
	 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11842
	 * <ul>
11843
	 *	 <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11844
	 *	 <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11845
	 * </ul>
11846
	 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11847
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11848
	 * @since 4.8.003 (2009-09-15)
11849
	 * @public
11850
	 */
11851
	public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11852
		$this->Polygon($p, $style, $line_style, $fill_color, false);
11853
	}
11854
 
11855
	/**
11856
	 * Draws a polygon.
11857
	 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11858
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11859
	 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11860
	 * <ul>
11861
	 *	 <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11862
	 *	 <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11863
	 * </ul>
11864
	 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11865
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11866
	 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
11867
	 * @public
11868
	 * @since 2.1.000 (2008-01-08)
11869
	 */
11870
	public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11871
		if ($this->state != 2) {
11872
			return;
11873
		}
11874
		$nc = count($p); // number of coordinates
11875
		$np = $nc / 2; // number of points
11876
		if ($closed) {
11877
			// close polygon by adding the first 2 points at the end (one line)
11878
			for ($i = 0; $i < 4; ++$i) {
11879
				$p[$nc + $i] = $p[$i];
11880
			}
11881
			// copy style for the last added line
11882
			if (isset($line_style[0])) {
11883
				$line_style[$np] = $line_style[0];
11884
			}
11885
			$nc += 4;
11886
		}
11887
		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11888
			$this->SetFillColorArray($fill_color);
11889
		}
11890
		$op = TCPDF_STATIC::getPathPaintOperator($style);
11891
		if ($op == 'f') {
11892
			$line_style = array();
11893
		}
11894
		$draw = true;
11895
		if ($line_style) {
11896
			if (isset($line_style['all'])) {
11897
				$this->SetLineStyle($line_style['all']);
11898
			} else {
11899
				$draw = false;
11900
				if ($op == 'B') {
11901
					// draw fill
11902
					$op = 'f';
11903
					$this->_outPoint($p[0], $p[1]);
11904
					for ($i = 2; $i < $nc; $i = $i + 2) {
11905
						$this->_outLine($p[$i], $p[$i + 1]);
11906
					}
11907
					$this->_out($op);
11908
				}
11909
				// draw outline
11910
				$this->_outPoint($p[0], $p[1]);
11911
				for ($i = 2; $i < $nc; $i = $i + 2) {
11912
					$line_num = ($i / 2) - 1;
11913
					if (isset($line_style[$line_num])) {
11914
						if ($line_style[$line_num] != 0) {
11915
							if (is_array($line_style[$line_num])) {
11916
								$this->_out('S');
11917
								$this->SetLineStyle($line_style[$line_num]);
11918
								$this->_outPoint($p[$i - 2], $p[$i - 1]);
11919
								$this->_outLine($p[$i], $p[$i + 1]);
11920
								$this->_out('S');
11921
								$this->_outPoint($p[$i], $p[$i + 1]);
11922
							} else {
11923
								$this->_outLine($p[$i], $p[$i + 1]);
11924
							}
11925
						}
11926
					} else {
11927
						$this->_outLine($p[$i], $p[$i + 1]);
11928
					}
11929
				}
11930
				$this->_out($op);
11931
			}
11932
		}
11933
		if ($draw) {
11934
			$this->_outPoint($p[0], $p[1]);
11935
			for ($i = 2; $i < $nc; $i = $i + 2) {
11936
				$this->_outLine($p[$i], $p[$i + 1]);
11937
			}
11938
			$this->_out($op);
11939
		}
11940
	}
11941
 
11942
	/**
11943
	 * Draws a regular polygon.
11944
	 * @param $x0 (float) Abscissa of center point.
11945
	 * @param $y0 (float) Ordinate of center point.
11946
	 * @param $r: (float) Radius of inscribed circle.
11947
	 * @param $ns (integer) Number of sides.
11948
	 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
11949
	 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
11950
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11951
	 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
11952
	 * <ul>
11953
	 *	 <li>all: Line style of all sides. Array like for SetLineStyle().</li>
11954
	 *	 <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
11955
	 * </ul>
11956
	 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
11957
	 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11958
	 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
11959
	 * <ul>
11960
	 *	 <li>D or empty string: Draw (default).</li>
11961
	 *	 <li>F: Fill.</li>
11962
	 *	 <li>DF or FD: Draw and fill.</li>
11963
	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
11964
	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
11965
	 * </ul>
11966
	 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
11967
	 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
11968
	 * @public
11969
	 * @since 2.1.000 (2008-01-08)
11970
	 */
11971
	public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
11972
		if (3 > $ns) {
11973
			$ns = 3;
11974
		}
11975
		if ($draw_circle) {
11976
			$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11977
		}
11978
		$p = array();
11979
		for ($i = 0; $i < $ns; ++$i) {
11980
			$a = $angle + ($i * 360 / $ns);
11981
			$a_rad = deg2rad((float) $a);
11982
			$p[] = $x0 + ($r * sin($a_rad));
11983
			$p[] = $y0 + ($r * cos($a_rad));
11984
		}
11985
		$this->Polygon($p, $style, $line_style, $fill_color);
11986
	}
11987
 
11988
	/**
11989
	 * Draws a star polygon
11990
	 * @param $x0 (float) Abscissa of center point.
11991
	 * @param $y0 (float) Ordinate of center point.
11992
	 * @param $r (float) Radius of inscribed circle.
11993
	 * @param $nv (integer) Number of vertices.
11994
	 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
11995
	 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11996
	 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
11997
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11998
	 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
11999
	 * <ul>
12000
	 *	 <li>all: Line style of all sides. Array like for
12001
	 * SetLineStyle().</li>
12002
	 *	 <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12003
	 * </ul>
12004
	 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12005
	 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12006
	 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12007
	 * <ul>
12008
	 *	 <li>D or empty string: Draw (default).</li>
12009
	 *	 <li>F: Fill.</li>
12010
	 *	 <li>DF or FD: Draw and fill.</li>
12011
	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12012
	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12013
	 * </ul>
12014
	 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12015
	 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12016
	 * @public
12017
	 * @since 2.1.000 (2008-01-08)
12018
	 */
12019
	public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12020
		if ($nv < 2) {
12021
			$nv = 2;
12022
		}
12023
		if ($draw_circle) {
12024
			$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12025
		}
12026
		$p2 = array();
12027
		$visited = array();
12028
		for ($i = 0; $i < $nv; ++$i) {
12029
			$a = $angle + ($i * 360 / $nv);
12030
			$a_rad = deg2rad((float) $a);
12031
			$p2[] = $x0 + ($r * sin($a_rad));
12032
			$p2[] = $y0 + ($r * cos($a_rad));
12033
			$visited[] = false;
12034
		}
12035
		$p = array();
12036
		$i = 0;
12037
		do {
12038
			$p[] = $p2[$i * 2];
12039
			$p[] = $p2[($i * 2) + 1];
12040
			$visited[$i] = true;
12041
			$i += $ng;
12042
			$i %= $nv;
12043
		} while (!$visited[$i]);
12044
		$this->Polygon($p, $style, $line_style, $fill_color);
12045
	}
12046
 
12047
	/**
12048
	 * Draws a rounded rectangle.
12049
	 * @param $x (float) Abscissa of upper-left corner.
12050
	 * @param $y (float) Ordinate of upper-left corner.
12051
	 * @param $w (float) Width.
12052
	 * @param $h (float) Height.
12053
	 * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
12054
	 * @param $round_corner (string) Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12055
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12056
	 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12057
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12058
	 * @public
12059
	 * @since 2.1.000 (2008-01-08)
12060
	 */
12061
	public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12062
		$this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12063
	}
12064
 
12065
	/**
12066
	 * Draws a rounded rectangle.
12067
	 * @param $x (float) Abscissa of upper-left corner.
12068
	 * @param $y (float) Ordinate of upper-left corner.
12069
	 * @param $w (float) Width.
12070
	 * @param $h (float) Height.
12071
	 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
12072
	 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
12073
	 * @param $round_corner (string) Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12074
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12075
	 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12076
	 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12077
	 * @public
12078
	 * @since 4.9.019 (2010-04-22)
12079
	 */
12080
	public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12081
		if ($this->state != 2) {
12082
			return;
12083
		}
12084
		if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12085
			// Not rounded
12086
			$this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12087
			return;
12088
		}
12089
		// Rounded
12090
		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12091
			$this->SetFillColorArray($fill_color);
12092
		}
12093
		$op = TCPDF_STATIC::getPathPaintOperator($style);
12094
		if ($op == 'f') {
12095
			$border_style = array();
12096
		}
12097
		if ($border_style) {
12098
			$this->SetLineStyle($border_style);
12099
		}
12100
		$MyArc = 4 / 3 * (sqrt(2) - 1);
12101
		$this->_outPoint($x + $rx, $y);
12102
		$xc = $x + $w - $rx;
12103
		$yc = $y + $ry;
12104
		$this->_outLine($xc, $y);
12105
		if ($round_corner[0]) {
12106
			$this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12107
		} else {
12108
			$this->_outLine($x + $w, $y);
12109
		}
12110
		$xc = $x + $w - $rx;
12111
		$yc = $y + $h - $ry;
12112
		$this->_outLine($x + $w, $yc);
12113
		if ($round_corner[1]) {
12114
			$this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12115
		} else {
12116
			$this->_outLine($x + $w, $y + $h);
12117
		}
12118
		$xc = $x + $rx;
12119
		$yc = $y + $h - $ry;
12120
		$this->_outLine($xc, $y + $h);
12121
		if ($round_corner[2]) {
12122
			$this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12123
		} else {
12124
			$this->_outLine($x, $y + $h);
12125
		}
12126
		$xc = $x + $rx;
12127
		$yc = $y + $ry;
12128
		$this->_outLine($x, $yc);
12129
		if ($round_corner[3]) {
12130
			$this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12131
		} else {
12132
			$this->_outLine($x, $y);
12133
			$this->_outLine($x + $rx, $y);
12134
		}
12135
		$this->_out($op);
12136
	}
12137
 
12138
	/**
12139
	 * Draws a grahic arrow.
12140
	 * @param $x0 (float) Abscissa of first point.
12141
	 * @param $y0 (float) Ordinate of first point.
12142
	 * @param $x1 (float) Abscissa of second point.
12143
	 * @param $y1 (float) Ordinate of second point.
12144
	 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12145
	 * @param $arm_size (float) length of arrowhead arms
12146
	 * @param $arm_angle (int) angle between an arm and the shaft
12147
	 * @author Piotr Galecki, Nicola Asuni, Andy Meier
12148
	 * @since 4.6.018 (2009-07-10)
12149
	 */
12150
	public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12151
		// getting arrow direction angle
12152
		// 0 deg angle is when both arms go along X axis. angle grows clockwise.
12153
		$dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12154
		if ($dir_angle < 0) {
12155
			$dir_angle += (2 * M_PI);
12156
		}
12157
		$arm_angle = deg2rad($arm_angle);
12158
		$sx1 = $x1;
12159
		$sy1 = $y1;
12160
		if ($head_style > 0) {
12161
			// calculate the stopping point for the arrow shaft
12162
			$sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12163
			$sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12164
		}
12165
		// main arrow line / shaft
12166
		$this->Line($x0, $y0, $sx1, $sy1);
12167
		// left arrowhead arm tip
12168
		$x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12169
		$y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12170
		// right arrowhead arm tip
12171
		$x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12172
		$y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12173
		$mode = 'D';
12174
		$style = array();
12175
		switch ($head_style) {
12176
			case 0: {
12177
				// draw only arrowhead arms
12178
				$mode = 'D';
12179
				$style = array(1, 1, 0);
12180
				break;
12181
			}
12182
			case 1: {
12183
				// draw closed arrowhead, but no fill
12184
				$mode = 'D';
12185
				break;
12186
			}
12187
			case 2: {
12188
				// closed and filled arrowhead
12189
				$mode = 'DF';
12190
				break;
12191
			}
12192
			case 3: {
12193
				// filled arrowhead
12194
				$mode = 'F';
12195
				break;
12196
			}
12197
		}
12198
		$this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12199
	}
12200
 
12201
	// END GRAPHIC FUNCTIONS SECTION -----------------------
12202
 
12203
	/**
12204
	 * Add a Named Destination.
12205
	 * NOTE: destination names are unique, so only last entry will be saved.
12206
	 * @param $name (string) Destination name.
12207
	 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12208
	 * @param $page (int) Target page number (leave empty for current page).
12209
	 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;).
12210
	 * @return (string) Stripped named destination identifier or false in case of error.
12211
	 * @public
12212
	 * @author Christian Deligant, Nicola Asuni
12213
	 * @since 5.9.097 (2011-06-23)
12214
	 */
12215
	public function setDestination($name, $y=-1, $page='', $x=-1) {
12216
		// remove unsupported characters
12217
		$name = TCPDF_STATIC::encodeNameObject($name);
12218
		if (TCPDF_STATIC::empty_string($name)) {
12219
			return false;
12220
		}
12221
		if ($y == -1) {
12222
			$y = $this->GetY();
12223
		} elseif ($y < 0) {
12224
			$y = 0;
12225
		} elseif ($y > $this->h) {
12226
			$y = $this->h;
12227
		}
12228
		if ($x == -1) {
12229
			$x = $this->GetX();
12230
		} elseif ($x < 0) {
12231
			$x = 0;
12232
		} elseif ($x > $this->w) {
12233
			$x = $this->w;
12234
		}
12235
		if (empty($page)) {
12236
			$page = $this->PageNo();
12237
			if (empty($page)) {
12238
				return;
12239
			}
12240
		}
12241
		$this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page);
12242
		return $name;
12243
	}
12244
 
12245
	/**
12246
	 * Return the Named Destination array.
12247
	 * @return (array) Named Destination array.
12248
	 * @public
12249
	 * @author Nicola Asuni
12250
	 * @since 5.9.097 (2011-06-23)
12251
	 */
12252
	public function getDestination() {
12253
		return $this->dests;
12254
	}
12255
 
12256
	/**
12257
	 * Insert Named Destinations.
12258
	 * @protected
12259
	 * @author Johannes Güntert, Nicola Asuni
12260
	 * @since 5.9.098 (2011-06-23)
12261
	 */
12262
	protected function _putdests() {
12263
		if (empty($this->dests)) {
12264
			return;
12265
		}
12266
		$this->n_dests = $this->_newobj();
12267
		$out = ' <<';
12268
		foreach($this->dests as $name => $o) {
12269
			$out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12270
		}
12271
		$out .= ' >>';
12272
		$out .= "\n".'endobj';
12273
		$this->_out($out);
12274
	}
12275
 
12276
	/**
12277
	 * Adds a bookmark - alias for Bookmark().
12278
	 * @param $txt (string) Bookmark description.
12279
	 * @param $level (int) Bookmark level (minimum value is 0).
12280
	 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12281
	 * @param $page (int) Target page number (leave empty for current page).
12282
	 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12283
	 * @param $color (array) RGB color array (values from 0 to 255).
12284
	 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12285
	 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12286
	 * @public
12287
	 */
12288
	public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12289
		$this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12290
	}
12291
 
12292
	/**
12293
	 * Adds a bookmark.
12294
	 * @param $txt (string) Bookmark description.
12295
	 * @param $level (int) Bookmark level (minimum value is 0).
12296
	 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12297
	 * @param $page (int) Target page number (leave empty for current page).
12298
	 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12299
	 * @param $color (array) RGB color array (values from 0 to 255).
12300
	 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12301
	 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12302
	 * @public
12303
	 * @since 2.1.002 (2008-02-12)
12304
	 */
12305
	public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12306
		if ($level < 0) {
12307
			$level = 0;
12308
		}
12309
		if (isset($this->outlines[0])) {
12310
			$lastoutline = end($this->outlines);
12311
			$maxlevel = $lastoutline['l'] + 1;
12312
		} else {
12313
			$maxlevel = 0;
12314
		}
12315
		if ($level > $maxlevel) {
12316
			$level = $maxlevel;
12317
		}
12318
		if ($y == -1) {
12319
			$y = $this->GetY();
12320
		} elseif ($y < 0) {
12321
			$y = 0;
12322
		} elseif ($y > $this->h) {
12323
			$y = $this->h;
12324
		}
12325
		if ($x == -1) {
12326
			$x = $this->GetX();
12327
		} elseif ($x < 0) {
12328
			$x = 0;
12329
		} elseif ($x > $this->w) {
12330
			$x = $this->w;
12331
		}
12332
		if (empty($page)) {
12333
			$page = $this->PageNo();
12334
			if (empty($page)) {
12335
				return;
12336
			}
12337
		}
12338
		$this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12339
	}
12340
 
12341
	/**
12342
	 * Sort bookmarks for page and key.
12343
	 * @protected
12344
	 * @since 5.9.119 (2011-09-19)
12345
	 */
12346
	protected function sortBookmarks() {
12347
		// get sorting columns
12348
		$outline_p = array();
12349
		$outline_y = array();
12350
		foreach ($this->outlines as $key => $row) {
12351
			$outline_p[$key] = $row['p'];
12352
			$outline_k[$key] = $key;
12353
		}
12354
		// sort outlines by page and original position
12355
		array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12356
	}
12357
 
12358
	/**
12359
	 * Create a bookmark PDF string.
12360
	 * @protected
12361
	 * @author Olivier Plathey, Nicola Asuni
12362
	 * @since 2.1.002 (2008-02-12)
12363
	 */
12364
	protected function _putbookmarks() {
12365
		$nb = count($this->outlines);
12366
		if ($nb == 0) {
12367
			return;
12368
		}
12369
		// sort bookmarks
12370
		$this->sortBookmarks();
12371
		$lru = array();
12372
		$level = 0;
12373
		foreach ($this->outlines as $i => $o) {
12374
			if ($o['l'] > 0) {
12375
				$parent = $lru[($o['l'] - 1)];
12376
				//Set parent and last pointers
12377
				$this->outlines[$i]['parent'] = $parent;
12378
				$this->outlines[$parent]['last'] = $i;
12379
				if ($o['l'] > $level) {
12380
					//Level increasing: set first pointer
12381
					$this->outlines[$parent]['first'] = $i;
12382
				}
12383
			} else {
12384
				$this->outlines[$i]['parent'] = $nb;
12385
			}
12386
			if (($o['l'] <= $level) AND ($i > 0)) {
12387
				//Set prev and next pointers
12388
				$prev = $lru[$o['l']];
12389
				$this->outlines[$prev]['next'] = $i;
12390
				$this->outlines[$i]['prev'] = $prev;
12391
			}
12392
			$lru[$o['l']] = $i;
12393
			$level = $o['l'];
12394
		}
12395
		//Outline items
12396
		$n = $this->n + 1;
12397
		$nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si';
12398
		foreach ($this->outlines as $i => $o) {
12399
			$oid = $this->_newobj();
12400
			// covert HTML title to string
12401
			$title = preg_replace($nltags, "\n", $o['t']);
12402
			$title = preg_replace("/[\r]+/si", '', $title);
12403
			$title = preg_replace("/[\n]+/si", "\n", $title);
12404
			$title = strip_tags($title);
12405
			$title = $this->stringTrim($title);
12406
			$out = '<</Title '.$this->_textstring($title, $oid);
12407
			$out .= ' /Parent '.($n + $o['parent']).' 0 R';
12408
			if (isset($o['prev'])) {
12409
				$out .= ' /Prev '.($n + $o['prev']).' 0 R';
12410
			}
12411
			if (isset($o['next'])) {
12412
				$out .= ' /Next '.($n + $o['next']).' 0 R';
12413
			}
12414
			if (isset($o['first'])) {
12415
				$out .= ' /First '.($n + $o['first']).' 0 R';
12416
			}
12417
			if (isset($o['last'])) {
12418
				$out .= ' /Last '.($n + $o['last']).' 0 R';
12419
			}
12420
			if (isset($o['u']) AND !empty($o['u'])) {
12421
				// link
12422
				if (is_string($o['u'])) {
12423
					if ($o['u'][0] == '#') {
12424
						// internal destination
12425
						$out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12426
					} elseif ($o['u'][0] == '%') {
12427
						// embedded PDF file
12428
						$filename = basename(substr($o['u'], 1));
12429
						$out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12430
					} elseif ($o['u'][0] == '*') {
12431
						// embedded generic file
12432
						$filename = basename(substr($o['u'], 1));
12433
						$jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
12434
						$out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12435
					} else {
12436
						// external URI link
12437
						$out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12438
					}
12439
				} elseif (isset($this->links[$o['u']])) {
12440
					// internal link ID
12441
					$l = $this->links[$o['u']];
12442
					if (isset($this->page_obj_id[($l[0])])) {
12443
						$out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
12444
					}
12445
				}
12446
			} elseif (isset($this->page_obj_id[($o['p'])])) {
12447
				// link to a page
12448
				$out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12449
			}
12450
			// set font style
12451
			$style = 0;
12452
			if (!empty($o['s'])) {
12453
				// bold
12454
				if (strpos($o['s'], 'B') !== false) {
12455
					$style |= 2;
12456
				}
12457
				// oblique
12458
				if (strpos($o['s'], 'I') !== false) {
12459
					$style |= 1;
12460
				}
12461
			}
12462
			$out .= sprintf(' /F %d', $style);
12463
			// set bookmark color
12464
			if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12465
				$color = array_values($o['c']);
12466
				$out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12467
			} else {
12468
				// black
12469
				$out .= ' /C [0.0 0.0 0.0]';
12470
			}
12471
			$out .= ' /Count 0'; // normally closed item
12472
			$out .= ' >>';
12473
			$out .= "\n".'endobj';
12474
			$this->_out($out);
12475
		}
12476
		//Outline root
12477
		$this->OutlineRoot = $this->_newobj();
12478
		$this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12479
	}
12480
 
12481
	// --- JAVASCRIPT ------------------------------------------------------
12482
 
12483
	/**
12484
	 * Adds a javascript
12485
	 * @param $script (string) Javascript code
12486
	 * @public
12487
	 * @author Johannes Güntert, Nicola Asuni
12488
	 * @since 2.1.002 (2008-02-12)
12489
	 */
12490
	public function IncludeJS($script) {
12491
		$this->javascript .= $script;
12492
	}
12493
 
12494
	/**
12495
	 * Adds a javascript object and return object ID
12496
	 * @param $script (string) Javascript code
12497
	 * @param $onload (boolean) if true executes this object when opening the document
12498
	 * @return int internal object ID
12499
	 * @public
12500
	 * @author Nicola Asuni
12501
	 * @since 4.8.000 (2009-09-07)
12502
	 */
12503
	public function addJavascriptObject($script, $onload=false) {
12504
		if ($this->pdfa_mode) {
12505
			// javascript is not allowed in PDF/A mode
12506
			return false;
12507
		}
12508
		++$this->n;
12509
		$this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12510
		return $this->n;
12511
	}
12512
 
12513
	/**
12514
	 * Create a javascript PDF string.
12515
	 * @protected
12516
	 * @author Johannes Güntert, Nicola Asuni
12517
	 * @since 2.1.002 (2008-02-12)
12518
	 */
12519
	protected function _putjavascript() {
12520
		if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12521
			return;
12522
		}
12523
		if (strpos($this->javascript, 'this.addField') > 0) {
12524
			if (!$this->ur['enabled']) {
12525
				//$this->setUserRights();
12526
			}
12527
			// the following two lines are used to avoid form fields duplication after saving
12528
			// The addField method only works when releasing user rights (UR3)
12529
			$jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12530
			$jsb = "getField('tcpdfdocsaved').value='saved';";
12531
			$this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12532
		}
12533
		// name tree for javascript
12534
		$this->n_js = '<< /Names [';
12535
		if (!empty($this->javascript)) {
12536
			$this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12537
		}
12538
		if (!empty($this->js_objects)) {
12539
			foreach ($this->js_objects as $key => $val) {
12540
				if ($val['onload']) {
12541
					$this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12542
				}
12543
			}
12544
		}
12545
		$this->n_js .= ' ] >>';
12546
		// default Javascript object
12547
		if (!empty($this->javascript)) {
12548
			$obj_id = $this->_newobj();
12549
			$out = '<< /S /JavaScript';
12550
			$out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12551
			$out .= ' >>';
12552
			$out .= "\n".'endobj';
12553
			$this->_out($out);
12554
		}
12555
		// additional Javascript objects
12556
		if (!empty($this->js_objects)) {
12557
			foreach ($this->js_objects as $key => $val) {
12558
				$out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12559
				$this->_out($out);
12560
			}
12561
		}
12562
	}
12563
 
12564
	/**
12565
	 * Adds a javascript form field.
12566
	 * @param $type (string) field type
12567
	 * @param $name (string) field name
12568
	 * @param $x (int) horizontal position
12569
	 * @param $y (int) vertical position
12570
	 * @param $w (int) width
12571
	 * @param $h (int) height
12572
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12573
	 * @protected
12574
	 * @author Denis Van Nuffelen, Nicola Asuni
12575
	 * @since 2.1.002 (2008-02-12)
12576
	 */
12577
	protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12578
		if ($this->rtl) {
12579
			$x = $x - $w;
12580
		}
12581
		// the followind avoid fields duplication after saving the document
12582
		$this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12583
		$k = $this->k;
12584
		$this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
12585
		$this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12586
		while (list($key, $val) = each($prop)) {
12587
			if (strcmp(substr($key, -5), 'Color') == 0) {
12588
				$val = TCPDF_COLORS::_JScolor($val);
12589
			} else {
12590
				$val = "'".$val."'";
12591
			}
12592
			$this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12593
		}
12594
		if ($this->rtl) {
12595
			$this->x -= $w;
12596
		} else {
12597
			$this->x += $w;
12598
		}
12599
		$this->javascript .= '}';
12600
	}
12601
 
12602
	// --- FORM FIELDS -----------------------------------------------------
12603
 
12604
 
12605
 
12606
	/**
12607
	 * Set default properties for form fields.
12608
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12609
	 * @public
12610
	 * @author Nicola Asuni
12611
	 * @since 4.8.000 (2009-09-06)
12612
	 */
12613
	public function setFormDefaultProp($prop=array()) {
12614
		$this->default_form_prop = $prop;
12615
	}
12616
 
12617
	/**
12618
	 * Return the default properties for form fields.
12619
	 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12620
	 * @public
12621
	 * @author Nicola Asuni
12622
	 * @since 4.8.000 (2009-09-06)
12623
	 */
12624
	public function getFormDefaultProp() {
12625
		return $this->default_form_prop;
12626
	}
12627
 
12628
	/**
12629
	 * Creates a text field
12630
	 * @param $name (string) field name
12631
	 * @param $w (float) Width of the rectangle
12632
	 * @param $h (float) Height of the rectangle
12633
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12634
	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12635
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12636
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12637
	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12638
	 * @public
12639
	 * @author Nicola Asuni
12640
	 * @since 4.8.000 (2009-09-07)
12641
	 */
12642
	public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12643
		if ($x === '') {
12644
			$x = $this->x;
12645
		}
12646
		if ($y === '') {
12647
			$y = $this->y;
12648
		}
12649
		// check page for no-write regions and adapt page margins if necessary
12650
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12651
		if ($js) {
12652
			$this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12653
			return;
12654
		}
12655
		// get default style
12656
		$prop = array_merge($this->getFormDefaultProp(), $prop);
12657
		// get annotation data
12658
		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12659
		// set default appearance stream
12660
		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12661
		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12662
		$popt['da'] = $fontstyle;
12663
		// build appearance stream
12664
		$popt['ap'] = array();
12665
		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12666
		$text = '';
12667
		if (isset($prop['value']) AND !empty($prop['value'])) {
12668
			$text = $prop['value'];
12669
		} elseif (isset($opt['v']) AND !empty($opt['v'])) {
12670
			$text = $opt['v'];
12671
		}
12672
		$tmpid = $this->startTemplate($w, $h, false);
12673
		$align = '';
12674
		if (isset($popt['q'])) {
12675
			switch ($popt['q']) {
12676
				case 0: {
12677
					$align = 'L';
12678
					break;
12679
				}
12680
				case 1: {
12681
					$align = 'C';
12682
					break;
12683
				}
12684
				case 2: {
12685
					$align = 'R';
12686
					break;
12687
				}
12688
				default: {
12689
					$align = '';
12690
					break;
12691
				}
12692
			}
12693
		}
12694
		$this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12695
		$this->endTemplate();
12696
		--$this->n;
12697
		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12698
		unset($this->xobjects[$tmpid]);
12699
		$popt['ap']['n'] .= 'Q EMC';
12700
		// merge options
12701
		$opt = array_merge($popt, $opt);
12702
		// remove some conflicting options
12703
		unset($opt['bs']);
12704
		// set remaining annotation data
12705
		$opt['Subtype'] = 'Widget';
12706
		$opt['ft'] = 'Tx';
12707
		$opt['t'] = $name;
12708
		// Additional annotation's parameters (check _putannotsobj() method):
12709
		//$opt['f']
12710
		//$opt['as']
12711
		//$opt['bs']
12712
		//$opt['be']
12713
		//$opt['c']
12714
		//$opt['border']
12715
		//$opt['h']
12716
		//$opt['mk'];
12717
		//$opt['mk']['r']
12718
		//$opt['mk']['bc'];
12719
		//$opt['mk']['bg'];
12720
		unset($opt['mk']['ca']);
12721
		unset($opt['mk']['rc']);
12722
		unset($opt['mk']['ac']);
12723
		unset($opt['mk']['i']);
12724
		unset($opt['mk']['ri']);
12725
		unset($opt['mk']['ix']);
12726
		unset($opt['mk']['if']);
12727
		//$opt['mk']['if']['sw'];
12728
		//$opt['mk']['if']['s'];
12729
		//$opt['mk']['if']['a'];
12730
		//$opt['mk']['if']['fb'];
12731
		unset($opt['mk']['tp']);
12732
		//$opt['tu']
12733
		//$opt['tm']
12734
		//$opt['ff']
12735
		//$opt['v']
12736
		//$opt['dv']
12737
		//$opt['a']
12738
		//$opt['aa']
12739
		//$opt['q']
12740
		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12741
		if ($this->rtl) {
12742
			$this->x -= $w;
12743
		} else {
12744
			$this->x += $w;
12745
		}
12746
	}
12747
 
12748
	/**
12749
	 * Creates a RadioButton field.
12750
	 * @param $name (string) Field name.
12751
	 * @param $w (int) Width of the radio button.
12752
	 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12753
	 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12754
	 * @param $onvalue (string) Value to be returned if selected.
12755
	 * @param $checked (boolean) Define the initial state.
12756
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12757
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12758
	 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12759
	 * @public
12760
	 * @author Nicola Asuni
12761
	 * @since 4.8.000 (2009-09-07)
12762
	 */
12763
	public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12764
		if ($x === '') {
12765
			$x = $this->x;
12766
		}
12767
		if ($y === '') {
12768
			$y = $this->y;
12769
		}
12770
		// check page for no-write regions and adapt page margins if necessary
12771
		list($x, $y) = $this->checkPageRegions($w, $x, $y);
12772
		if ($js) {
12773
			$this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12774
			return;
12775
		}
12776
		if (TCPDF_STATIC::empty_string($onvalue)) {
12777
			$onvalue = 'On';
12778
		}
12779
		if ($checked) {
12780
			$defval = $onvalue;
12781
		} else {
12782
			$defval = 'Off';
12783
		}
12784
		// set font
12785
		$font = 'zapfdingbats';
12786
		if ($this->pdfa_mode) {
12787
			// all fonts must be embedded
12788
			$font = 'pdfa'.$font;
12789
		}
12790
		$this->AddFont($font);
12791
		$tmpfont = $this->getFontBuffer($font);
12792
		// set data for parent group
12793
		if (!isset($this->radiobutton_groups[$this->page])) {
12794
			$this->radiobutton_groups[$this->page] = array();
12795
		}
12796
		if (!isset($this->radiobutton_groups[$this->page][$name])) {
12797
			$this->radiobutton_groups[$this->page][$name] = array();
12798
			++$this->n;
12799
			$this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12800
			$this->radio_groups[] = $this->n;
12801
		}
12802
		$kid = ($this->n + 1);
12803
		// save object ID to be added on Kids entry on parent object
12804
		$this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12805
		// get default style
12806
		$prop = array_merge($this->getFormDefaultProp(), $prop);
12807
		$prop['NoToggleToOff'] = 'true';
12808
		$prop['Radio'] = 'true';
12809
		$prop['borderStyle'] = 'inset';
12810
		// get annotation data
12811
		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12812
		// set additional default options
12813
		$this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12814
		$fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12815
		$popt['da'] = $fontstyle;
12816
		// build appearance stream
12817
		$popt['ap'] = array();
12818
		$popt['ap']['n'] = array();
12819
		$fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12820
		$fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12821
		$popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12822
		$popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12823
		if (!isset($popt['mk'])) {
12824
			$popt['mk'] = array();
12825
		}
12826
		$popt['mk']['ca'] = '(l)';
12827
		// merge options
12828
		$opt = array_merge($popt, $opt);
12829
		// set remaining annotation data
12830
		$opt['Subtype'] = 'Widget';
12831
		$opt['ft'] = 'Btn';
12832
		if ($checked) {
12833
			$opt['v'] = array('/'.$onvalue);
12834
			$opt['as'] = $onvalue;
12835
		} else {
12836
			$opt['as'] = 'Off';
12837
		}
12838
		// store readonly flag
12839
		if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12840
			$this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12841
		}
12842
		$this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12843
		$this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12844
		if ($this->rtl) {
12845
			$this->x -= $w;
12846
		} else {
12847
			$this->x += $w;
12848
		}
12849
	}
12850
 
12851
	/**
12852
	 * Creates a List-box field
12853
	 * @param $name (string) field name
12854
	 * @param $w (int) width
12855
	 * @param $h (int) height
12856
	 * @param $values (array) array containing the list of values.
12857
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12858
	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12859
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12860
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12861
	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12862
	 * @public
12863
	 * @author Nicola Asuni
12864
	 * @since 4.8.000 (2009-09-07)
12865
	 */
12866
	public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12867
		if ($x === '') {
12868
			$x = $this->x;
12869
		}
12870
		if ($y === '') {
12871
			$y = $this->y;
12872
		}
12873
		// check page for no-write regions and adapt page margins if necessary
12874
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12875
		if ($js) {
12876
			$this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12877
			$s = '';
12878
			foreach ($values as $value) {
12879
				if (is_array($value)) {
12880
					$s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12881
				} else {
12882
					$s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12883
				}
12884
			}
12885
			$this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12886
			return;
12887
		}
12888
		// get default style
12889
		$prop = array_merge($this->getFormDefaultProp(), $prop);
12890
		// get annotation data
12891
		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12892
		// set additional default values
12893
		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12894
		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12895
		$popt['da'] = $fontstyle;
12896
		// build appearance stream
12897
		$popt['ap'] = array();
12898
		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12899
		$text = '';
12900
		foreach($values as $item) {
12901
			if (is_array($item)) {
12902
				$text .= $item[1]."\n";
12903
			} else {
12904
				$text .= $item."\n";
12905
			}
12906
		}
12907
		$tmpid = $this->startTemplate($w, $h, false);
12908
		$this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12909
		$this->endTemplate();
12910
		--$this->n;
12911
		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12912
		unset($this->xobjects[$tmpid]);
12913
		$popt['ap']['n'] .= 'Q EMC';
12914
		// merge options
12915
		$opt = array_merge($popt, $opt);
12916
		// set remaining annotation data
12917
		$opt['Subtype'] = 'Widget';
12918
		$opt['ft'] = 'Ch';
12919
		$opt['t'] = $name;
12920
		$opt['opt'] = $values;
12921
		unset($opt['mk']['ca']);
12922
		unset($opt['mk']['rc']);
12923
		unset($opt['mk']['ac']);
12924
		unset($opt['mk']['i']);
12925
		unset($opt['mk']['ri']);
12926
		unset($opt['mk']['ix']);
12927
		unset($opt['mk']['if']);
12928
		unset($opt['mk']['tp']);
12929
		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12930
		if ($this->rtl) {
12931
			$this->x -= $w;
12932
		} else {
12933
			$this->x += $w;
12934
		}
12935
	}
12936
 
12937
	/**
12938
	 * Creates a Combo-box field
12939
	 * @param $name (string) field name
12940
	 * @param $w (int) width
12941
	 * @param $h (int) height
12942
	 * @param $values (array) array containing the list of values.
12943
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12944
	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12945
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12946
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12947
	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12948
	 * @public
12949
	 * @author Nicola Asuni
12950
	 * @since 4.8.000 (2009-09-07)
12951
	 */
12952
	public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12953
		if ($x === '') {
12954
			$x = $this->x;
12955
		}
12956
		if ($y === '') {
12957
			$y = $this->y;
12958
		}
12959
		// check page for no-write regions and adapt page margins if necessary
12960
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12961
		if ($js) {
12962
			$this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12963
			$s = '';
12964
			foreach ($values as $value) {
12965
				if (is_array($value)) {
12966
					$s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12967
				} else {
12968
					$s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12969
				}
12970
			}
12971
			$this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12972
			return;
12973
		}
12974
		// get default style
12975
		$prop = array_merge($this->getFormDefaultProp(), $prop);
12976
		$prop['Combo'] = true;
12977
		// get annotation data
12978
		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12979
		// set additional default options
12980
		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12981
		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12982
		$popt['da'] = $fontstyle;
12983
		// build appearance stream
12984
		$popt['ap'] = array();
12985
		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12986
		$text = '';
12987
		foreach($values as $item) {
12988
			if (is_array($item)) {
12989
				$text .= $item[1]."\n";
12990
			} else {
12991
				$text .= $item."\n";
12992
			}
12993
		}
12994
		$tmpid = $this->startTemplate($w, $h, false);
12995
		$this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12996
		$this->endTemplate();
12997
		--$this->n;
12998
		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12999
		unset($this->xobjects[$tmpid]);
13000
		$popt['ap']['n'] .= 'Q EMC';
13001
		// merge options
13002
		$opt = array_merge($popt, $opt);
13003
		// set remaining annotation data
13004
		$opt['Subtype'] = 'Widget';
13005
		$opt['ft'] = 'Ch';
13006
		$opt['t'] = $name;
13007
		$opt['opt'] = $values;
13008
		unset($opt['mk']['ca']);
13009
		unset($opt['mk']['rc']);
13010
		unset($opt['mk']['ac']);
13011
		unset($opt['mk']['i']);
13012
		unset($opt['mk']['ri']);
13013
		unset($opt['mk']['ix']);
13014
		unset($opt['mk']['if']);
13015
		unset($opt['mk']['tp']);
13016
		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13017
		if ($this->rtl) {
13018
			$this->x -= $w;
13019
		} else {
13020
			$this->x += $w;
13021
		}
13022
	}
13023
 
13024
	/**
13025
	 * Creates a CheckBox field
13026
	 * @param $name (string) field name
13027
	 * @param $w (int) width
13028
	 * @param $checked (boolean) define the initial state.
13029
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13030
	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13031
	 * @param $onvalue (string) value to be returned if selected.
13032
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13033
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13034
	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13035
	 * @public
13036
	 * @author Nicola Asuni
13037
	 * @since 4.8.000 (2009-09-07)
13038
	 */
13039
	public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13040
		if ($x === '') {
13041
			$x = $this->x;
13042
		}
13043
		if ($y === '') {
13044
			$y = $this->y;
13045
		}
13046
		// check page for no-write regions and adapt page margins if necessary
13047
		list($x, $y) = $this->checkPageRegions($w, $x, $y);
13048
		if ($js) {
13049
			$this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13050
			return;
13051
		}
13052
		if (!isset($prop['value'])) {
13053
			$prop['value'] = array('Yes');
13054
		}
13055
		// get default style
13056
		$prop = array_merge($this->getFormDefaultProp(), $prop);
13057
		$prop['borderStyle'] = 'inset';
13058
		// get annotation data
13059
		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13060
		// set additional default options
13061
		$font = 'zapfdingbats';
13062
		if ($this->pdfa_mode) {
13063
			// all fonts must be embedded
13064
			$font = 'pdfa'.$font;
13065
		}
13066
		$this->AddFont($font);
13067
		$tmpfont = $this->getFontBuffer($font);
13068
		$this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13069
		$fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13070
		$popt['da'] = $fontstyle;
13071
		// build appearance stream
13072
		$popt['ap'] = array();
13073
		$popt['ap']['n'] = array();
13074
		$fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13075
		$fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13076
		$popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13077
		$popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13078
		// merge options
13079
		$opt = array_merge($popt, $opt);
13080
		// set remaining annotation data
13081
		$opt['Subtype'] = 'Widget';
13082
		$opt['ft'] = 'Btn';
13083
		$opt['t'] = $name;
13084
		if (TCPDF_STATIC::empty_string($onvalue)) {
13085
			$onvalue = 'Yes';
13086
		}
13087
		$opt['opt'] = array($onvalue);
13088
		if ($checked) {
13089
			$opt['v'] = array('/Yes');
13090
			$opt['as'] = 'Yes';
13091
		} else {
13092
			$opt['v'] = array('/Off');
13093
			$opt['as'] = 'Off';
13094
		}
13095
		$this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13096
		if ($this->rtl) {
13097
			$this->x -= $w;
13098
		} else {
13099
			$this->x += $w;
13100
		}
13101
	}
13102
 
13103
	/**
13104
	 * Creates a button field
13105
	 * @param $name (string) field name
13106
	 * @param $w (int) width
13107
	 * @param $h (int) height
13108
	 * @param $caption (string) caption.
13109
	 * @param $action (mixed) action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
13110
	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13111
	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13112
	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13113
	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13114
	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13115
	 * @public
13116
	 * @author Nicola Asuni
13117
	 * @since 4.8.000 (2009-09-07)
13118
	 */
13119
	public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13120
		if ($x === '') {
13121
			$x = $this->x;
13122
		}
13123
		if ($y === '') {
13124
			$y = $this->y;
13125
		}
13126
		// check page for no-write regions and adapt page margins if necessary
13127
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
13128
		if ($js) {
13129
			$this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13130
			$this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13131
			$this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13132
			$this->javascript .= 'f'.$name.".highlight='push';\n";
13133
			$this->javascript .= 'f'.$name.".print=false;\n";
13134
			return;
13135
		}
13136
		// get default style
13137
		$prop = array_merge($this->getFormDefaultProp(), $prop);
13138
		$prop['Pushbutton'] = 'true';
13139
		$prop['highlight'] = 'push';
13140
		$prop['display'] = 'display.noPrint';
13141
		// get annotation data
13142
		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13143
		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13144
		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13145
		$popt['da'] = $fontstyle;
13146
		// build appearance stream
13147
		$popt['ap'] = array();
13148
		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13149
		$tmpid = $this->startTemplate($w, $h, false);
13150
		$bw = (2 / $this->k); // border width
13151
		$border = array(
13152
			'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13153
			'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13154
			'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13155
			'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13156
		$this->SetFillColor(204);
13157
		$this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13158
		$this->endTemplate();
13159
		--$this->n;
13160
		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13161
		unset($this->xobjects[$tmpid]);
13162
		$popt['ap']['n'] .= 'Q EMC';
13163
		// set additional default options
13164
		if (!isset($popt['mk'])) {
13165
			$popt['mk'] = array();
13166
		}
13167
		$ann_obj_id = ($this->n + 1);
13168
		if (!empty($action) AND !is_array($action)) {
13169
			$ann_obj_id = ($this->n + 2);
13170
		}
13171
		$popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13172
		$popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13173
		$popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13174
		// merge options
13175
		$opt = array_merge($popt, $opt);
13176
		// set remaining annotation data
13177
		$opt['Subtype'] = 'Widget';
13178
		$opt['ft'] = 'Btn';
13179
		$opt['t'] = $caption;
13180
		$opt['v'] = $name;
13181
		if (!empty($action)) {
13182
			if (is_array($action)) {
13183
				// form action options as on section 12.7.5 of PDF32000_2008.
13184
				$opt['aa'] = '/D <<';
13185
				$bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13186
				foreach ($action AS $key => $val) {
13187
					if (($key == 'S') AND in_array($val, $bmode)) {
13188
						$opt['aa'] .= ' /S /'.$val;
13189
					} elseif (($key == 'F') AND (!empty($val))) {
13190
						$opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13191
					} elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13192
						$opt['aa'] .= ' /Fields [';
13193
						foreach ($val AS $field) {
13194
							$opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13195
						}
13196
						$opt['aa'] .= ']';
13197
					} elseif (($key == 'Flags')) {
13198
						$ff = 0;
13199
						if (is_array($val)) {
13200
							foreach ($val AS $flag) {
13201
								switch ($flag) {
13202
									case 'Include/Exclude': {
13203
										$ff += 1 << 0;
13204
										break;
13205
									}
13206
									case 'IncludeNoValueFields': {
13207
										$ff += 1 << 1;
13208
										break;
13209
									}
13210
									case 'ExportFormat': {
13211
										$ff += 1 << 2;
13212
										break;
13213
									}
13214
									case 'GetMethod': {
13215
										$ff += 1 << 3;
13216
										break;
13217
									}
13218
									case 'SubmitCoordinates': {
13219
										$ff += 1 << 4;
13220
										break;
13221
									}
13222
									case 'XFDF': {
13223
										$ff += 1 << 5;
13224
										break;
13225
									}
13226
									case 'IncludeAppendSaves': {
13227
										$ff += 1 << 6;
13228
										break;
13229
									}
13230
									case 'IncludeAnnotations': {
13231
										$ff += 1 << 7;
13232
										break;
13233
									}
13234
									case 'SubmitPDF': {
13235
										$ff += 1 << 8;
13236
										break;
13237
									}
13238
									case 'CanonicalFormat': {
13239
										$ff += 1 << 9;
13240
										break;
13241
									}
13242
									case 'ExclNonUserAnnots': {
13243
										$ff += 1 << 10;
13244
										break;
13245
									}
13246
									case 'ExclFKey': {
13247
										$ff += 1 << 11;
13248
										break;
13249
									}
13250
									case 'EmbedForm': {
13251
										$ff += 1 << 13;
13252
										break;
13253
									}
13254
								}
13255
							}
13256
						} else {
13257
							$ff = intval($val);
13258
						}
13259
						$opt['aa'] .= ' /Flags '.$ff;
13260
					}
13261
				}
13262
				$opt['aa'] .= ' >>';
13263
			} else {
13264
				// Javascript action or raw action command
13265
				$js_obj_id = $this->addJavascriptObject($action);
13266
				$opt['aa'] = '/D '.$js_obj_id.' 0 R';
13267
			}
13268
		}
13269
		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13270
		if ($this->rtl) {
13271
			$this->x -= $w;
13272
		} else {
13273
			$this->x += $w;
13274
		}
13275
	}
13276
 
13277
	// --- END FORMS FIELDS ------------------------------------------------
13278
 
13279
	/**
13280
	 * Add certification signature (DocMDP or UR3)
13281
	 * You can set only one signature type
13282
	 * @protected
13283
	 * @author Nicola Asuni
13284
	 * @since 4.6.008 (2009-05-07)
13285
	 */
13286
	protected function _putsignature() {
13287
		if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13288
			return;
13289
		}
13290
		$sigobjid = ($this->sig_obj_id + 1);
13291
		$out = $this->_getobj($sigobjid)."\n";
13292
		$out .= '<< /Type /Sig';
13293
		$out .= ' /Filter /Adobe.PPKLite';
13294
		$out .= ' /SubFilter /adbe.pkcs7.detached';
13295
		$out .= ' '.TCPDF_STATIC::$byterange_string;
13296
		$out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13297
		$out .= ' /Reference ['; // array of signature reference dictionaries
13298
		$out .= ' << /Type /SigRef';
13299
		if ($this->signature_data['cert_type'] > 0) {
13300
			$out .= ' /TransformMethod /DocMDP';
13301
			$out .= ' /TransformParams <<';
13302
			$out .= ' /Type /TransformParams';
13303
			$out .= ' /P '.$this->signature_data['cert_type'];
13304
			$out .= ' /V /1.2';
13305
		} else {
13306
			$out .= ' /TransformMethod /UR3';
13307
			$out .= ' /TransformParams <<';
13308
			$out .= ' /Type /TransformParams';
13309
			$out .= ' /V /2.2';
13310
			if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13311
				$out .= ' /Document['.$this->ur['document'].']';
13312
			}
13313
			if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13314
				$out .= ' /Form['.$this->ur['form'].']';
13315
			}
13316
			if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13317
				$out .= ' /Signature['.$this->ur['signature'].']';
13318
			}
13319
			if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13320
				$out .= ' /Annots['.$this->ur['annots'].']';
13321
			}
13322
			if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13323
				$out .= ' /EF['.$this->ur['ef'].']';
13324
			}
13325
			if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13326
				$out .= ' /FormEX['.$this->ur['formex'].']';
13327
			}
13328
		}
13329
		$out .= ' >>'; // close TransformParams
13330
		// optional digest data (values must be calculated and replaced later)
13331
		//$out .= ' /Data ********** 0 R';
13332
		//$out .= ' /DigestMethod/MD5';
13333
		//$out .= ' /DigestLocation[********** 34]';
13334
		//$out .= ' /DigestValue<********************************>';
13335
		$out .= ' >>';
13336
		$out .= ' ]'; // end of reference
13337
		if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13338
			$out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13339
		}
13340
		if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13341
			$out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13342
		}
13343
		if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13344
			$out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13345
		}
13346
		if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13347
			$out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13348
		}
13349
		$out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13350
		$out .= ' >>';
13351
		$out .= "\n".'endobj';
13352
		$this->_out($out);
13353
	}
13354
 
13355
	/**
13356
	 * Set User's Rights for PDF Reader
13357
	 * WARNING: This is experimental and currently do not work.
13358
	 * Check the PDF Reference 8.7.1 Transform Methods,
13359
	 * Table 8.105 Entries in the UR transform parameters dictionary
13360
	 * @param $enable (boolean) if true enable user's rights on PDF reader
13361
	 * @param $document (string) Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
13362
	 * @param $annots (string) Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
13363
	 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13364
	 * @param $signature (string) Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
13365
	 * @param $ef (string) Names specifying additional usage rights for named embedded files in the document. Valid names are /Create/Delete/Modify/Import, which permit the user to perform the named operation on named embedded files
13366
	 Names specifying additional embedded-files-related usage rights for the document.
13367
	 * @param $formex (string) Names specifying additional form-field-related usage rights. The only valid name is BarcodePlaintext, which permits text form field data to be encoded as a plaintext two-dimensional barcode.
13368
	 * @public
13369
	 * @author Nicola Asuni
13370
	 * @since 2.9.000 (2008-03-26)
13371
	 */
13372
	public function setUserRights(
13373
			$enable=true,
13374
			$document='/FullSave',
13375
			$annots='/Create/Delete/Modify/Copy/Import/Export',
13376
			$form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13377
			$signature='/Modify',
13378
			$ef='/Create/Delete/Modify/Import',
13379
			$formex='') {
13380
		$this->ur['enabled'] = $enable;
13381
		$this->ur['document'] = $document;
13382
		$this->ur['annots'] = $annots;
13383
		$this->ur['form'] = $form;
13384
		$this->ur['signature'] = $signature;
13385
		$this->ur['ef'] = $ef;
13386
		$this->ur['formex'] = $formex;
13387
		if (!$this->sign) {
13388
			$this->setSignature('', '', '', '', 0, array());
13389
		}
13390
	}
13391
 
13392
	/**
13393
	 * Enable document signature (requires the OpenSSL Library).
13394
	 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13395
	 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13396
	 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13397
	 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13398
	 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
13399
	 * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
13400
	 * @param $private_key_password (string) password
13401
	 * @param $extracerts (string) specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
13402
	 * @param $cert_type (int) The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
13403
	 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
13404
	 * @public
13405
	 * @author Nicola Asuni
13406
	 * @since 4.6.005 (2009-04-24)
13407
	 */
13408
	public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
13409
		// to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13410
		// to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13411
		// to convert pfx certificate to pem: openssl
13412
		//     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13413
		$this->sign = true;
13414
		++$this->n;
13415
		$this->sig_obj_id = $this->n; // signature widget
13416
		++$this->n; // signature object ($this->sig_obj_id + 1)
13417
		$this->signature_data = array();
13418
		if (strlen($signing_cert) == 0) {
13419
			$this->Error('Please provide a certificate file and password!');
13420
		}
13421
		if (strlen($private_key) == 0) {
13422
			$private_key = $signing_cert;
13423
		}
13424
		$this->signature_data['signcert'] = $signing_cert;
13425
		$this->signature_data['privkey'] = $private_key;
13426
		$this->signature_data['password'] = $private_key_password;
13427
		$this->signature_data['extracerts'] = $extracerts;
13428
		$this->signature_data['cert_type'] = $cert_type;
13429
		$this->signature_data['info'] = $info;
13430
	}
13431
 
13432
	/**
13433
	 * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13434
	 * @param $x (float) Abscissa of the upper-left corner.
13435
	 * @param $y (float) Ordinate of the upper-left corner.
13436
	 * @param $w (float) Width of the signature area.
13437
	 * @param $h (float) Height of the signature area.
13438
	 * @param $page (int) option page number (if < 0 the current page is used).
13439
	 * @param $name (string) Name of the signature.
13440
	 * @public
13441
	 * @author Nicola Asuni
13442
	 * @since 5.3.011 (2010-06-17)
13443
	 */
13444
	public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13445
		$this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13446
	}
13447
 
13448
	/**
13449
	 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13450
	 * @param $x (float) Abscissa of the upper-left corner.
13451
	 * @param $y (float) Ordinate of the upper-left corner.
13452
	 * @param $w (float) Width of the signature area.
13453
	 * @param $h (float) Height of the signature area.
13454
	 * @param $page (int) option page number (if < 0 the current page is used).
13455
	 * @param $name (string) Name of the signature.
13456
	 * @public
13457
	 * @author Nicola Asuni
13458
	 * @since 5.9.101 (2011-07-06)
13459
	 */
13460
	public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13461
		++$this->n;
13462
		$this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13463
	}
13464
 
13465
	/**
13466
	 * Get the array that defines the signature appearance (page and rectangle coordinates).
13467
	 * @param $x (float) Abscissa of the upper-left corner.
13468
	 * @param $y (float) Ordinate of the upper-left corner.
13469
	 * @param $w (float) Width of the signature area.
13470
	 * @param $h (float) Height of the signature area.
13471
	 * @param $page (int) option page number (if < 0 the current page is used).
13472
	 * @param $name (string) Name of the signature.
13473
	 * @return (array) Array defining page and rectangle coordinates of signature appearance.
13474
	 * @protected
13475
	 * @author Nicola Asuni
13476
	 * @since 5.9.101 (2011-07-06)
13477
	 */
13478
	protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13479
		$sigapp = array();
13480
		if (($page < 1) OR ($page > $this->numpages)) {
13481
			$sigapp['page'] = $this->page;
13482
		} else {
13483
			$sigapp['page'] = intval($page);
13484
		}
13485
		if (empty($name)) {
13486
			$sigapp['name'] = 'Signature';
13487
		} else {
13488
			$sigapp['name'] = $name;
13489
		}
13490
		$a = $x * $this->k;
13491
		$b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13492
		$c = $w * $this->k;
13493
		$d = $h * $this->k;
13494
		$sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13495
		return $sigapp;
13496
	}
13497
 
13498
	/**
13499
	 * Create a new page group.
13500
	 * NOTE: call this function before calling AddPage()
13501
	 * @param $page (int) starting group page (leave empty for next page).
13502
	 * @public
13503
	 * @since 3.0.000 (2008-03-27)
13504
	 */
13505
	public function startPageGroup($page='') {
13506
		if (empty($page)) {
13507
			$page = $this->page + 1;
13508
		}
13509
		$this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13510
	}
13511
 
13512
	/**
13513
	 * Set the starting page number.
13514
	 * @param $num (int) Starting page number.
13515
	 * @since 5.9.093 (2011-06-16)
13516
	 * @public
13517
	 */
13518
	public function setStartingPageNumber($num=1) {
13519
		$this->starting_page_number = max(0, intval($num));
13520
	}
13521
 
13522
	/**
13523
	 * Returns the string alias used right align page numbers.
13524
	 * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13525
	 * @return string
13526
	 * @since 5.9.099 (2011-06-27)
13527
	 * @public
13528
	 */
13529
	public function getAliasRightShift() {
13530
		// calculate aproximatively the ratio between widths of aliases and replacements.
13531
		$ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13532
		$rep = str_repeat(' ', $this->GetNumChars($ref));
13533
		$wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep)));
13534
		$sdiff = sprintf('%F', $wdiff);
13535
		$alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13536
		if ($this->isUnicodeFont()) {
13537
			$alias = '{'.$alias;
13538
		}
13539
		return $alias;
13540
	}
13541
 
13542
	/**
13543
	 * Returns the string alias used for the total number of pages.
13544
	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13545
	 * This alias will be replaced by the total number of pages in the document.
13546
	 * @return string
13547
	 * @since 4.0.018 (2008-08-08)
13548
	 * @public
13549
	 */
13550
	public function getAliasNbPages() {
13551
		if ($this->isUnicodeFont()) {
13552
			return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13553
		}
13554
		return TCPDF_STATIC::$alias_tot_pages;
13555
	}
13556
 
13557
	/**
13558
	 * Returns the string alias used for the page number.
13559
	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13560
	 * This alias will be replaced by the page number.
13561
	 * @return string
13562
	 * @since 4.5.000 (2009-01-02)
13563
	 * @public
13564
	 */
13565
	public function getAliasNumPage() {
13566
		if ($this->isUnicodeFont()) {
13567
			return '{'.TCPDF_STATIC::$alias_num_page.'}';
13568
		}
13569
		return TCPDF_STATIC::$alias_num_page;
13570
	}
13571
 
13572
	/**
13573
	 * Return the alias for the total number of pages in the current page group.
13574
	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13575
	 * This alias will be replaced by the total number of pages in this group.
13576
	 * @return alias of the current page group
13577
	 * @public
13578
	 * @since 3.0.000 (2008-03-27)
13579
	 */
13580
	public function getPageGroupAlias() {
13581
		if ($this->isUnicodeFont()) {
13582
			return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13583
		}
13584
		return TCPDF_STATIC::$alias_group_tot_pages;
13585
	}
13586
 
13587
	/**
13588
	 * Return the alias for the page number on the current page group.
13589
	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13590
	 * This alias will be replaced by the page number (relative to the belonging group).
13591
	 * @return alias of the current page group
13592
	 * @public
13593
	 * @since 4.5.000 (2009-01-02)
13594
	 */
13595
	public function getPageNumGroupAlias() {
13596
		if ($this->isUnicodeFont()) {
13597
			return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13598
		}
13599
		return TCPDF_STATIC::$alias_group_num_page;
13600
	}
13601
 
13602
	/**
13603
	 * Return the current page in the group.
13604
	 * @return current page in the group
13605
	 * @public
13606
	 * @since 3.0.000 (2008-03-27)
13607
	 */
13608
	public function getGroupPageNo() {
13609
		return $this->pagegroups[$this->currpagegroup];
13610
	}
13611
 
13612
	/**
13613
	 * Returns the current group page number formatted as a string.
13614
	 * @public
13615
	 * @since 4.3.003 (2008-11-18)
13616
	 * @see PaneNo(), formatPageNumber()
13617
	 */
13618
	public function getGroupPageNoFormatted() {
13619
		return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo());
13620
	}
13621
 
13622
	/**
13623
	 * Returns the current page number formatted as a string.
13624
	 * @public
13625
	 * @since 4.2.005 (2008-11-06)
13626
	 * @see PaneNo(), formatPageNumber()
13627
	 */
13628
	public function PageNoFormatted() {
13629
		return TCPDF_STATIC::formatPageNumber($this->PageNo());
13630
	}
13631
 
13632
	/**
13633
	 * Put pdf layers.
13634
	 * @protected
13635
	 * @since 3.0.000 (2008-03-27)
13636
	 */
13637
	protected function _putocg() {
13638
		if (empty($this->pdflayers)) {
13639
			return;
13640
		}
13641
		foreach ($this->pdflayers as $key => $layer) {
13642
			 $this->pdflayers[$key]['objid'] = $this->_newobj();
13643
			 $out = '<< /Type /OCG';
13644
			 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13645
			 $out .= ' /Usage <<';
13646
			 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13647
			 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13648
			 $out .= ' >> >>';
13649
			 $out .= "\n".'endobj';
13650
			 $this->_out($out);
13651
		}
13652
	}
13653
 
13654
	/**
13655
	 * Start a new pdf layer.
13656
	 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
13657
	 * @param $print (boolean) Set to true to print this layer.
13658
	 * @param $view (boolean) Set to true to view this layer.
13659
	 * @public
13660
	 * @since 5.9.102 (2011-07-13)
13661
	 */
13662
	public function startLayer($name='', $print=true, $view=true) {
13663
		if ($this->state != 2) {
13664
			return;
13665
		}
13666
		$layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13667
		if (empty($name)) {
13668
			$name = $layer;
13669
		} else {
13670
			$name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13671
		}
13672
		$this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view);
13673
		$this->openMarkedContent = true;
13674
		$this->_out('/OC /'.$layer.' BDC');
13675
	}
13676
 
13677
	/**
13678
	 * End the current PDF layer.
13679
	 * @public
13680
	 * @since 5.9.102 (2011-07-13)
13681
	 */
13682
	public function endLayer() {
13683
		if ($this->state != 2) {
13684
			return;
13685
		}
13686
		if ($this->openMarkedContent) {
13687
			// close existing open marked-content layer
13688
			$this->_out('EMC');
13689
			$this->openMarkedContent = false;
13690
		}
13691
	}
13692
 
13693
	/**
13694
	 * Set the visibility of the successive elements.
13695
	 * This can be useful, for instance, to put a background
13696
	 * image or color that will show on screen but won't print.
13697
	 * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
13698
	 * @public
13699
	 * @since 3.0.000 (2008-03-27)
13700
	 */
13701
	public function setVisibility($v) {
13702
		if ($this->state != 2) {
13703
			return;
13704
		}
13705
		$this->endLayer();
13706
		switch($v) {
13707
			case 'print': {
13708
				$this->startLayer('Print', true, false);
13709
				break;
13710
			}
13711
			case 'view':
13712
			case 'screen': {
13713
				$this->startLayer('View', false, true);
13714
				break;
13715
			}
13716
			case 'all': {
13717
				$this->_out('');
13718
				break;
13719
			}
13720
			default: {
13721
				$this->Error('Incorrect visibility: '.$v);
13722
				break;
13723
			}
13724
		}
13725
	}
13726
 
13727
	/**
13728
	 * Add transparency parameters to the current extgstate
13729
	 * @param $parms (array) parameters
13730
	 * @return the number of extgstates
13731
	 * @protected
13732
	 * @since 3.0.000 (2008-03-27)
13733
	 */
13734
	protected function addExtGState($parms) {
13735
		if ($this->pdfa_mode) {
13736
			// transparencies are not allowed in PDF/A mode
13737
			return;
13738
		}
13739
		// check if this ExtGState already exist
13740
		foreach ($this->extgstates as $i => $ext) {
13741
			if ($ext['parms'] == $parms) {
13742
				if ($this->inxobj) {
13743
					// we are inside an XObject template
13744
					$this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13745
				}
13746
				// return reference to existing ExtGState
13747
				return $i;
13748
			}
13749
		}
13750
		$n = (count($this->extgstates) + 1);
13751
		$this->extgstates[$n] = array('parms' => $parms);
13752
		if ($this->inxobj) {
13753
			// we are inside an XObject template
13754
			$this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13755
		}
13756
		return $n;
13757
	}
13758
 
13759
	/**
13760
	 * Add an extgstate
13761
	 * @param $gs (array) extgstate
13762
	 * @protected
13763
	 * @since 3.0.000 (2008-03-27)
13764
	 */
13765
	protected function setExtGState($gs) {
13766
		if ($this->pdfa_mode OR ($this->state != 2)) {
13767
			// transparency is not allowed in PDF/A mode
13768
			return;
13769
		}
13770
		$this->_out(sprintf('/GS%d gs', $gs));
13771
	}
13772
 
13773
	/**
13774
	 * Put extgstates for object transparency
13775
	 * @protected
13776
	 * @since 3.0.000 (2008-03-27)
13777
	 */
13778
	protected function _putextgstates() {
13779
		foreach ($this->extgstates as $i => $ext) {
13780
			$this->extgstates[$i]['n'] = $this->_newobj();
13781
			$out = '<< /Type /ExtGState';
13782
			foreach ($ext['parms'] as $k => $v) {
13783
				if (is_float($v)) {
13784
					$v = sprintf('%F', $v);
13785
				} elseif ($v === true) {
13786
					$v = 'true';
13787
				} elseif ($v === false) {
13788
					$v = 'false';
13789
				}
13790
				$out .= ' /'.$k.' '.$v;
13791
			}
13792
			$out .= ' >>';
13793
			$out .= "\n".'endobj';
13794
			$this->_out($out);
13795
		}
13796
	}
13797
 
13798
	/**
13799
	 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13800
	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13801
	 * @param $stroking (boolean) If true apply overprint for stroking operations.
13802
	 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking.
13803
	 * @param $mode (integer) Overprint mode: (0 = each source colour component value replaces the value previously painted for the corresponding device colorant; 1 = a tint value of 0.0 for a source colour component shall leave the corresponding component of the previously painted colour unchanged).
13804
	 * @public
13805
	 * @since 5.9.152 (2012-03-23)
13806
	 */
13807
	public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13808
		if ($this->state != 2) {
13809
			return;
13810
		}
13811
		$stroking = $stroking ? true : false;
13812
		if (TCPDF_STATIC::empty_string($nonstroking)) {
13813
			// default value if not set
13814
			$nonstroking = $stroking;
13815
		} else {
13816
			$nonstroking = $nonstroking ? true : false;
13817
		}
13818
		if (($mode != 0) AND ($mode != 1)) {
13819
			$mode = 0;
13820
		}
13821
		$this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13822
		$gs = $this->addExtGState($this->overprint);
13823
		$this->setExtGState($gs);
13824
	}
13825
 
13826
	/**
13827
	 * Get the overprint mode array (OP, op, OPM).
13828
	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13829
	 * @return array.
13830
	 * @public
13831
	 * @since 5.9.152 (2012-03-23)
13832
	 */
13833
	public function getOverprint() {
13834
		return $this->overprint;
13835
	}
13836
 
13837
	/**
13838
	 * Set alpha for stroking (CA) and non-stroking (ca) operations.
13839
	 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
13840
	 * @param $bm (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
13841
	 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
13842
	 * @param $ais (boolean)
13843
	 * @public
13844
	 * @since 3.0.000 (2008-03-27)
13845
	 */
13846
	public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13847
		if ($this->pdfa_mode) {
13848
			// transparency is not allowed in PDF/A mode
13849
			return;
13850
		}
13851
		$stroking = floatval($stroking);
13852
		if (TCPDF_STATIC::empty_string($nonstroking)) {
13853
			// default value if not set
13854
			$nonstroking = $stroking;
13855
		} else {
13856
			$nonstroking = floatval($nonstroking);
13857
		}
13858
		if ($bm[0] == '/') {
13859
			// remove trailing slash
13860
			$bm = substr($bm, 1);
13861
		}
13862
		if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13863
			$bm = 'Normal';
13864
		}
13865
		$ais = $ais ? true : false;
13866
		$this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13867
		$gs = $this->addExtGState($this->alpha);
13868
		$this->setExtGState($gs);
13869
	}
13870
 
13871
	/**
13872
	 * Get the alpha mode array (CA, ca, BM, AIS).
13873
	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13874
	 * @return array.
13875
	 * @public
13876
	 * @since 5.9.152 (2012-03-23)
13877
	 */
13878
	public function getAlpha() {
13879
		return $this->alpha;
13880
	}
13881
 
13882
	/**
13883
	 * Set the default JPEG compression quality (1-100)
13884
	 * @param $quality (int) JPEG quality, integer between 1 and 100
13885
	 * @public
13886
	 * @since 3.0.000 (2008-03-27)
13887
	 */
13888
	public function setJPEGQuality($quality) {
13889
		if (($quality < 1) OR ($quality > 100)) {
13890
			$quality = 75;
13891
		}
13892
		$this->jpeg_quality = intval($quality);
13893
	}
13894
 
13895
	/**
13896
	 * Set the default number of columns in a row for HTML tables.
13897
	 * @param $cols (int) number of columns
13898
	 * @public
13899
	 * @since 3.0.014 (2008-06-04)
13900
	 */
13901
	public function setDefaultTableColumns($cols=4) {
13902
		$this->default_table_columns = intval($cols);
13903
	}
13904
 
13905
	/**
13906
	 * Set the height of the cell (line height) respect the font height.
13907
	 * @param $h (int) cell proportion respect font height (typical value = 1.25).
13908
	 * @public
13909
	 * @since 3.0.014 (2008-06-04)
13910
	 */
13911
	public function setCellHeightRatio($h) {
13912
		$this->cell_height_ratio = $h;
13913
	}
13914
 
13915
	/**
13916
	 * return the height of cell repect font height.
13917
	 * @public
13918
	 * @since 4.0.012 (2008-07-24)
13919
	 */
13920
	public function getCellHeightRatio() {
13921
		return $this->cell_height_ratio;
13922
	}
13923
 
13924
	/**
13925
	 * Set the PDF version (check PDF reference for valid values).
13926
	 * @param $version (string) PDF document version.
13927
	 * @public
13928
	 * @since 3.1.000 (2008-06-09)
13929
	 */
13930
	public function setPDFVersion($version='1.7') {
13931
		if ($this->pdfa_mode) {
13932
			// PDF/A mode
13933
			$this->PDFVersion = '1.4';
13934
		} else {
13935
			$this->PDFVersion = $version;
13936
		}
13937
	}
13938
 
13939
	/**
13940
	 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
13941
	 * (see Section 8.1 of PDF reference, "Viewer Preferences").
13942
	 * <ul><li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li><li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li><li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li><li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li><li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li></ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li><li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li></ul></li><li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li><li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li><li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li><li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li></ul>
13943
	 * @param $preferences (array) array of options.
13944
	 * @author Nicola Asuni
13945
	 * @public
13946
	 * @since 3.1.000 (2008-06-09)
13947
	 */
13948
	public function setViewerPreferences($preferences) {
13949
		$this->viewer_preferences = $preferences;
13950
	}
13951
 
13952
	/**
13953
	 * Paints color transition registration bars
13954
	 * @param $x (float) abscissa of the top left corner of the rectangle.
13955
	 * @param $y (float) ordinate of the top left corner of the rectangle.
13956
	 * @param $w (float) width of the rectangle.
13957
	 * @param $h (float) height of the rectangle.
13958
	 * @param $transition (boolean) if true prints tcolor transitions to white.
13959
	 * @param $vertical (boolean) if true prints bar vertically.
13960
	 * @param $colors (string) colors to print, one letter per color separated by comma (for example 'A,W,R,G,B,C,M,Y,K'): A=black, W=white, R=red, G=green, B=blue, C=cyan, M=magenta, Y=yellow, K=black.
13961
	 * @author Nicola Asuni
13962
	 * @since 4.9.000 (2010-03-26)
13963
	 * @public
13964
	 */
13965
	public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
13966
		$bars = explode(',', $colors);
13967
		$numbars = count($bars); // number of bars to print
13968
		// set bar measures
13969
		if ($vertical) {
13970
			$coords = array(0, 0, 0, 1);
13971
			$wb = $w / $numbars; // bar width
13972
			$hb = $h; // bar height
13973
			$xd = $wb; // delta x
13974
			$yd = 0; // delta y
13975
		} else {
13976
			$coords = array(1, 0, 0, 0);
13977
			$wb = $w; // bar width
13978
			$hb = $h / $numbars; // bar height
13979
			$xd = 0; // delta x
13980
			$yd = $hb; // delta y
13981
		}
13982
		$xb = $x;
13983
		$yb = $y;
13984
		foreach ($bars as $col) {
13985
			switch ($col) {
13986
				// set transition colors
13987
				case 'A': { // BLACK
13988
					$col_a = array(255);
13989
					$col_b = array(0);
13990
					break;
13991
				}
13992
				case 'W': { // WHITE
13993
					$col_a = array(0);
13994
					$col_b = array(255);
13995
					break;
13996
				}
13997
				case 'R': { // R
13998
					$col_a = array(255,255,255);
13999
					$col_b = array(255,0,0);
14000
					break;
14001
				}
14002
				case 'G': { // G
14003
					$col_a = array(255,255,255);
14004
					$col_b = array(0,255,0);
14005
					break;
14006
				}
14007
				case 'B': { // B
14008
					$col_a = array(255,255,255);
14009
					$col_b = array(0,0,255);
14010
					break;
14011
				}
14012
				case 'C': { // C
14013
					$col_a = array(0,0,0,0);
14014
					$col_b = array(100,0,0,0);
14015
					break;
14016
				}
14017
				case 'M': { // M
14018
					$col_a = array(0,0,0,0);
14019
					$col_b = array(0,100,0,0);
14020
					break;
14021
				}
14022
				case 'Y': { // Y
14023
					$col_a = array(0,0,0,0);
14024
					$col_b = array(0,0,100,0);
14025
					break;
14026
				}
14027
				case 'K': { // K
14028
					$col_a = array(0,0,0,0);
14029
					$col_b = array(0,0,0,100);
14030
					break;
14031
				}
14032
				default: { // GRAY
14033
					$col_a = array(255);
14034
					$col_b = array(0);
14035
					break;
14036
				}
14037
			}
14038
			if ($transition) {
14039
				// color gradient
14040
				$this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14041
			} else {
14042
				// color rectangle
14043
				$this->SetFillColorArray($col_b);
14044
				$this->Rect($xb, $yb, $wb, $hb, 'F', array());
14045
			}
14046
			$xb += $xd;
14047
			$yb += $yd;
14048
		}
14049
	}
14050
 
14051
	/**
14052
	 * Paints crop marks.
14053
	 * @param $x (float) abscissa of the crop mark center.
14054
	 * @param $y (float) ordinate of the crop mark center.
14055
	 * @param $w (float) width of the crop mark.
14056
	 * @param $h (float) height of the crop mark.
14057
	 * @param $type (string) type of crop mark, one symbol per type separated by comma: T = TOP, F = BOTTOM, L = LEFT, R = RIGHT, TL = A = TOP-LEFT, TR = B = TOP-RIGHT, BL = C = BOTTOM-LEFT, BR = D = BOTTOM-RIGHT.
14058
	 * @param $color (array) crop mark color (default black).
14059
	 * @author Nicola Asuni
14060
	 * @since 4.9.000 (2010-03-26)
14061
	 * @public
14062
	 */
14063
	public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(0,0,0)) {
14064
		$this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14065
		$type = strtoupper($type);
14066
		$type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14067
		// split type in single components
14068
		$type = str_replace('-', ',', $type);
14069
		$type = str_replace('TL', 'T,L', $type);
14070
		$type = str_replace('TR', 'T,R', $type);
14071
		$type = str_replace('BL', 'F,L', $type);
14072
		$type = str_replace('BR', 'F,R', $type);
14073
		$type = str_replace('A', 'T,L', $type);
14074
		$type = str_replace('B', 'T,R', $type);
14075
		$type = str_replace('T,RO', 'BO', $type);
14076
		$type = str_replace('C', 'F,L', $type);
14077
		$type = str_replace('D', 'F,R', $type);
14078
		$crops = explode(',', strtoupper($type));
14079
		// remove duplicates
14080
		$crops = array_unique($crops);
14081
		$dw = ($w / 4); // horizontal space to leave before the intersection point
14082
		$dh = ($h / 4); // vertical space to leave before the intersection point
14083
		foreach ($crops as $crop) {
14084
			switch ($crop) {
14085
				case 'T':
14086
				case 'TOP': {
14087
					$x1 = $x;
14088
					$y1 = ($y - $h);
14089
					$x2 = $x;
14090
					$y2 = ($y - $dh);
14091
					break;
14092
				}
14093
				case 'F':
14094
				case 'BOTTOM': {
14095
					$x1 = $x;
14096
					$y1 = ($y + $dh);
14097
					$x2 = $x;
14098
					$y2 = ($y + $h);
14099
					break;
14100
				}
14101
				case 'L':
14102
				case 'LEFT': {
14103
					$x1 = ($x - $w);
14104
					$y1 = $y;
14105
					$x2 = ($x - $dw);
14106
					$y2 = $y;
14107
					break;
14108
				}
14109
				case 'R':
14110
				case 'RIGHT': {
14111
					$x1 = ($x + $dw);
14112
					$y1 = $y;
14113
					$x2 = ($x + $w);
14114
					$y2 = $y;
14115
					break;
14116
				}
14117
			}
14118
			$this->Line($x1, $y1, $x2, $y2);
14119
		}
14120
	}
14121
 
14122
	/**
14123
	 * Paints a registration mark
14124
	 * @param $x (float) abscissa of the registration mark center.
14125
	 * @param $y (float) ordinate of the registration mark center.
14126
	 * @param $r (float) radius of the crop mark.
14127
	 * @param $double (boolean) if true print two concentric crop marks.
14128
	 * @param $cola (array) crop mark color (default black).
14129
	 * @param $colb (array) second crop mark color.
14130
	 * @author Nicola Asuni
14131
	 * @since 4.9.000 (2010-03-26)
14132
	 * @public
14133
	 */
14134
	public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
14135
		$line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14136
		$this->SetFillColorArray($cola);
14137
		$this->PieSector($x, $y, $r, 90, 180, 'F');
14138
		$this->PieSector($x, $y, $r, 270, 360, 'F');
14139
		$this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14140
		if ($double) {
14141
			$r2 = $r * 0.5;
14142
			$this->SetFillColorArray($colb);
14143
			$this->PieSector($x, $y, $r2, 90, 180, 'F');
14144
			$this->PieSector($x, $y, $r2, 270, 360, 'F');
14145
			$this->SetFillColorArray($cola);
14146
			$this->PieSector($x, $y, $r2, 0, 90, 'F');
14147
			$this->PieSector($x, $y, $r2, 180, 270, 'F');
14148
			$this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
14149
		}
14150
	}
14151
 
14152
	/**
14153
	 * Paints a linear colour gradient.
14154
	 * @param $x (float) abscissa of the top left corner of the rectangle.
14155
	 * @param $y (float) ordinate of the top left corner of the rectangle.
14156
	 * @param $w (float) width of the rectangle.
14157
	 * @param $h (float) height of the rectangle.
14158
	 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14159
	 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14160
	 * @param $coords (array) array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
14161
	 * @author Andreas Würmser, Nicola Asuni
14162
	 * @since 3.1.000 (2008-06-09)
14163
	 * @public
14164
	 */
14165
	public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14166
		$this->Clip($x, $y, $w, $h);
14167
		$this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14168
	}
14169
 
14170
	/**
14171
	 * Paints a radial colour gradient.
14172
	 * @param $x (float) abscissa of the top left corner of the rectangle.
14173
	 * @param $y (float) ordinate of the top left corner of the rectangle.
14174
	 * @param $w (float) width of the rectangle.
14175
	 * @param $h (float) height of the rectangle.
14176
	 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14177
	 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14178
	 * @param $coords (array) array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.
14179
	 * @author Andreas Würmser, Nicola Asuni
14180
	 * @since 3.1.000 (2008-06-09)
14181
	 * @public
14182
	 */
14183
	public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14184
		$this->Clip($x, $y, $w, $h);
14185
		$this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14186
	}
14187
 
14188
	/**
14189
	 * Paints a coons patch mesh.
14190
	 * @param $x (float) abscissa of the top left corner of the rectangle.
14191
	 * @param $y (float) ordinate of the top left corner of the rectangle.
14192
	 * @param $w (float) width of the rectangle.
14193
	 * @param $h (float) height of the rectangle.
14194
	 * @param $col1 (array) first color (lower left corner) (RGB components).
14195
	 * @param $col2 (array) second color (lower right corner) (RGB components).
14196
	 * @param $col3 (array) third color (upper right corner) (RGB components).
14197
	 * @param $col4 (array) fourth color (upper left corner) (RGB components).
14198
	 * @param $coords (array) <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>
14199
	 * @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
14200
	 * @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
14201
	 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14202
	 * @author Andreas Würmser, Nicola Asuni
14203
	 * @since 3.1.000 (2008-06-09)
14204
	 * @public
14205
	 */
14206
	public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) {
14207
		if ($this->pdfa_mode OR ($this->state != 2)) {
14208
			return;
14209
		}
14210
		$this->Clip($x, $y, $w, $h);
14211
		$n = count($this->gradients) + 1;
14212
		$this->gradients[$n] = array();
14213
		$this->gradients[$n]['type'] = 6; //coons patch mesh
14214
		$this->gradients[$n]['coords'] = array();
14215
		$this->gradients[$n]['antialias'] = $antialias;
14216
		$this->gradients[$n]['colors'] = array();
14217
		$this->gradients[$n]['transparency'] = false;
14218
		//check the coords array if it is the simple array or the multi patch array
14219
		if (!isset($coords[0]['f'])) {
14220
			//simple array -> convert to multi patch array
14221
			if (!isset($col1[1])) {
14222
				$col1[1] = $col1[2] = $col1[0];
14223
			}
14224
			if (!isset($col2[1])) {
14225
				$col2[1] = $col2[2] = $col2[0];
14226
			}
14227
			if (!isset($col3[1])) {
14228
				$col3[1] = $col3[2] = $col3[0];
14229
			}
14230
			if (!isset($col4[1])) {
14231
				$col4[1] = $col4[2] = $col4[0];
14232
			}
14233
			$patch_array[0]['f'] = 0;
14234
			$patch_array[0]['points'] = $coords;
14235
			$patch_array[0]['colors'][0]['r'] = $col1[0];
14236
			$patch_array[0]['colors'][0]['g'] = $col1[1];
14237
			$patch_array[0]['colors'][0]['b'] = $col1[2];
14238
			$patch_array[0]['colors'][1]['r'] = $col2[0];
14239
			$patch_array[0]['colors'][1]['g'] = $col2[1];
14240
			$patch_array[0]['colors'][1]['b'] = $col2[2];
14241
			$patch_array[0]['colors'][2]['r'] = $col3[0];
14242
			$patch_array[0]['colors'][2]['g'] = $col3[1];
14243
			$patch_array[0]['colors'][2]['b'] = $col3[2];
14244
			$patch_array[0]['colors'][3]['r'] = $col4[0];
14245
			$patch_array[0]['colors'][3]['g'] = $col4[1];
14246
			$patch_array[0]['colors'][3]['b'] = $col4[2];
14247
		} else {
14248
			//multi patch array
14249
			$patch_array = $coords;
14250
		}
14251
		$bpcd = 65535; //16 bits per coordinate
14252
		//build the data stream
14253
		$this->gradients[$n]['stream'] = '';
14254
		$count_patch = count($patch_array);
14255
		for ($i=0; $i < $count_patch; ++$i) {
14256
			$this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14257
			$count_points = count($patch_array[$i]['points']);
14258
			for ($j=0; $j < $count_points; ++$j) {
14259
				//each point as 16 bit
14260
				$patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14261
				if ($patch_array[$i]['points'][$j] < 0) {
14262
					$patch_array[$i]['points'][$j] = 0;
14263
				}
14264
				if ($patch_array[$i]['points'][$j] > $bpcd) {
14265
					$patch_array[$i]['points'][$j] = $bpcd;
14266
				}
14267
				$this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14268
				$this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14269
			}
14270
			$count_cols = count($patch_array[$i]['colors']);
14271
			for ($j=0; $j < $count_cols; ++$j) {
14272
				//each color component as 8 bit
14273
				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14274
				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14275
				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14276
			}
14277
		}
14278
		//paint the gradient
14279
		$this->_out('/Sh'.$n.' sh');
14280
		//restore previous Graphic State
14281
		$this->_out('Q');
14282
		if ($this->inxobj) {
14283
			// we are inside an XObject template
14284
			$this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14285
		}
14286
	}
14287
 
14288
	/**
14289
	 * Set a rectangular clipping area.
14290
	 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14291
	 * @param $y (float) ordinate of the top left corner of the rectangle.
14292
	 * @param $w (float) width of the rectangle.
14293
	 * @param $h (float) height of the rectangle.
14294
	 * @author Andreas Würmser, Nicola Asuni
14295
	 * @since 3.1.000 (2008-06-09)
14296
	 * @protected
14297
	 */
14298
	protected function Clip($x, $y, $w, $h) {
14299
		if ($this->state != 2) {
14300
			 return;
14301
		}
14302
		if ($this->rtl) {
14303
			$x = $this->w - $x - $w;
14304
		}
14305
		//save current Graphic State
14306
		$s = 'q';
14307
		//set clipping area
14308
		$s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14309
		//set up transformation matrix for gradient
14310
		$s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14311
		$this->_out($s);
14312
	}
14313
 
14314
	/**
14315
	 * Output gradient.
14316
	 * @param $type (int) type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported)
14317
	 * @param $coords (array) array of coordinates.
14318
	 * @param $stops (array) array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1).
14319
	 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
14320
	 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14321
	 * @author Nicola Asuni
14322
	 * @since 3.1.000 (2008-06-09)
14323
	 * @public
14324
	 */
14325
	public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14326
		if ($this->pdfa_mode OR ($this->state != 2)) {
14327
			return;
14328
		}
14329
		$n = count($this->gradients) + 1;
14330
		$this->gradients[$n] = array();
14331
		$this->gradients[$n]['type'] = $type;
14332
		$this->gradients[$n]['coords'] = $coords;
14333
		$this->gradients[$n]['antialias'] = $antialias;
14334
		$this->gradients[$n]['colors'] = array();
14335
		$this->gradients[$n]['transparency'] = false;
14336
		// color space
14337
		$numcolspace = count($stops[0]['color']);
14338
		$bcolor = array_values($background);
14339
		switch($numcolspace) {
14340
			case 4: { // CMYK
14341
				$this->gradients[$n]['colspace'] = 'DeviceCMYK';
14342
				if (!empty($background)) {
14343
					$this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14344
				}
14345
				break;
14346
			}
14347
			case 3: { // RGB
14348
				$this->gradients[$n]['colspace'] = 'DeviceRGB';
14349
				if (!empty($background)) {
14350
					$this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14351
				}
14352
				break;
14353
			}
14354
			case 1: { // Gray scale
14355
				$this->gradients[$n]['colspace'] = 'DeviceGray';
14356
				if (!empty($background)) {
14357
					$this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14358
				}
14359
				break;
14360
			}
14361
		}
14362
		$num_stops = count($stops);
14363
		$last_stop_id = $num_stops - 1;
14364
		foreach ($stops as $key => $stop) {
14365
			$this->gradients[$n]['colors'][$key] = array();
14366
			// offset represents a location along the gradient vector
14367
			if (isset($stop['offset'])) {
14368
				$this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14369
			} else {
14370
				if ($key == 0) {
14371
					$this->gradients[$n]['colors'][$key]['offset'] = 0;
14372
				} elseif ($key == $last_stop_id) {
14373
					$this->gradients[$n]['colors'][$key]['offset'] = 1;
14374
				} else {
14375
					$offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14376
					$this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14377
				}
14378
			}
14379
			if (isset($stop['opacity'])) {
14380
				$this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14381
				if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14382
					$this->gradients[$n]['transparency'] = true;
14383
				}
14384
			} else {
14385
				$this->gradients[$n]['colors'][$key]['opacity'] = 1;
14386
			}
14387
			// exponent for the exponential interpolation function
14388
			if (isset($stop['exponent'])) {
14389
				$this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14390
			} else {
14391
				$this->gradients[$n]['colors'][$key]['exponent'] = 1;
14392
			}
14393
			// set colors
14394
			$color = array_values($stop['color']);
14395
			switch($numcolspace) {
14396
				case 4: { // CMYK
14397
					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14398
					break;
14399
				}
14400
				case 3: { // RGB
14401
					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14402
					break;
14403
				}
14404
				case 1: { // Gray scale
14405
					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14406
					break;
14407
				}
14408
			}
14409
		}
14410
		if ($this->gradients[$n]['transparency']) {
14411
			// paint luminosity gradient
14412
			$this->_out('/TGS'.$n.' gs');
14413
		}
14414
		//paint the gradient
14415
		$this->_out('/Sh'.$n.' sh');
14416
		//restore previous Graphic State
14417
		$this->_out('Q');
14418
		if ($this->inxobj) {
14419
			// we are inside an XObject template
14420
			$this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14421
		}
14422
	}
14423
 
14424
	/**
14425
	 * Output gradient shaders.
14426
	 * @author Nicola Asuni
14427
	 * @since 3.1.000 (2008-06-09)
14428
	 * @protected
14429
	 */
14430
	function _putshaders() {
14431
		if ($this->pdfa_mode) {
14432
			return;
14433
		}
14434
		$idt = count($this->gradients); //index for transparency gradients
14435
		foreach ($this->gradients as $id => $grad) {
14436
			if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14437
				$fc = $this->_newobj();
14438
				$out = '<<';
14439
				$out .= ' /FunctionType 3';
14440
				$out .= ' /Domain [0 1]';
14441
				$functions = '';
14442
				$bounds = '';
14443
				$encode = '';
14444
				$i = 1;
14445
				$num_cols = count($grad['colors']);
14446
				$lastcols = $num_cols - 1;
14447
				for ($i = 1; $i < $num_cols; ++$i) {
14448
					$functions .= ($fc + $i).' 0 R ';
14449
					if ($i < $lastcols) {
14450
						$bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14451
					}
14452
					$encode .= '0 1 ';
14453
				}
14454
				$out .= ' /Functions ['.trim($functions).']';
14455
				$out .= ' /Bounds ['.trim($bounds).']';
14456
				$out .= ' /Encode ['.trim($encode).']';
14457
				$out .= ' >>';
14458
				$out .= "\n".'endobj';
14459
				$this->_out($out);
14460
				for ($i = 1; $i < $num_cols; ++$i) {
14461
					$this->_newobj();
14462
					$out = '<<';
14463
					$out .= ' /FunctionType 2';
14464
					$out .= ' /Domain [0 1]';
14465
					$out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14466
					$out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14467
					$out .= ' /N '.$grad['colors'][$i]['exponent'];
14468
					$out .= ' >>';
14469
					$out .= "\n".'endobj';
14470
					$this->_out($out);
14471
				}
14472
				// set transparency fuctions
14473
				if ($grad['transparency']) {
14474
					$ft = $this->_newobj();
14475
					$out = '<<';
14476
					$out .= ' /FunctionType 3';
14477
					$out .= ' /Domain [0 1]';
14478
					$functions = '';
14479
					$i = 1;
14480
					$num_cols = count($grad['colors']);
14481
					for ($i = 1; $i < $num_cols; ++$i) {
14482
						$functions .= ($ft + $i).' 0 R ';
14483
					}
14484
					$out .= ' /Functions ['.trim($functions).']';
14485
					$out .= ' /Bounds ['.trim($bounds).']';
14486
					$out .= ' /Encode ['.trim($encode).']';
14487
					$out .= ' >>';
14488
					$out .= "\n".'endobj';
14489
					$this->_out($out);
14490
					for ($i = 1; $i < $num_cols; ++$i) {
14491
						$this->_newobj();
14492
						$out = '<<';
14493
						$out .= ' /FunctionType 2';
14494
						$out .= ' /Domain [0 1]';
14495
						$out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14496
						$out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14497
						$out .= ' /N '.$grad['colors'][$i]['exponent'];
14498
						$out .= ' >>';
14499
						$out .= "\n".'endobj';
14500
						$this->_out($out);
14501
					}
14502
				}
14503
			}
14504
			// set shading object
14505
			$this->_newobj();
14506
			$out = '<< /ShadingType '.$grad['type'];
14507
			if (isset($grad['colspace'])) {
14508
				$out .= ' /ColorSpace /'.$grad['colspace'];
14509
			} else {
14510
				$out .= ' /ColorSpace /DeviceRGB';
14511
			}
14512
			if (isset($grad['background']) AND !empty($grad['background'])) {
14513
				$out .= ' /Background ['.$grad['background'].']';
14514
			}
14515
			if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14516
				$out .= ' /AntiAlias true';
14517
			}
14518
			if ($grad['type'] == 2) {
14519
				$out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14520
				$out .= ' /Domain [0 1]';
14521
				$out .= ' /Function '.$fc.' 0 R';
14522
				$out .= ' /Extend [true true]';
14523
				$out .= ' >>';
14524
			} elseif ($grad['type'] == 3) {
14525
				//x0, y0, r0, x1, y1, r1
14526
				//at this this time radius of inner circle is 0
14527
				$out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14528
				$out .= ' /Domain [0 1]';
14529
				$out .= ' /Function '.$fc.' 0 R';
14530
				$out .= ' /Extend [true true]';
14531
				$out .= ' >>';
14532
			} elseif ($grad['type'] == 6) {
14533
				$out .= ' /BitsPerCoordinate 16';
14534
				$out .= ' /BitsPerComponent 8';
14535
				$out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14536
				$out .= ' /BitsPerFlag 8';
14537
				$stream = $this->_getrawstream($grad['stream']);
14538
				$out .= ' /Length '.strlen($stream);
14539
				$out .= ' >>';
14540
				$out .= ' stream'."\n".$stream."\n".'endstream';
14541
			}
14542
			$out .= "\n".'endobj';
14543
			$this->_out($out);
14544
			if ($grad['transparency']) {
14545
				$shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14546
				$shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14547
			}
14548
			$this->gradients[$id]['id'] = $this->n;
14549
			// set pattern object
14550
			$this->_newobj();
14551
			$out = '<< /Type /Pattern /PatternType 2';
14552
			$out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14553
			$out .= ' >>';
14554
			$out .= "\n".'endobj';
14555
			$this->_out($out);
14556
			$this->gradients[$id]['pattern'] = $this->n;
14557
			// set shading and pattern for transparency mask
14558
			if ($grad['transparency']) {
14559
				// luminosity pattern
14560
				$idgs = $id + $idt;
14561
				$this->_newobj();
14562
				$this->_out($shading_transparency);
14563
				$this->gradients[$idgs]['id'] = $this->n;
14564
				$this->_newobj();
14565
				$out = '<< /Type /Pattern /PatternType 2';
14566
				$out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14567
				$out .= ' >>';
14568
				$out .= "\n".'endobj';
14569
				$this->_out($out);
14570
				$this->gradients[$idgs]['pattern'] = $this->n;
14571
				// luminosity XObject
14572
				$oid = $this->_newobj();
14573
				$this->xobjects['LX'.$oid] = array('n' => $oid);
14574
				$filter = '';
14575
				$stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14576
				if ($this->compress) {
14577
					$filter = ' /Filter /FlateDecode';
14578
					$stream = gzcompress($stream);
14579
				}
14580
				$stream = $this->_getrawstream($stream);
14581
				$out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14582
				$out .= ' /Length '.strlen($stream);
14583
				$rect = sprintf('%F %F', $this->wPt, $this->hPt);
14584
				$out .= ' /BBox [0 0 '.$rect.']';
14585
				$out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14586
				$out .= ' /Resources <<';
14587
				$out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14588
				$out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14589
				$out .= ' >>';
14590
				$out .= ' >> ';
14591
				$out .= ' stream'."\n".$stream."\n".'endstream';
14592
				$out .= "\n".'endobj';
14593
				$this->_out($out);
14594
				// SMask
14595
				$this->_newobj();
14596
				$out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14597
				$this->_out($out);
14598
				// ExtGState
14599
				$this->_newobj();
14600
				$out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14601
				$this->_out($out);
14602
				$this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14603
			}
14604
		}
14605
	}
14606
 
14607
	/**
14608
	 * Draw the sector of a circle.
14609
	 * It can be used for instance to render pie charts.
14610
	 * @param $xc (float) abscissa of the center.
14611
	 * @param $yc (float) ordinate of the center.
14612
	 * @param $r (float) radius.
14613
	 * @param $a (float) start angle (in degrees).
14614
	 * @param $b (float) end angle (in degrees).
14615
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14616
	 * @param $cw: (float) indicates whether to go clockwise (default: true).
14617
	 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.
14618
	 * @author Maxime Delorme, Nicola Asuni
14619
	 * @since 3.1.000 (2008-06-09)
14620
	 * @public
14621
	 */
14622
	public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14623
		$this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14624
	}
14625
 
14626
	/**
14627
	 * Draw the sector of an ellipse.
14628
	 * It can be used for instance to render pie charts.
14629
	 * @param $xc (float) abscissa of the center.
14630
	 * @param $yc (float) ordinate of the center.
14631
	 * @param $rx (float) the x-axis radius.
14632
	 * @param $ry (float) the y-axis radius.
14633
	 * @param $a (float) start angle (in degrees).
14634
	 * @param $b (float) end angle (in degrees).
14635
	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14636
	 * @param $cw: (float) indicates whether to go clockwise.
14637
	 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14638
	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
14639
	 * @author Maxime Delorme, Nicola Asuni
14640
	 * @since 3.1.000 (2008-06-09)
14641
	 * @public
14642
	 */
14643
	public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14644
		if ($this->state != 2) {
14645
			 return;
14646
		}
14647
		if ($this->rtl) {
14648
			$xc = ($this->w - $xc);
14649
		}
14650
		$op = TCPDF_STATIC::getPathPaintOperator($style);
14651
		if ($op == 'f') {
14652
			$line_style = array();
14653
		}
14654
		if ($cw) {
14655
			$d = $b;
14656
			$b = (360 - $a + $o);
14657
			$a = (360 - $d + $o);
14658
		} else {
14659
			$b += $o;
14660
			$a += $o;
14661
		}
14662
		$this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14663
		$this->_out($op);
14664
	}
14665
 
14666
	/**
14667
	 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14668
	 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14669
	 * Only vector drawing is supported, not text or bitmap.
14670
	 * Although the script was successfully tested with various AI format versions, best results are probably achieved with files that were exported in the AI3 format (tested with Illustrator CS2, Freehand MX and Photoshop CS2).
14671
	 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14672
	 * @param $x (float) Abscissa of the upper-left corner.
14673
	 * @param $y (float) Ordinate of the upper-left corner.
14674
	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14675
	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14676
	 * @param $link (mixed) URL or identifier returned by AddLink().
14677
	 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14678
	 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
14679
	 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
14680
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
14681
	 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
14682
	 * @param $fixoutvals (boolean) if true remove values outside the bounding box.
14683
	 * @author Valentin Schmidt, Nicola Asuni
14684
	 * @since 3.1.000 (2008-06-09)
14685
	 * @public
14686
	 */
14687
	public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14688
		if ($this->state != 2) {
14689
			 return;
14690
		}
14691
		if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14692
			// convert EPS to raster image using GD or ImageMagick libraries
14693
			return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14694
		}
14695
		if ($x === '') {
14696
			$x = $this->x;
14697
		}
14698
		if ($y === '') {
14699
			$y = $this->y;
14700
		}
14701
		// check page for no-write regions and adapt page margins if necessary
14702
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
14703
		$k = $this->k;
14704
		if ($file{0} === '@') { // image from string
14705
			$data = substr($file, 1);
14706
		} else { // EPS/AI file
14707
			$data = file_get_contents($file);
14708
		}
14709
		if ($data === false) {
14710
			$this->Error('EPS file not found: '.$file);
14711
		}
14712
		$regs = array();
14713
		// EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14714
		preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14715
		if (count($regs) > 1) {
14716
			$version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14717
			if (strpos($version_str, 'Adobe Illustrator') !== false) {
14718
				$versexp = explode(' ', $version_str);
14719
				$version = (float)array_pop($versexp);
14720
				if ($version >= 9) {
14721
					$this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14722
				}
14723
			}
14724
		}
14725
		// strip binary bytes in front of PS-header
14726
		$start = strpos($data, '%!PS-Adobe');
14727
		if ($start > 0) {
14728
			$data = substr($data, $start);
14729
		}
14730
		// find BoundingBox params
14731
		preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14732
		if (count($regs) > 1) {
14733
			list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14734
		} else {
14735
			$this->Error('No BoundingBox found in EPS/AI file: '.$file);
14736
		}
14737
		$start = strpos($data, '%%EndSetup');
14738
		if ($start === false) {
14739
			$start = strpos($data, '%%EndProlog');
14740
		}
14741
		if ($start === false) {
14742
			$start = strpos($data, '%%BoundingBox');
14743
		}
14744
		$data = substr($data, $start);
14745
		$end = strpos($data, '%%PageTrailer');
14746
		if ($end===false) {
14747
			$end = strpos($data, 'showpage');
14748
		}
14749
		if ($end) {
14750
			$data = substr($data, 0, $end);
14751
		}
14752
		// calculate image width and height on document
14753
		if (($w <= 0) AND ($h <= 0)) {
14754
			$w = ($x2 - $x1) / $k;
14755
			$h = ($y2 - $y1) / $k;
14756
		} elseif ($w <= 0) {
14757
			$w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14758
		} elseif ($h <= 0) {
14759
			$h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14760
		}
14761
		// fit the image on available space
14762
		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14763
		if ($this->rasterize_vector_images) {
14764
			// convert EPS to raster image using GD or ImageMagick libraries
14765
			return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14766
		}
14767
		// set scaling factors
14768
		$scale_x = $w / (($x2 - $x1) / $k);
14769
		$scale_y = $h / (($y2 - $y1) / $k);
14770
		// set alignment
14771
		$this->img_rb_y = $y + $h;
14772
		// set alignment
14773
		if ($this->rtl) {
14774
			if ($palign == 'L') {
14775
				$ximg = $this->lMargin;
14776
			} elseif ($palign == 'C') {
14777
				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14778
			} elseif ($palign == 'R') {
14779
				$ximg = $this->w - $this->rMargin - $w;
14780
			} else {
14781
				$ximg = $x - $w;
14782
			}
14783
			$this->img_rb_x = $ximg;
14784
		} else {
14785
			if ($palign == 'L') {
14786
				$ximg = $this->lMargin;
14787
			} elseif ($palign == 'C') {
14788
				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14789
			} elseif ($palign == 'R') {
14790
				$ximg = $this->w - $this->rMargin - $w;
14791
			} else {
14792
				$ximg = $x;
14793
			}
14794
			$this->img_rb_x = $ximg + $w;
14795
		}
14796
		if ($useBoundingBox) {
14797
			$dx = $ximg * $k - $x1;
14798
			$dy = $y * $k - $y1;
14799
		} else {
14800
			$dx = $ximg * $k;
14801
			$dy = $y * $k;
14802
		}
14803
		// save the current graphic state
14804
		$this->_out('q'.$this->epsmarker);
14805
		// translate
14806
		$this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14807
		// scale
14808
		if (isset($scale_x)) {
14809
			$this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14810
		}
14811
		// handle pc/unix/mac line endings
14812
		$lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14813
		$u=0;
14814
		$cnt = count($lines);
14815
		for ($i=0; $i < $cnt; ++$i) {
14816
			$line = $lines[$i];
14817
			if (($line == '') OR ($line{0} == '%')) {
14818
				continue;
14819
			}
14820
			$len = strlen($line);
14821
			// check for spot color names
14822
			$color_name = '';
14823
			if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14824
				if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
14825
					// extract spot color name
14826
					$color_name = $matches[0];
14827
					// remove color name from string
14828
					$line = str_replace(' '.$color_name, '', $line);
14829
					// remove pharentesis from color name
14830
					$color_name = substr($color_name, 1, -1);
14831
				}
14832
			}
14833
			$chunks = explode(' ', $line);
14834
			$cmd = trim(array_pop($chunks));
14835
			// RGB
14836
			if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14837
				$b = array_pop($chunks);
14838
				$g = array_pop($chunks);
14839
				$r = array_pop($chunks);
14840
				$this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
14841
				continue;
14842
			}
14843
			$skip = false;
14844
			if ($fixoutvals) {
14845
				// check for values outside the bounding box
14846
				switch ($cmd) {
14847
					case 'm':
14848
					case 'l':
14849
					case 'L': {
14850
						// skip values outside bounding box
14851
						foreach ($chunks as $key => $val) {
14852
							if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
14853
								$skip = true;
14854
							} elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
14855
								$skip = true;
14856
							}
14857
						}
14858
					}
14859
				}
14860
			}
14861
			switch ($cmd) {
14862
				case 'm':
14863
				case 'l':
14864
				case 'v':
14865
				case 'y':
14866
				case 'c':
14867
				case 'k':
14868
				case 'K':
14869
				case 'g':
14870
				case 'G':
14871
				case 's':
14872
				case 'S':
14873
				case 'J':
14874
				case 'j':
14875
				case 'w':
14876
				case 'M':
14877
				case 'd':
14878
				case 'n': {
14879
					if ($skip) {
14880
						break;
14881
					}
14882
					$this->_out($line);
14883
					break;
14884
				}
14885
				case 'x': {// custom fill color
14886
					if (empty($color_name)) {
14887
						// CMYK color
14888
						list($col_c, $col_m, $col_y, $col_k) = $chunks;
14889
						$this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
14890
					} else {
14891
						// Spot Color (CMYK + tint)
14892
						list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
14893
						$this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
14894
						$color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
14895
						$this->_out($color_cmd);
14896
					}
14897
					break;
14898
				}
14899
				case 'X': { // custom stroke color
14900
					if (empty($color_name)) {
14901
						// CMYK color
14902
						list($col_c, $col_m, $col_y, $col_k) = $chunks;
14903
						$this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
14904
					} else {
14905
						// Spot Color (CMYK + tint)
14906
						list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
14907
						$this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
14908
						$color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
14909
						$this->_out($color_cmd);
14910
					}
14911
					break;
14912
				}
14913
				case 'Y':
14914
				case 'N':
14915
				case 'V':
14916
				case 'L':
14917
				case 'C': {
14918
					if ($skip) {
14919
						break;
14920
					}
14921
					$line[($len - 1)] = strtolower($cmd);
14922
					$this->_out($line);
14923
					break;
14924
				}
14925
				case 'b':
14926
				case 'B': {
14927
					$this->_out($cmd . '*');
14928
					break;
14929
				}
14930
				case 'f':
14931
				case 'F': {
14932
					if ($u > 0) {
14933
						$isU = false;
14934
						$max = min(($i + 5), $cnt);
14935
						for ($j = ($i + 1); $j < $max; ++$j) {
14936
							$isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
14937
						}
14938
						if ($isU) {
14939
							$this->_out('f*');
14940
						}
14941
					} else {
14942
						$this->_out('f*');
14943
					}
14944
					break;
14945
				}
14946
				case '*u': {
14947
					++$u;
14948
					break;
14949
				}
14950
				case '*U': {
14951
					--$u;
14952
					break;
14953
				}
14954
			}
14955
		}
14956
		// restore previous graphic state
14957
		$this->_out($this->epsmarker.'Q');
14958
		if (!empty($border)) {
14959
			$bx = $this->x;
14960
			$by = $this->y;
14961
			$this->x = $ximg;
14962
			if ($this->rtl) {
14963
				$this->x += $w;
14964
			}
14965
			$this->y = $y;
14966
			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
14967
			$this->x = $bx;
14968
			$this->y = $by;
14969
		}
14970
		if ($link) {
14971
			$this->Link($ximg, $y, $w, $h, $link, 0);
14972
		}
14973
		// set pointer to align the next text/objects
14974
		switch($align) {
14975
			case 'T':{
14976
				$this->y = $y;
14977
				$this->x = $this->img_rb_x;
14978
				break;
14979
			}
14980
			case 'M':{
14981
				$this->y = $y + round($h/2);
14982
				$this->x = $this->img_rb_x;
14983
				break;
14984
			}
14985
			case 'B':{
14986
				$this->y = $this->img_rb_y;
14987
				$this->x = $this->img_rb_x;
14988
				break;
14989
			}
14990
			case 'N':{
14991
				$this->SetY($this->img_rb_y);
14992
				break;
14993
			}
14994
			default:{
14995
				break;
14996
			}
14997
		}
14998
		$this->endlinex = $this->img_rb_x;
14999
	}
15000
 
15001
	/**
15002
	 * Set document barcode.
15003
	 * @param $bc (string) barcode
15004
	 * @public
15005
	 */
15006
	public function setBarcode($bc='') {
15007
		$this->barcode = $bc;
15008
	}
15009
 
15010
	/**
15011
	 * Get current barcode.
15012
	 * @return string
15013
	 * @public
15014
	 * @since 4.0.012 (2008-07-24)
15015
	 */
15016
	public function getBarcode() {
15017
		return $this->barcode;
15018
	}
15019
 
15020
	/**
15021
	 * Print a Linear Barcode.
15022
	 * @param $code (string) code to print
15023
	 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15024
	 * @param $x (int) x position in user units (empty string = current x position)
15025
	 * @param $y (int) y position in user units (empty string = current y position)
15026
	 * @param $w (int) width in user units (empty string = remaining page width)
15027
	 * @param $h (int) height in user units (empty string = remaining page height)
15028
	 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
15029
	 * @param $style (array) array of options:<ul>
15030
	 * <li>boolean $style['border'] if true prints a border</li>
15031
	 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15032
	 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15033
	 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15034
	 * <li>array $style['fgcolor'] color array for bars and text</li>
15035
	 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15036
	 * <li>boolean $style['text'] if true prints text below the barcode</li>
15037
	 * <li>string $style['label'] override default label</li>
15038
	 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15039
	 * <li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing.</li>
15040
	 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15041
	 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15042
	 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15043
	 * <li>string $style['fitwidth'] if true reduce the width to fit the barcode width + padding. When this option is enabled the 'stretch' option is automatically disabled.</li>
15044
	 * <li>string $style['cellfitalign'] this option works only when 'fitwidth' is true and 'position' is unset or empty. Set the horizontal position of the containing barcode cell inside the specified rectangle: L = left; C = center; R = right.</li></ul>
15045
	 * @param $align (string) Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
15046
	 * @author Nicola Asuni
15047
	 * @since 3.1.000 (2008-06-09)
15048
	 * @public
15049
	 */
15050
	public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15051
		if (TCPDF_STATIC::empty_string(trim($code))) {
15052
			return;
15053
		}
15054
		require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15055
		// save current graphic settings
15056
		$gvars = $this->getGraphicVars();
15057
		// create new barcode object
15058
		$barcodeobj = new TCPDFBarcode($code, $type);
15059
		$arrcode = $barcodeobj->getBarcodeArray();
15060
		if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] == 0)) {
15061
			$this->Error('Error in 1D barcode string');
15062
		}
15063
		// set default values
15064
		if (!isset($style['position'])) {
15065
			$style['position'] = '';
15066
		} elseif ($style['position'] == 'S') {
15067
			// keep this for backward compatibility
15068
			$style['position'] = '';
15069
			$style['stretch'] = true;
15070
		}
15071
		if (!isset($style['fitwidth'])) {
15072
			if (!isset($style['stretch'])) {
15073
				$style['fitwidth'] = true;
15074
			} else {
15075
				$style['fitwidth'] = false;
15076
			}
15077
		}
15078
		if ($style['fitwidth']) {
15079
			// disable stretch
15080
			$style['stretch'] = false;
15081
		}
15082
		if (!isset($style['stretch'])) {
15083
			if (($w === '') OR ($w <= 0)) {
15084
				$style['stretch'] = false;
15085
			} else {
15086
				$style['stretch'] = true;
15087
			}
15088
		}
15089
		if (!isset($style['fgcolor'])) {
15090
			$style['fgcolor'] = array(0,0,0); // default black
15091
		}
15092
		if (!isset($style['bgcolor'])) {
15093
			$style['bgcolor'] = false; // default transparent
15094
		}
15095
		if (!isset($style['border'])) {
15096
			$style['border'] = false;
15097
		}
15098
		$fontsize = 0;
15099
		if (!isset($style['text'])) {
15100
			$style['text'] = false;
15101
		}
15102
		if ($style['text'] AND isset($style['font'])) {
15103
			if (isset($style['fontsize'])) {
15104
				$fontsize = $style['fontsize'];
15105
			}
15106
			$this->SetFont($style['font'], '', $fontsize);
15107
		}
15108
		if (!isset($style['stretchtext'])) {
15109
			$style['stretchtext'] = 4;
15110
		}
15111
		if ($x === '') {
15112
			$x = $this->x;
15113
		}
15114
		if ($y === '') {
15115
			$y = $this->y;
15116
		}
15117
		// check page for no-write regions and adapt page margins if necessary
15118
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
15119
		if (($w === '') OR ($w <= 0)) {
15120
			if ($this->rtl) {
15121
				$w = $x - $this->lMargin;
15122
			} else {
15123
				$w = $this->w - $this->rMargin - $x;
15124
			}
15125
		}
15126
		// padding
15127
		if (!isset($style['padding'])) {
15128
			$padding = 0;
15129
		} elseif ($style['padding'] === 'auto') {
15130
			$padding = 10 * ($w / ($arrcode['maxw'] + 20));
15131
		} else {
15132
			$padding = floatval($style['padding']);
15133
		}
15134
		// horizontal padding
15135
		if (!isset($style['hpadding'])) {
15136
			$hpadding = $padding;
15137
		} elseif ($style['hpadding'] === 'auto') {
15138
			$hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15139
		} else {
15140
			$hpadding = floatval($style['hpadding']);
15141
		}
15142
		// vertical padding
15143
		if (!isset($style['vpadding'])) {
15144
			$vpadding = $padding;
15145
		} elseif ($style['vpadding'] === 'auto') {
15146
			$vpadding = ($hpadding / 2);
15147
		} else {
15148
			$vpadding = floatval($style['vpadding']);
15149
		}
15150
		// calculate xres (single bar width)
15151
		$max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15152
		if ($style['stretch']) {
15153
			$xres = $max_xres;
15154
		} else {
15155
			if (TCPDF_STATIC::empty_string($xres)) {
15156
				$xres = (0.141 * $this->k); // default bar width = 0.4 mm
15157
			}
15158
			if ($xres > $max_xres) {
15159
				// correct xres to fit on $w
15160
				$xres = $max_xres;
15161
			}
15162
			if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15163
				OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15164
				$hpadding = 10 * $xres;
15165
				if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15166
					$vpadding = ($hpadding / 2);
15167
				}
15168
			}
15169
		}
15170
		if ($style['fitwidth']) {
15171
			$wold = $w;
15172
			$w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15173
			if (isset($style['cellfitalign'])) {
15174
				switch ($style['cellfitalign']) {
15175
					case 'L': {
15176
						if ($this->rtl) {
15177
							$x -= ($wold - $w);
15178
						}
15179
						break;
15180
					}
15181
					case 'R': {
15182
						if (!$this->rtl) {
15183
							$x += ($wold - $w);
15184
						}
15185
						break;
15186
					}
15187
					case 'C': {
15188
						if ($this->rtl) {
15189
							$x -= (($wold - $w) / 2);
15190
						} else {
15191
							$x += (($wold - $w) / 2);
15192
						}
15193
						break;
15194
					}
15195
					default : {
15196
						break;
15197
					}
15198
				}
15199
			}
15200
		}
15201
		$text_height = ($this->cell_height_ratio * $fontsize / $this->k);
15202
		// height
15203
		if (($h === '') OR ($h <= 0)) {
15204
			// set default height
15205
			$h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15206
		}
15207
		$barh = $h - $text_height - (2 * $vpadding);
15208
		if ($barh <=0) {
15209
			// try to reduce font or padding to fit barcode on available height
15210
			if ($text_height > $h) {
15211
				$fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15212
				$text_height = ($this->cell_height_ratio * $fontsize / $this->k);
15213
				$this->SetFont($style['font'], '', $fontsize);
15214
			}
15215
			if ($vpadding > 0) {
15216
				$vpadding = (($h - $text_height) / 4);
15217
			}
15218
			$barh = $h - $text_height - (2 * $vpadding);
15219
		}
15220
		// fit the barcode on available space
15221
		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15222
		// set alignment
15223
		$this->img_rb_y = $y + $h;
15224
		// set alignment
15225
		if ($this->rtl) {
15226
			if ($style['position'] == 'L') {
15227
				$xpos = $this->lMargin;
15228
			} elseif ($style['position'] == 'C') {
15229
				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15230
			} elseif ($style['position'] == 'R') {
15231
				$xpos = $this->w - $this->rMargin - $w;
15232
			} else {
15233
				$xpos = $x - $w;
15234
			}
15235
			$this->img_rb_x = $xpos;
15236
		} else {
15237
			if ($style['position'] == 'L') {
15238
				$xpos = $this->lMargin;
15239
			} elseif ($style['position'] == 'C') {
15240
				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15241
			} elseif ($style['position'] == 'R') {
15242
				$xpos = $this->w - $this->rMargin - $w;
15243
			} else {
15244
				$xpos = $x;
15245
			}
15246
			$this->img_rb_x = $xpos + $w;
15247
		}
15248
		$xpos_rect = $xpos;
15249
		if (!isset($style['align'])) {
15250
			$style['align'] = 'C';
15251
		}
15252
		switch ($style['align']) {
15253
			case 'L': {
15254
				$xpos = $xpos_rect + $hpadding;
15255
				break;
15256
			}
15257
			case 'R': {
15258
				$xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15259
				break;
15260
			}
15261
			case 'C':
15262
			default : {
15263
				$xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15264
				break;
15265
			}
15266
		}
15267
		$xpos_text = $xpos;
15268
		// barcode is always printed in LTR direction
15269
		$tempRTL = $this->rtl;
15270
		$this->rtl = false;
15271
		// print background color
15272
		if ($style['bgcolor']) {
15273
			$this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15274
		} elseif ($style['border']) {
15275
			$this->Rect($xpos_rect, $y, $w, $h, 'D');
15276
		}
15277
		// set foreground color
15278
		$this->SetDrawColorArray($style['fgcolor']);
15279
		$this->SetTextColorArray($style['fgcolor']);
15280
		// print bars
15281
		foreach ($arrcode['bcode'] as $k => $v) {
15282
			$bw = ($v['w'] * $xres);
15283
			if ($v['t']) {
15284
				// draw a vertical bar
15285
				$ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15286
				$this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15287
			}
15288
			$xpos += $bw;
15289
		}
15290
		// print text
15291
		if ($style['text']) {
15292
			if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15293
				$label = $style['label'];
15294
			} else {
15295
				$label = $code;
15296
			}
15297
			$txtwidth = ($arrcode['maxw'] * $xres);
15298
			if ($this->GetStringWidth($label) > $txtwidth) {
15299
				$style['stretchtext'] = 2;
15300
			}
15301
			// print text
15302
			$this->x = $xpos_text;
15303
			$this->y = $y + $vpadding + $barh;
15304
			$cellpadding = $this->cell_padding;
15305
			$this->SetCellPadding(0);
15306
			$this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15307
			$this->cell_padding = $cellpadding;
15308
		}
15309
		// restore original direction
15310
		$this->rtl = $tempRTL;
15311
		// restore previous settings
15312
		$this->setGraphicVars($gvars);
15313
		// set pointer to align the next text/objects
15314
		switch($align) {
15315
			case 'T':{
15316
				$this->y = $y;
15317
				$this->x = $this->img_rb_x;
15318
				break;
15319
			}
15320
			case 'M':{
15321
				$this->y = $y + round($h / 2);
15322
				$this->x = $this->img_rb_x;
15323
				break;
15324
			}
15325
			case 'B':{
15326
				$this->y = $this->img_rb_y;
15327
				$this->x = $this->img_rb_x;
15328
				break;
15329
			}
15330
			case 'N':{
15331
				$this->SetY($this->img_rb_y);
15332
				break;
15333
			}
15334
			default:{
15335
				break;
15336
			}
15337
		}
15338
		$this->endlinex = $this->img_rb_x;
15339
	}
15340
 
15341
	/**
15342
	 * Print 2D Barcode.
15343
	 * @param $code (string) code to print
15344
	 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15345
	 * @param $x (int) x position in user units
15346
	 * @param $y (int) y position in user units
15347
	 * @param $w (int) width in user units
15348
	 * @param $h (int) height in user units
15349
	 * @param $style (array) array of options:<ul>
15350
	 * <li>boolean $style['border'] if true prints a border around the barcode</li>
15351
	 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15352
	 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15353
	 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15354
	 * <li>int $style['module_width'] width of a single module in points</li>
15355
	 * <li>int $style['module_height'] height of a single module in points</li>
15356
	 * <li>array $style['fgcolor'] color array for bars and text</li>
15357
	 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15358
	 * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li><li>$style['module_width'] width of a single module in points</li>
15359
	 * <li>$style['module_height'] height of a single module in points</li></ul>
15360
	 * @param $align (string) Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
15361
	 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
15362
	 * @author Nicola Asuni
15363
	 * @since 4.5.037 (2009-04-07)
15364
	 * @public
15365
	 */
15366
	public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15367
		if (TCPDF_STATIC::empty_string(trim($code))) {
15368
			return;
15369
		}
15370
		require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15371
		// save current graphic settings
15372
		$gvars = $this->getGraphicVars();
15373
		// create new barcode object
15374
		$barcodeobj = new TCPDF2DBarcode($code, $type);
15375
		$arrcode = $barcodeobj->getBarcodeArray();
15376
		if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) {
15377
			$this->Error('Error in 2D barcode string');
15378
		}
15379
		// set default values
15380
		if (!isset($style['position'])) {
15381
			$style['position'] = '';
15382
		}
15383
		if (!isset($style['fgcolor'])) {
15384
			$style['fgcolor'] = array(0,0,0); // default black
15385
		}
15386
		if (!isset($style['bgcolor'])) {
15387
			$style['bgcolor'] = false; // default transparent
15388
		}
15389
		if (!isset($style['border'])) {
15390
			$style['border'] = false;
15391
		}
15392
		// padding
15393
		if (!isset($style['padding'])) {
15394
			$style['padding'] = 0;
15395
		} elseif ($style['padding'] === 'auto') {
15396
			$style['padding'] = 4;
15397
		}
15398
		if (!isset($style['hpadding'])) {
15399
			$style['hpadding'] = $style['padding'];
15400
		} elseif ($style['hpadding'] === 'auto') {
15401
			$style['hpadding'] = 4;
15402
		}
15403
		if (!isset($style['vpadding'])) {
15404
			$style['vpadding'] = $style['padding'];
15405
		} elseif ($style['vpadding'] === 'auto') {
15406
			$style['vpadding'] = 4;
15407
		}
15408
		$hpad = (2 * $style['hpadding']);
15409
		$vpad = (2 * $style['vpadding']);
15410
		// cell (module) dimension
15411
		if (!isset($style['module_width'])) {
15412
			$style['module_width'] = 1; // width of a single module in points
15413
		}
15414
		if (!isset($style['module_height'])) {
15415
			$style['module_height'] = 1; // height of a single module in points
15416
		}
15417
		if ($x === '') {
15418
			$x = $this->x;
15419
		}
15420
		if ($y === '') {
15421
			$y = $this->y;
15422
		}
15423
		// check page for no-write regions and adapt page margins if necessary
15424
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
15425
		// number of barcode columns and rows
15426
		$rows = $arrcode['num_rows'];
15427
		$cols = $arrcode['num_cols'];
15428
		// module width and height
15429
		$mw = $style['module_width'];
15430
		$mh = $style['module_height'];
15431
		if (($mw == 0) OR ($mh == 0)) {
15432
			$this->Error('Error in 2D barcode string');
15433
		}
15434
		// get max dimensions
15435
		if ($this->rtl) {
15436
			$maxw = $x - $this->lMargin;
15437
		} else {
15438
			$maxw = $this->w - $this->rMargin - $x;
15439
		}
15440
		$maxh = ($this->h - $this->tMargin - $this->bMargin);
15441
		$ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15442
		$ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15443
		if (!$distort) {
15444
			if (($maxw * $ratioHW) > $maxh) {
15445
				$maxw = $maxh * $ratioWH;
15446
			}
15447
			if (($maxh * $ratioWH) > $maxw) {
15448
				$maxh = $maxw * $ratioHW;
15449
			}
15450
		}
15451
		// set maximum dimesions
15452
		if ($w > $maxw) {
15453
			$w = $maxw;
15454
		}
15455
		if ($h > $maxh) {
15456
			$h = $maxh;
15457
		}
15458
		// set dimensions
15459
		if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15460
			$w = ($cols + $hpad) * ($mw / $this->k);
15461
			$h = ($rows + $vpad) * ($mh / $this->k);
15462
		} elseif (($w === '') OR ($w <= 0)) {
15463
			$w = $h * $ratioWH;
15464
		} elseif (($h === '') OR ($h <= 0)) {
15465
			$h = $w * $ratioHW;
15466
		}
15467
		// barcode size (excluding padding)
15468
		$bw = ($w * $cols) / ($cols + $hpad);
15469
		$bh = ($h * $rows) / ($rows + $vpad);
15470
		// dimension of single barcode cell unit
15471
		$cw = $bw / $cols;
15472
		$ch = $bh / $rows;
15473
		if (!$distort) {
15474
			if (($cw / $ch) > ($mw / $mh)) {
15475
				// correct horizontal distortion
15476
				$cw = $ch * $mw / $mh;
15477
				$bw = $cw * $cols;
15478
				$style['hpadding'] = ($w - $bw) / (2 * $cw);
15479
			} else {
15480
				// correct vertical distortion
15481
				$ch = $cw * $mh / $mw;
15482
				$bh = $ch * $rows;
15483
				$style['vpadding'] = ($h - $bh) / (2 * $ch);
15484
			}
15485
		}
15486
		// fit the barcode on available space
15487
		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15488
		// set alignment
15489
		$this->img_rb_y = $y + $h;
15490
		// set alignment
15491
		if ($this->rtl) {
15492
			if ($style['position'] == 'L') {
15493
				$xpos = $this->lMargin;
15494
			} elseif ($style['position'] == 'C') {
15495
				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15496
			} elseif ($style['position'] == 'R') {
15497
				$xpos = $this->w - $this->rMargin - $w;
15498
			} else {
15499
				$xpos = $x - $w;
15500
			}
15501
			$this->img_rb_x = $xpos;
15502
		} else {
15503
			if ($style['position'] == 'L') {
15504
				$xpos = $this->lMargin;
15505
			} elseif ($style['position'] == 'C') {
15506
				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15507
			} elseif ($style['position'] == 'R') {
15508
				$xpos = $this->w - $this->rMargin - $w;
15509
			} else {
15510
				$xpos = $x;
15511
			}
15512
			$this->img_rb_x = $xpos + $w;
15513
		}
15514
		$xstart = $xpos + ($style['hpadding'] * $cw);
15515
		$ystart = $y + ($style['vpadding'] * $ch);
15516
		// barcode is always printed in LTR direction
15517
		$tempRTL = $this->rtl;
15518
		$this->rtl = false;
15519
		// print background color
15520
		if ($style['bgcolor']) {
15521
			$this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15522
		} elseif ($style['border']) {
15523
			$this->Rect($xpos, $y, $w, $h, 'D');
15524
		}
15525
		// set foreground color
15526
		$this->SetDrawColorArray($style['fgcolor']);
15527
		// print barcode cells
15528
		// for each row
15529
		for ($r = 0; $r < $rows; ++$r) {
15530
			$xr = $xstart;
15531
			// for each column
15532
			for ($c = 0; $c < $cols; ++$c) {
15533
				if ($arrcode['bcode'][$r][$c] == 1) {
15534
					// draw a single barcode cell
15535
					$this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15536
				}
15537
				$xr += $cw;
15538
			}
15539
			$ystart += $ch;
15540
		}
15541
		// restore original direction
15542
		$this->rtl = $tempRTL;
15543
		// restore previous settings
15544
		$this->setGraphicVars($gvars);
15545
		// set pointer to align the next text/objects
15546
		switch($align) {
15547
			case 'T':{
15548
				$this->y = $y;
15549
				$this->x = $this->img_rb_x;
15550
				break;
15551
			}
15552
			case 'M':{
15553
				$this->y = $y + round($h/2);
15554
				$this->x = $this->img_rb_x;
15555
				break;
15556
			}
15557
			case 'B':{
15558
				$this->y = $this->img_rb_y;
15559
				$this->x = $this->img_rb_x;
15560
				break;
15561
			}
15562
			case 'N':{
15563
				$this->SetY($this->img_rb_y);
15564
				break;
15565
			}
15566
			default:{
15567
				break;
15568
			}
15569
		}
15570
		$this->endlinex = $this->img_rb_x;
15571
	}
15572
 
15573
	/**
15574
	 * Returns an array containing current margins:
15575
	 * <ul>
15576
			<li>$ret['left'] = left margin</li>
15577
			<li>$ret['right'] = right margin</li>
15578
			<li>$ret['top'] = top margin</li>
15579
			<li>$ret['bottom'] = bottom margin</li>
15580
			<li>$ret['header'] = header margin</li>
15581
			<li>$ret['footer'] = footer margin</li>
15582
			<li>$ret['cell'] = cell padding array</li>
15583
			<li>$ret['padding_left'] = cell left padding</li>
15584
			<li>$ret['padding_top'] = cell top padding</li>
15585
			<li>$ret['padding_right'] = cell right padding</li>
15586
			<li>$ret['padding_bottom'] = cell bottom padding</li>
15587
	 * </ul>
15588
	 * @return array containing all margins measures
15589
	 * @public
15590
	 * @since 3.2.000 (2008-06-23)
15591
	 */
15592
	public function getMargins() {
15593
		$ret = array(
15594
			'left' => $this->lMargin,
15595
			'right' => $this->rMargin,
15596
			'top' => $this->tMargin,
15597
			'bottom' => $this->bMargin,
15598
			'header' => $this->header_margin,
15599
			'footer' => $this->footer_margin,
15600
			'cell' => $this->cell_padding,
15601
			'padding_left' => $this->cell_padding['L'],
15602
			'padding_top' => $this->cell_padding['T'],
15603
			'padding_right' => $this->cell_padding['R'],
15604
			'padding_bottom' => $this->cell_padding['B']
15605
		);
15606
		return $ret;
15607
	}
15608
 
15609
	/**
15610
	 * Returns an array containing original margins:
15611
	 * <ul>
15612
			<li>$ret['left'] = left margin</li>
15613
			<li>$ret['right'] = right margin</li>
15614
	 * </ul>
15615
	 * @return array containing all margins measures
15616
	 * @public
15617
	 * @since 4.0.012 (2008-07-24)
15618
	 */
15619
	public function getOriginalMargins() {
15620
		$ret = array(
15621
			'left' => $this->original_lMargin,
15622
			'right' => $this->original_rMargin
15623
		);
15624
		return $ret;
15625
	}
15626
 
15627
	/**
15628
	 * Returns the current font size.
15629
	 * @return current font size
15630
	 * @public
15631
	 * @since 3.2.000 (2008-06-23)
15632
	 */
15633
	public function getFontSize() {
15634
		return $this->FontSize;
15635
	}
15636
 
15637
	/**
15638
	 * Returns the current font size in points unit.
15639
	 * @return current font size in points unit
15640
	 * @public
15641
	 * @since 3.2.000 (2008-06-23)
15642
	 */
15643
	public function getFontSizePt() {
15644
		return $this->FontSizePt;
15645
	}
15646
 
15647
	/**
15648
	 * Returns the current font family name.
15649
	 * @return string current font family name
15650
	 * @public
15651
	 * @since 4.3.008 (2008-12-05)
15652
	 */
15653
	public function getFontFamily() {
15654
		return $this->FontFamily;
15655
	}
15656
 
15657
	/**
15658
	 * Returns the current font style.
15659
	 * @return string current font style
15660
	 * @public
15661
	 * @since 4.3.008 (2008-12-05)
15662
	 */
15663
	public function getFontStyle() {
15664
		return $this->FontStyle;
15665
	}
15666
 
15667
	/**
15668
	 * Cleanup HTML code (requires HTML Tidy library).
15669
	 * @param $html (string) htmlcode to fix
15670
	 * @param $default_css (string) CSS commands to add
15671
	 * @param $tagvs (array) parameters for setHtmlVSpace method
15672
	 * @param $tidy_options (array) options for tidy_parse_string function
15673
	 * @return string XHTML code cleaned up
15674
	 * @author Nicola Asuni
15675
	 * @public
15676
	 * @since 5.9.017 (2010-11-16)
15677
	 * @see setHtmlVSpace()
15678
	 */
15679
	public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15680
		return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15681
	}
15682
 
15683
	/**
15684
	 * Returns the border width from CSS property
15685
	 * @param $width (string) border width
15686
	 * @return int with in user units
15687
	 * @protected
15688
	 * @since 5.7.000 (2010-08-02)
15689
	 */
15690
	protected function getCSSBorderWidth($width) {
15691
		if ($width == 'thin') {
15692
			$width = (2 / $this->k);
15693
		} elseif ($width == 'medium') {
15694
			$width = (4 / $this->k);
15695
		} elseif ($width == 'thick') {
15696
			$width = (6 / $this->k);
15697
		} else {
15698
			$width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15699
		}
15700
		return $width;
15701
	}
15702
 
15703
	/**
15704
	 * Returns the border dash style from CSS property
15705
	 * @param $style (string) border style to convert
15706
	 * @return int sash style (return -1 in case of none or hidden border)
15707
	 * @protected
15708
	 * @since 5.7.000 (2010-08-02)
15709
	 */
15710
	protected function getCSSBorderDashStyle($style) {
15711
		switch (strtolower($style)) {
15712
			case 'none':
15713
			case 'hidden': {
15714
				$dash = -1;
15715
				break;
15716
			}
15717
			case 'dotted': {
15718
				$dash = 1;
15719
				break;
15720
			}
15721
			case 'dashed': {
15722
				$dash = 3;
15723
				break;
15724
			}
15725
			case 'double':
15726
			case 'groove':
15727
			case 'ridge':
15728
			case 'inset':
15729
			case 'outset':
15730
			case 'solid':
15731
			default: {
15732
				$dash = 0;
15733
				break;
15734
			}
15735
		}
15736
		return $dash;
15737
	}
15738
 
15739
	/**
15740
	 * Returns the border style array from CSS border properties
15741
	 * @param $cssborder (string) border properties
15742
	 * @return array containing border properties
15743
	 * @protected
15744
	 * @since 5.7.000 (2010-08-02)
15745
	 */
15746
	protected function getCSSBorderStyle($cssborder) {
15747
		$bprop = preg_split('/[\s]+/', trim($cssborder));
15748
		$border = array(); // value to be returned
15749
		switch (count($bprop)) {
15750
			case 3: {
15751
				$width = $bprop[0];
15752
				$style = $bprop[1];
15753
				$color = $bprop[2];
15754
				break;
15755
			}
15756
			case 2: {
15757
				$width = 'medium';
15758
				$style = $bprop[0];
15759
				$color = $bprop[1];
15760
				break;
15761
			}
15762
			case 1: {
15763
				$width = 'medium';
15764
				$style = $bprop[0];
15765
				$color = 'black';
15766
				break;
15767
			}
15768
			default: {
15769
				$width = 'medium';
15770
				$style = 'solid';
15771
				$color = 'black';
15772
				break;
15773
			}
15774
		}
15775
		if ($style == 'none') {
15776
			return array();
15777
		}
15778
		$border['cap'] = 'square';
15779
		$border['join'] = 'miter';
15780
		$border['dash'] = $this->getCSSBorderDashStyle($style);
15781
		if ($border['dash'] < 0) {
15782
			return array();
15783
		}
15784
		$border['width'] = $this->getCSSBorderWidth($width);
15785
		$border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15786
		return $border;
15787
	}
15788
 
15789
	/**
15790
	 * Get the internal Cell padding from CSS attribute.
15791
	 * @param $csspadding (string) padding properties
15792
	 * @param $width (float) width of the containing element
15793
	 * @return array of cell paddings
15794
	 * @public
15795
	 * @since 5.9.000 (2010-10-04)
15796
	 */
15797
	public function getCSSPadding($csspadding, $width=0) {
15798
		$padding = preg_split('/[\s]+/', trim($csspadding));
15799
		$cell_padding = array(); // value to be returned
15800
		switch (count($padding)) {
15801
			case 4: {
15802
				$cell_padding['T'] = $padding[0];
15803
				$cell_padding['R'] = $padding[1];
15804
				$cell_padding['B'] = $padding[2];
15805
				$cell_padding['L'] = $padding[3];
15806
				break;
15807
			}
15808
			case 3: {
15809
				$cell_padding['T'] = $padding[0];
15810
				$cell_padding['R'] = $padding[1];
15811
				$cell_padding['B'] = $padding[2];
15812
				$cell_padding['L'] = $padding[1];
15813
				break;
15814
			}
15815
			case 2: {
15816
				$cell_padding['T'] = $padding[0];
15817
				$cell_padding['R'] = $padding[1];
15818
				$cell_padding['B'] = $padding[0];
15819
				$cell_padding['L'] = $padding[1];
15820
				break;
15821
			}
15822
			case 1: {
15823
				$cell_padding['T'] = $padding[0];
15824
				$cell_padding['R'] = $padding[0];
15825
				$cell_padding['B'] = $padding[0];
15826
				$cell_padding['L'] = $padding[0];
15827
				break;
15828
			}
15829
			default: {
15830
				return $this->cell_padding;
15831
			}
15832
		}
15833
		if ($width == 0) {
15834
			$width = $this->w - $this->lMargin - $this->rMargin;
15835
		}
15836
		$cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15837
		$cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15838
		$cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15839
		$cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15840
		return $cell_padding;
15841
	}
15842
 
15843
	/**
15844
	 * Get the internal Cell margin from CSS attribute.
15845
	 * @param $cssmargin (string) margin properties
15846
	 * @param $width (float) width of the containing element
15847
	 * @return array of cell margins
15848
	 * @public
15849
	 * @since 5.9.000 (2010-10-04)
15850
	 */
15851
	public function getCSSMargin($cssmargin, $width=0) {
15852
		$margin = preg_split('/[\s]+/', trim($cssmargin));
15853
		$cell_margin = array(); // value to be returned
15854
		switch (count($margin)) {
15855
			case 4: {
15856
				$cell_margin['T'] = $margin[0];
15857
				$cell_margin['R'] = $margin[1];
15858
				$cell_margin['B'] = $margin[2];
15859
				$cell_margin['L'] = $margin[3];
15860
				break;
15861
			}
15862
			case 3: {
15863
				$cell_margin['T'] = $margin[0];
15864
				$cell_margin['R'] = $margin[1];
15865
				$cell_margin['B'] = $margin[2];
15866
				$cell_margin['L'] = $margin[1];
15867
				break;
15868
			}
15869
			case 2: {
15870
				$cell_margin['T'] = $margin[0];
15871
				$cell_margin['R'] = $margin[1];
15872
				$cell_margin['B'] = $margin[0];
15873
				$cell_margin['L'] = $margin[1];
15874
				break;
15875
			}
15876
			case 1: {
15877
				$cell_margin['T'] = $margin[0];
15878
				$cell_margin['R'] = $margin[0];
15879
				$cell_margin['B'] = $margin[0];
15880
				$cell_margin['L'] = $margin[0];
15881
				break;
15882
			}
15883
			default: {
15884
				return $this->cell_margin;
15885
			}
15886
		}
15887
		if ($width == 0) {
15888
			$width = $this->w - $this->lMargin - $this->rMargin;
15889
		}
15890
		$cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
15891
		$cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
15892
		$cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
15893
		$cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
15894
		return $cell_margin;
15895
	}
15896
 
15897
	/**
15898
	 * Get the border-spacing from CSS attribute.
15899
	 * @param $cssbspace (string) border-spacing CSS properties
15900
	 * @param $width (float) width of the containing element
15901
	 * @return array of border spacings
15902
	 * @public
15903
	 * @since 5.9.010 (2010-10-27)
15904
	 */
15905
	public function getCSSBorderMargin($cssbspace, $width=0) {
15906
		$space = preg_split('/[\s]+/', trim($cssbspace));
15907
		$border_spacing = array(); // value to be returned
15908
		switch (count($space)) {
15909
			case 2: {
15910
				$border_spacing['H'] = $space[0];
15911
				$border_spacing['V'] = $space[1];
15912
				break;
15913
			}
15914
			case 1: {
15915
				$border_spacing['H'] = $space[0];
15916
				$border_spacing['V'] = $space[0];
15917
				break;
15918
			}
15919
			default: {
15920
				return array('H' => 0, 'V' => 0);
15921
			}
15922
		}
15923
		if ($width == 0) {
15924
			$width = $this->w - $this->lMargin - $this->rMargin;
15925
		}
15926
		$border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
15927
		$border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
15928
		return $border_spacing;
15929
	}
15930
 
15931
	/**
15932
	 * Returns the letter-spacing value from CSS value
15933
	 * @param $spacing (string) letter-spacing value
15934
	 * @param $parent (float) font spacing (tracking) value of the parent element
15935
	 * @return float quantity to increases or decreases the space between characters in a text.
15936
	 * @protected
15937
	 * @since 5.9.000 (2010-10-02)
15938
	 */
15939
	protected function getCSSFontSpacing($spacing, $parent=0) {
15940
		$val = 0; // value to be returned
15941
		$spacing = trim($spacing);
15942
		switch ($spacing) {
15943
			case 'normal': {
15944
				$val = 0;
15945
				break;
15946
			}
15947
			case 'inherit': {
15948
				if ($parent == 'normal') {
15949
					$val = 0;
15950
				} else {
15951
					$val = $parent;
15952
				}
15953
				break;
15954
			}
15955
			default: {
15956
				$val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
15957
			}
15958
		}
15959
		return $val;
15960
	}
15961
 
15962
	/**
15963
	 * Returns the percentage of font stretching from CSS value
15964
	 * @param $stretch (string) stretch mode
15965
	 * @param $parent (float) stretch value of the parent element
15966
	 * @return float font stretching percentage
15967
	 * @protected
15968
	 * @since 5.9.000 (2010-10-02)
15969
	 */
15970
	protected function getCSSFontStretching($stretch, $parent=100) {
15971
		$val = 100; // value to be returned
15972
		$stretch = trim($stretch);
15973
		switch ($stretch) {
15974
			case 'ultra-condensed': {
15975
				$val = 40;
15976
				break;
15977
			}
15978
			case 'extra-condensed': {
15979
				$val = 55;
15980
				break;
15981
			}
15982
			case 'condensed': {
15983
				$val = 70;
15984
				break;
15985
			}
15986
			case 'semi-condensed': {
15987
				$val = 85;
15988
				break;
15989
			}
15990
			case 'normal': {
15991
				$val = 100;
15992
				break;
15993
			}
15994
			case 'semi-expanded': {
15995
				$val = 115;
15996
				break;
15997
			}
15998
			case 'expanded': {
15999
				$val = 130;
16000
				break;
16001
			}
16002
			case 'extra-expanded': {
16003
				$val = 145;
16004
				break;
16005
			}
16006
			case 'ultra-expanded': {
16007
				$val = 160;
16008
				break;
16009
			}
16010
			case 'wider': {
16011
				$val = ($parent + 10);
16012
				break;
16013
			}
16014
			case 'narrower': {
16015
				$val = ($parent - 10);
16016
				break;
16017
			}
16018
			case 'inherit': {
16019
				if ($parent == 'normal') {
16020
					$val = 100;
16021
				} else {
16022
					$val = $parent;
16023
				}
16024
				break;
16025
			}
16026
			default: {
16027
				$val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16028
			}
16029
		}
16030
		return $val;
16031
	}
16032
 
16033
	/**
16034
	 * Convert HTML string containing font size value to points
16035
	 * @param $val (string) String containing font size value and unit.
16036
	 * @param $refsize (float) Reference font size in points.
16037
	 * @param $parent_size (float) Parent font size in points.
16038
	 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16039
	 * @return float value in points
16040
	 * @public
16041
	 */
16042
	public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16043
		$refsize = TCPDF_FONTS::getFontRefSize($refsize);
16044
		$parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16045
		switch ($val) {
16046
			case 'xx-small': {
16047
				$size = ($refsize - 4);
16048
				break;
16049
			}
16050
			case 'x-small': {
16051
				$size = ($refsize - 3);
16052
				break;
16053
			}
16054
			case 'small': {
16055
				$size = ($refsize - 2);
16056
				break;
16057
			}
16058
			case 'medium': {
16059
				$size = $refsize;
16060
				break;
16061
			}
16062
			case 'large': {
16063
				$size = ($refsize + 2);
16064
				break;
16065
			}
16066
			case 'x-large': {
16067
				$size = ($refsize + 4);
16068
				break;
16069
			}
16070
			case 'xx-large': {
16071
				$size = ($refsize + 6);
16072
				break;
16073
			}
16074
			case 'smaller': {
16075
				$size = ($parent_size - 3);
16076
				break;
16077
			}
16078
			case 'larger': {
16079
				$size = ($parent_size + 3);
16080
				break;
16081
			}
16082
			default: {
16083
				$size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16084
			}
16085
		}
16086
		return $size;
16087
	}
16088
 
16089
	/**
16090
	 * Returns the HTML DOM array.
16091
	 * @param $html (string) html code
16092
	 * @return array
16093
	 * @protected
16094
	 * @since 3.2.000 (2008-06-20)
16095
	 */
16096
	protected function getHtmlDomArray($html) {
16097
		// array of CSS styles ( selector => properties).
16098
		$css = array();
16099
		// get CSS array defined at previous call
16100
		$matches = array();
16101
		if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16102
			if (isset($matches[1][0])) {
16103
				$css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
16104
			}
16105
			$html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16106
		}
16107
		// extract external CSS files
16108
		$matches = array();
16109
		if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
16110
			foreach ($matches[1] as $key => $link) {
16111
				$type = array();
16112
				if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16113
					$type = array();
16114
					preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16115
					// get 'all' and 'print' media, other media types are discarded
16116
					// (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16117
					if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16118
						$type = array();
16119
						if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16120
							// read CSS data file
16121
							$cssdata = file_get_contents(trim($type[1]));
16122
							$css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16123
						}
16124
					}
16125
				}
16126
			}
16127
		}
16128
		// extract style tags
16129
		$matches = array();
16130
		if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
16131
			foreach ($matches[1] as $key => $media) {
16132
				$type = array();
16133
				preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16134
				// get 'all' and 'print' media, other media types are discarded
16135
				// (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16136
				if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16137
					$cssdata = $matches[2][$key];
16138
					$css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16139
				}
16140
			}
16141
		}
16142
		// create a special tag to contain the CSS array (used for table content)
16143
		$csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
16144
		// remove head and style blocks
16145
		$html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
16146
		$html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
16147
		// define block tags
16148
		$blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16149
		// define self-closing tags
16150
		$selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16151
		// remove all unsupported tags (the line below lists all supported tags)
16152
		$html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
16153
		//replace some blank characters
16154
		$html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16155
		$html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
16156
		$html = preg_replace('@(\r\n|\r)@', "\n", $html);
16157
		$repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16158
		$html = strtr($html, $repTable);
16159
		$offset = 0;
16160
		while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16161
			$html_a = substr($html, 0, $offset);
16162
			$html_b = substr($html, $offset, ($pos - $offset + 6));
16163
			while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16164
				// preserve newlines on <pre> tag
16165
				$html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16166
			}
16167
			while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16168
				// preserve spaces on <pre> tag
16169
				$html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16170
			}
16171
			$html = $html_a.$html_b.substr($html, $pos + 6);
16172
			$offset = strlen($html_a.$html_b);
16173
		}
16174
		$offset = 0;
16175
		while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16176
			$html_a = substr($html, 0, $offset);
16177
			$html_b = substr($html, $offset, ($pos - $offset + 11));
16178
			while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16179
				// preserve newlines on <textarea> tag
16180
				$html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16181
				$html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16182
			}
16183
			$html = $html_a.$html_b.substr($html, $pos + 11);
16184
			$offset = strlen($html_a.$html_b);
16185
		}
16186
		$html = preg_replace('/([\s]*)<option/si', '<option', $html);
16187
		$html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16188
		$offset = 0;
16189
		while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16190
			$html_a = substr($html, 0, $offset);
16191
			$html_b = substr($html, $offset, ($pos - $offset + 9));
16192
			while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
16193
				$html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16194
				$html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16195
			}
16196
			$html = $html_a.$html_b.substr($html, $pos + 9);
16197
			$offset = strlen($html_a.$html_b);
16198
		}
16199
		if (preg_match("'</select'si", $html)) {
16200
			$html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
16201
			$html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16202
		}
16203
		$html = str_replace("\n", ' ', $html);
16204
		// restore textarea newlines
16205
		$html = str_replace('<TBR>', "\n", $html);
16206
		// remove extra spaces from code
16207
		$html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16208
		$html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16209
		$html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16210
		$html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16211
		$html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
16212
		$html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16213
		$html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16214
		$html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16215
		$html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1>&nbsp;\\2', $html);
16216
		$html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16217
		$html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16218
		$html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16219
		$html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16220
		$html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16221
		$html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16222
		$html = preg_replace('/[\s]<\/([^\>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16223
		$html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16224
		$html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16225
		$html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16226
		// trim string
16227
		$html = $this->stringTrim($html);
16228
		// fix first image tag alignment
16229
		$html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16230
		// pattern for generic tag
16231
		$tagpattern = '/(<[^>]+>)/';
16232
		// explodes the string
16233
		$a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16234
		// count elements
16235
		$maxel = count($a);
16236
		$elkey = 0;
16237
		$key = 0;
16238
		// create an array of elements
16239
		$dom = array();
16240
		$dom[$key] = array();
16241
		// set inheritable properties fot the first void element
16242
		// possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
16243
		$dom[$key]['tag'] = false;
16244
		$dom[$key]['block'] = false;
16245
		$dom[$key]['value'] = '';
16246
		$dom[$key]['parent'] = 0;
16247
		$dom[$key]['hide'] = false;
16248
		$dom[$key]['fontname'] = $this->FontFamily;
16249
		$dom[$key]['fontstyle'] = $this->FontStyle;
16250
		$dom[$key]['fontsize'] = $this->FontSizePt;
16251
		$dom[$key]['font-stretch'] = $this->font_stretching;
16252
		$dom[$key]['letter-spacing'] = $this->font_spacing;
16253
		$dom[$key]['stroke'] = $this->textstrokewidth;
16254
		$dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16255
		$dom[$key]['clip'] = ($this->textrendermode > 3);
16256
		$dom[$key]['line-height'] = $this->cell_height_ratio;
16257
		$dom[$key]['bgcolor'] = false;
16258
		$dom[$key]['fgcolor'] = $this->fgcolor; // color
16259
		$dom[$key]['strokecolor'] = $this->strokecolor;
16260
		$dom[$key]['align'] = '';
16261
		$dom[$key]['listtype'] = '';
16262
		$dom[$key]['text-indent'] = 0;
16263
		$dom[$key]['border'] = array();
16264
		$dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16265
		$thead = false; // true when we are inside the THEAD tag
16266
		++$key;
16267
		$level = array();
16268
		array_push($level, 0); // root
16269
		while ($elkey < $maxel) {
16270
			$dom[$key] = array();
16271
			$element = $a[$elkey];
16272
			$dom[$key]['elkey'] = $elkey;
16273
			if (preg_match($tagpattern, $element)) {
16274
				// html tag
16275
				$element = substr($element, 1, -1);
16276
				// get tag name
16277
				preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16278
				$tagname = strtolower($tag[1]);
16279
				// check if we are inside a table header
16280
				if ($tagname == 'thead') {
16281
					if ($element{0} == '/') {
16282
						$thead = false;
16283
					} else {
16284
						$thead = true;
16285
					}
16286
					++$elkey;
16287
					continue;
16288
				}
16289
				$dom[$key]['tag'] = true;
16290
				$dom[$key]['value'] = $tagname;
16291
				if (in_array($dom[$key]['value'], $blocktags)) {
16292
					$dom[$key]['block'] = true;
16293
				} else {
16294
					$dom[$key]['block'] = false;
16295
				}
16296
				if ($element{0} == '/') {
16297
					// *** closing html tag
16298
					$dom[$key]['opening'] = false;
16299
					$dom[$key]['parent'] = end($level);
16300
					array_pop($level);
16301
					$dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16302
					$dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16303
					$dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16304
					$dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16305
					$dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16306
					$dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16307
					$dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16308
					$dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16309
					$dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16310
					$dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16311
					$dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16312
					$dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16313
					$dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16314
					$dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16315
					$dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16316
					if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16317
						$dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16318
					}
16319
					// set the number of columns in table tag
16320
					if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16321
						$dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16322
					}
16323
					if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16324
						$dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16325
						for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16326
							$dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
16327
						}
16328
						$key = $i;
16329
						// mark nested tables
16330
						$dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16331
						// remove thead sections from nested tables
16332
						$dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16333
						$dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16334
					}
16335
					// store header rows on a new table
16336
					if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16337
						if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16338
							$dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16339
						}
16340
						for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16341
							$dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16342
						}
16343
						if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16344
							$dom[($dom[$key]['parent'])]['attribute'] = array();
16345
						}
16346
						// header elements must be always contained in a single page
16347
						$dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16348
					}
16349
					if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16350
						// remove the nobr attributes from the table header
16351
						$dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16352
						$dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16353
					}
16354
				} else {
16355
					// *** opening or self-closing html tag
16356
					$dom[$key]['opening'] = true;
16357
					$dom[$key]['parent'] = end($level);
16358
					if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16359
						// self-closing tag
16360
						$dom[$key]['self'] = true;
16361
					} else {
16362
						// opening tag
16363
						array_push($level, $key);
16364
						$dom[$key]['self'] = false;
16365
					}
16366
					// copy some values from parent
16367
					$parentkey = 0;
16368
					if ($key > 0) {
16369
						$parentkey = $dom[$key]['parent'];
16370
						$dom[$key]['hide'] = $dom[$parentkey]['hide'];
16371
						$dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16372
						$dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16373
						$dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16374
						$dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16375
						$dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16376
						$dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16377
						$dom[$key]['fill'] = $dom[$parentkey]['fill'];
16378
						$dom[$key]['clip'] = $dom[$parentkey]['clip'];
16379
						$dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16380
						$dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16381
						$dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16382
						$dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16383
						$dom[$key]['align'] = $dom[$parentkey]['align'];
16384
						$dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16385
						$dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16386
						$dom[$key]['border'] = array();
16387
						$dom[$key]['dir'] = $dom[$parentkey]['dir'];
16388
					}
16389
					// get attributes
16390
					preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16391
					$dom[$key]['attribute'] = array(); // reset attribute array
16392
					while (list($id, $name) = each($attr_array[1])) {
16393
						$dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16394
					}
16395
					if (!empty($css)) {
16396
						// merge CSS style to current style
16397
						list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16398
						$dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16399
					}
16400
					// split style attributes
16401
					if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16402
						// get style attributes
16403
						preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16404
						$dom[$key]['style'] = array(); // reset style attribute array
16405
						while (list($id, $name) = each($style_array[1])) {
16406
							// in case of duplicate attribute the last replace the previous
16407
							$dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16408
						}
16409
						// --- get some style attributes ---
16410
						// text direction
16411
						if (isset($dom[$key]['style']['direction'])) {
16412
							$dom[$key]['dir'] = $dom[$key]['style']['direction'];
16413
						}
16414
						// display
16415
						if (isset($dom[$key]['style']['display'])) {
16416
							$dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16417
						}
16418
						// font family
16419
						if (isset($dom[$key]['style']['font-family'])) {
16420
							$dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16421
						}
16422
						// list-style-type
16423
						if (isset($dom[$key]['style']['list-style-type'])) {
16424
							$dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16425
							if ($dom[$key]['listtype'] == 'inherit') {
16426
								$dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16427
							}
16428
						}
16429
						// text-indent
16430
						if (isset($dom[$key]['style']['text-indent'])) {
16431
							$dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16432
							if ($dom[$key]['text-indent'] == 'inherit') {
16433
								$dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16434
							}
16435
						}
16436
						// font size
16437
						if (isset($dom[$key]['style']['font-size'])) {
16438
							$fsize = trim($dom[$key]['style']['font-size']);
16439
							$dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16440
						}
16441
						// font-stretch
16442
						if (isset($dom[$key]['style']['font-stretch'])) {
16443
							$dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16444
						}
16445
						// letter-spacing
16446
						if (isset($dom[$key]['style']['letter-spacing'])) {
16447
							$dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16448
						}
16449
						// line-height
16450
						if (isset($dom[$key]['style']['line-height'])) {
16451
							$lineheight = trim($dom[$key]['style']['line-height']);
16452
							switch ($lineheight) {
16453
								// A normal line height. This is default
16454
								case 'normal': {
16455
									$dom[$key]['line-height'] = $dom[0]['line-height'];
16456
									break;
16457
								}
16458
								default: {
16459
									if (is_numeric($lineheight)) {
16460
										$lineheight = $lineheight * 100;
16461
									}
16462
									$dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16463
								}
16464
							}
16465
						}
16466
						// font style
16467
						if (isset($dom[$key]['style']['font-weight'])) {
16468
							if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') {
16469
								if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16470
									$dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16471
								}
16472
							} elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') {
16473
								$dom[$key]['fontstyle'] .= 'B';
16474
							}
16475
						}
16476
						if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
16477
							$dom[$key]['fontstyle'] .= 'I';
16478
						}
16479
						// font color
16480
						if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16481
							$dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16482
						} elseif ($dom[$key]['value'] == 'a') {
16483
							$dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16484
						}
16485
						// background color
16486
						if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16487
							$dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16488
						}
16489
						// text-decoration
16490
						if (isset($dom[$key]['style']['text-decoration'])) {
16491
							$decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16492
							foreach ($decors as $dec) {
16493
								$dec = trim($dec);
16494
								if (!TCPDF_STATIC::empty_string($dec)) {
16495
									if ($dec{0} == 'u') {
16496
										// underline
16497
										$dom[$key]['fontstyle'] .= 'U';
16498
									} elseif ($dec{0} == 'l') {
16499
										// line-through
16500
										$dom[$key]['fontstyle'] .= 'D';
16501
									} elseif ($dec{0} == 'o') {
16502
										// overline
16503
										$dom[$key]['fontstyle'] .= 'O';
16504
									}
16505
								}
16506
							}
16507
						} elseif ($dom[$key]['value'] == 'a') {
16508
							$dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16509
						}
16510
						// check for width attribute
16511
						if (isset($dom[$key]['style']['width'])) {
16512
							$dom[$key]['width'] = $dom[$key]['style']['width'];
16513
						}
16514
						// check for height attribute
16515
						if (isset($dom[$key]['style']['height'])) {
16516
							$dom[$key]['height'] = $dom[$key]['style']['height'];
16517
						}
16518
						// check for text alignment
16519
						if (isset($dom[$key]['style']['text-align'])) {
16520
							$dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
16521
						}
16522
						// check for CSS border properties
16523
						if (isset($dom[$key]['style']['border'])) {
16524
							$borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16525
							if (!empty($borderstyle)) {
16526
								$dom[$key]['border']['LTRB'] = $borderstyle;
16527
							}
16528
						}
16529
						if (isset($dom[$key]['style']['border-color'])) {
16530
							$brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16531
							if (isset($brd_colors[3])) {
16532
								$dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16533
							}
16534
							if (isset($brd_colors[1])) {
16535
								$dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16536
							}
16537
							if (isset($brd_colors[0])) {
16538
								$dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16539
							}
16540
							if (isset($brd_colors[2])) {
16541
								$dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16542
							}
16543
						}
16544
						if (isset($dom[$key]['style']['border-width'])) {
16545
							$brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16546
							if (isset($brd_widths[3])) {
16547
								$dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16548
							}
16549
							if (isset($brd_widths[1])) {
16550
								$dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16551
							}
16552
							if (isset($brd_widths[0])) {
16553
								$dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16554
							}
16555
							if (isset($brd_widths[2])) {
16556
								$dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16557
							}
16558
						}
16559
						if (isset($dom[$key]['style']['border-style'])) {
16560
							$brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16561
							if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16562
								$dom[$key]['border']['L']['cap'] = 'square';
16563
								$dom[$key]['border']['L']['join'] = 'miter';
16564
								$dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16565
								if ($dom[$key]['border']['L']['dash'] < 0) {
16566
									$dom[$key]['border']['L'] = array();
16567
								}
16568
							}
16569
							if (isset($brd_styles[1])) {
16570
								$dom[$key]['border']['R']['cap'] = 'square';
16571
								$dom[$key]['border']['R']['join'] = 'miter';
16572
								$dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16573
								if ($dom[$key]['border']['R']['dash'] < 0) {
16574
									$dom[$key]['border']['R'] = array();
16575
								}
16576
							}
16577
							if (isset($brd_styles[0])) {
16578
								$dom[$key]['border']['T']['cap'] = 'square';
16579
								$dom[$key]['border']['T']['join'] = 'miter';
16580
								$dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16581
								if ($dom[$key]['border']['T']['dash'] < 0) {
16582
									$dom[$key]['border']['T'] = array();
16583
								}
16584
							}
16585
							if (isset($brd_styles[2])) {
16586
								$dom[$key]['border']['B']['cap'] = 'square';
16587
								$dom[$key]['border']['B']['join'] = 'miter';
16588
								$dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16589
								if ($dom[$key]['border']['B']['dash'] < 0) {
16590
									$dom[$key]['border']['B'] = array();
16591
								}
16592
							}
16593
						}
16594
						$cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16595
						foreach ($cellside as $bsk => $bsv) {
16596
							if (isset($dom[$key]['style']['border-'.$bsv])) {
16597
								$borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16598
								if (!empty($borderstyle)) {
16599
									$dom[$key]['border'][$bsk] = $borderstyle;
16600
								}
16601
							}
16602
							if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16603
								$dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16604
							}
16605
							if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16606
								$dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16607
							}
16608
							if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16609
								$dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16610
								if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16611
									$dom[$key]['border'][$bsk] = array();
16612
								}
16613
							}
16614
						}
16615
						// check for CSS padding properties
16616
						if (isset($dom[$key]['style']['padding'])) {
16617
							$dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16618
						} else {
16619
							$dom[$key]['padding'] = $this->cell_padding;
16620
						}
16621
						foreach ($cellside as $psk => $psv) {
16622
							if (isset($dom[$key]['style']['padding-'.$psv])) {
16623
								$dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16624
							}
16625
						}
16626
						// check for CSS margin properties
16627
						if (isset($dom[$key]['style']['margin'])) {
16628
							$dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16629
						} else {
16630
							$dom[$key]['margin'] = $this->cell_margin;
16631
						}
16632
						foreach ($cellside as $psk => $psv) {
16633
							if (isset($dom[$key]['style']['margin-'.$psv])) {
16634
								$dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16635
							}
16636
						}
16637
						// check for CSS border-spacing properties
16638
						if (isset($dom[$key]['style']['border-spacing'])) {
16639
							$dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16640
						}
16641
						// page-break-inside
16642
						if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16643
							$dom[$key]['attribute']['nobr'] = 'true';
16644
						}
16645
						// page-break-before
16646
						if (isset($dom[$key]['style']['page-break-before'])) {
16647
							if ($dom[$key]['style']['page-break-before'] == 'always') {
16648
								$dom[$key]['attribute']['pagebreak'] = 'true';
16649
							} elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16650
								$dom[$key]['attribute']['pagebreak'] = 'left';
16651
							} elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16652
								$dom[$key]['attribute']['pagebreak'] = 'right';
16653
							}
16654
						}
16655
						// page-break-after
16656
						if (isset($dom[$key]['style']['page-break-after'])) {
16657
							if ($dom[$key]['style']['page-break-after'] == 'always') {
16658
								$dom[$key]['attribute']['pagebreakafter'] = 'true';
16659
							} elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16660
								$dom[$key]['attribute']['pagebreakafter'] = 'left';
16661
							} elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16662
								$dom[$key]['attribute']['pagebreakafter'] = 'right';
16663
							}
16664
						}
16665
					}
16666
					if (isset($dom[$key]['attribute']['display'])) {
16667
						$dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16668
					}
16669
					if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16670
						$borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16671
						if (!empty($borderstyle)) {
16672
							$dom[$key]['border']['LTRB'] = $borderstyle;
16673
						}
16674
					}
16675
					// check for font tag
16676
					if ($dom[$key]['value'] == 'font') {
16677
						// font family
16678
						if (isset($dom[$key]['attribute']['face'])) {
16679
							$dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16680
						}
16681
						// font size
16682
						if (isset($dom[$key]['attribute']['size'])) {
16683
							if ($key > 0) {
16684
								if ($dom[$key]['attribute']['size']{0} == '+') {
16685
									$dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16686
								} elseif ($dom[$key]['attribute']['size']{0} == '-') {
16687
									$dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16688
								} else {
16689
									$dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16690
								}
16691
							} else {
16692
								$dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16693
							}
16694
						}
16695
					}
16696
					// force natural alignment for lists
16697
					if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16698
						AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16699
						if ($this->rtl) {
16700
							$dom[$key]['align'] = 'R';
16701
						} else {
16702
							$dom[$key]['align'] = 'L';
16703
						}
16704
					}
16705
					if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16706
						if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16707
							$dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16708
						}
16709
					}
16710
					if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16711
						$dom[$key]['fontstyle'] .= 'B';
16712
					}
16713
					if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16714
						$dom[$key]['fontstyle'] .= 'I';
16715
					}
16716
					if ($dom[$key]['value'] == 'u') {
16717
						$dom[$key]['fontstyle'] .= 'U';
16718
					}
16719
					if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16720
						$dom[$key]['fontstyle'] .= 'D';
16721
					}
16722
					if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16723
						$dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16724
					}
16725
					if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16726
						$dom[$key]['fontname'] = $this->default_monospaced_font;
16727
					}
16728
					if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
16729
						// headings h1, h2, h3, h4, h5, h6
16730
						if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16731
							$headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16732
							$dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16733
						}
16734
						if (!isset($dom[$key]['style']['font-weight'])) {
16735
							$dom[$key]['fontstyle'] .= 'B';
16736
						}
16737
					}
16738
					if (($dom[$key]['value'] == 'table')) {
16739
						$dom[$key]['rows'] = 0; // number of rows
16740
						$dom[$key]['trids'] = array(); // IDs of TR elements
16741
						$dom[$key]['thead'] = ''; // table header rows
16742
					}
16743
					if (($dom[$key]['value'] == 'tr')) {
16744
						$dom[$key]['cols'] = 0;
16745
						if ($thead) {
16746
							$dom[$key]['thead'] = true;
16747
							// rows on thead block are printed as a separate table
16748
						} else {
16749
							$dom[$key]['thead'] = false;
16750
							// store the number of rows on table element
16751
							++$dom[($dom[$key]['parent'])]['rows'];
16752
							// store the TR elements IDs on table element
16753
							array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16754
						}
16755
					}
16756
					if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16757
						if (isset($dom[$key]['attribute']['colspan'])) {
16758
							$colspan = intval($dom[$key]['attribute']['colspan']);
16759
						} else {
16760
							$colspan = 1;
16761
						}
16762
						$dom[$key]['attribute']['colspan'] = $colspan;
16763
						$dom[($dom[$key]['parent'])]['cols'] += $colspan;
16764
					}
16765
					// text direction
16766
					if (isset($dom[$key]['attribute']['dir'])) {
16767
						$dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16768
					}
16769
					// set foreground color attribute
16770
					if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16771
						$dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16772
					} elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16773
						$dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16774
					}
16775
					// set background color attribute
16776
					if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16777
						$dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16778
					}
16779
					// set stroke color attribute
16780
					if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16781
						$dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16782
					}
16783
					// check for width attribute
16784
					if (isset($dom[$key]['attribute']['width'])) {
16785
						$dom[$key]['width'] = $dom[$key]['attribute']['width'];
16786
					}
16787
					// check for height attribute
16788
					if (isset($dom[$key]['attribute']['height'])) {
16789
						$dom[$key]['height'] = $dom[$key]['attribute']['height'];
16790
					}
16791
					// check for text alignment
16792
					if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16793
						$dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
16794
					}
16795
					// check for text rendering mode (the following attributes do not exist in HTML)
16796
					if (isset($dom[$key]['attribute']['stroke'])) {
16797
						// font stroke width
16798
						$dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16799
					}
16800
					if (isset($dom[$key]['attribute']['fill'])) {
16801
						// font fill
16802
						if ($dom[$key]['attribute']['fill'] == 'true') {
16803
							$dom[$key]['fill'] = true;
16804
						} else {
16805
							$dom[$key]['fill'] = false;
16806
						}
16807
					}
16808
					if (isset($dom[$key]['attribute']['clip'])) {
16809
						// clipping mode
16810
						if ($dom[$key]['attribute']['clip'] == 'true') {
16811
							$dom[$key]['clip'] = true;
16812
						} else {
16813
							$dom[$key]['clip'] = false;
16814
						}
16815
					}
16816
				} // end opening tag
16817
			} else {
16818
				// text
16819
				$dom[$key]['tag'] = false;
16820
				$dom[$key]['block'] = false;
16821
				//$element = str_replace('&nbsp;', TCPDF_FONTS::unichr(160, $this->isunicode), $element);
16822
				$dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
16823
				$dom[$key]['parent'] = end($level);
16824
				$dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16825
			}
16826
			++$elkey;
16827
			++$key;
16828
		}
16829
		return $dom;
16830
	}
16831
 
16832
	/**
16833
	 * Returns the string used to find spaces
16834
	 * @return string
16835
	 * @protected
16836
	 * @author Nicola Asuni
16837
	 * @since 4.8.024 (2010-01-15)
16838
	 */
16839
	protected function getSpaceString() {
16840
		$spacestr = chr(32);
16841
		if ($this->isUnicodeFont()) {
16842
			$spacestr = chr(0).chr(32);
16843
		}
16844
		return $spacestr;
16845
	}
16846
 
16847
	/**
16848
	 * Serialize an array of parameters to be used with TCPDF tag in HTML code.
16849
	 * @param $pararray (array) parameters array
16850
	 * @return sting containing serialized data
16851
	 * @since 4.9.006 (2010-04-02)
16852
	 * @public
16853
	 * @deprecated
16854
	 */
16855
	public function serializeTCPDFtagParameters($pararray) {
16856
		return TCPDF_STATIC::serializeTCPDFtagParameters($pararray);
16857
	}
16858
 
16859
	/**
16860
	 * Prints a cell (rectangular area) with optional borders, background color and html text string.
16861
	 * The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br />
16862
	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
16863
	 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
16864
	 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
16865
	 * NOTE: all the HTML attributes must be enclosed in double-quote.
16866
	 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
16867
	 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
16868
	 * @param $x (float) upper-left corner X coordinate
16869
	 * @param $y (float) upper-left corner Y coordinate
16870
	 * @param $html (string) html text to print. Default value: empty string.
16871
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
16872
	 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
16873
Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
16874
	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
16875
	 * @param $reseth (boolean) if true reset the last cell height (default true).
16876
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
16877
	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
16878
	 * @see Multicell(), writeHTML()
16879
	 * @public
16880
	 */
16881
	public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
16882
		return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
16883
	}
16884
 
16885
	/**
16886
	 * Allows to preserve some HTML formatting (limited support).<br />
16887
	 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
16888
	 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
16889
	 * NOTE: all the HTML attributes must be enclosed in double-quote.
16890
	 * @param $html (string) text to display
16891
	 * @param $ln (boolean) if true add a new line after text (default = true)
16892
	 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
16893
	 * @param $reseth (boolean) if true reset the last cell height (default false).
16894
	 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
16895
	 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
16896
	 * @public
16897
	 */
16898
	public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
16899
		$gvars = $this->getGraphicVars();
16900
		// store current values
16901
		$prev_cell_margin = $this->cell_margin;
16902
		$prev_cell_padding = $this->cell_padding;
16903
		$prevPage = $this->page;
16904
		$prevlMargin = $this->lMargin;
16905
		$prevrMargin = $this->rMargin;
16906
		$curfontname = $this->FontFamily;
16907
		$curfontstyle = $this->FontStyle;
16908
		$curfontsize = $this->FontSizePt;
16909
		$curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
16910
		$curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
16911
		$curfontstretcing = $this->font_stretching;
16912
		$curfonttracking = $this->font_spacing;
16913
		$this->newline = true;
16914
		$newline = true;
16915
		$startlinepage = $this->page;
16916
		$minstartliney = $this->y;
16917
		$maxbottomliney = 0;
16918
		$startlinex = $this->x;
16919
		$startliney = $this->y;
16920
		$yshift = 0;
16921
		$loop = 0;
16922
		$curpos = 0;
16923
		$this_method_vars = array();
16924
		$undo = false;
16925
		$fontaligned = false;
16926
		$reverse_dir = false; // true when the text direction is reversed
16927
		$this->premode = false;
16928
		if ($this->inxobj) {
16929
			// we are inside an XObject template
16930
			$pask = count($this->xobjects[$this->xobjid]['annotations']);
16931
		} elseif (isset($this->PageAnnots[$this->page])) {
16932
			$pask = count($this->PageAnnots[$this->page]);
16933
		} else {
16934
			$pask = 0;
16935
		}
16936
		if ($this->inxobj) {
16937
			// we are inside an XObject template
16938
			$startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
16939
		} elseif (!$this->InFooter) {
16940
			if (isset($this->footerlen[$this->page])) {
16941
				$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
16942
			} else {
16943
				$this->footerpos[$this->page] = $this->pagelen[$this->page];
16944
			}
16945
			$startlinepos = $this->footerpos[$this->page];
16946
		} else {
16947
			// we are inside the footer
16948
			$startlinepos = $this->pagelen[$this->page];
16949
		}
16950
		$lalign = $align;
16951
		$plalign = $align;
16952
		if ($this->rtl) {
16953
			$w = $this->x - $this->lMargin;
16954
		} else {
16955
			$w = $this->w - $this->rMargin - $this->x;
16956
		}
16957
		$w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
16958
		if ($cell) {
16959
			if ($this->rtl) {
16960
				$this->x -= $this->cell_padding['R'];
16961
				$this->lMargin += $this->cell_padding['R'];
16962
			} else {
16963
				$this->x += $this->cell_padding['L'];
16964
				$this->rMargin += $this->cell_padding['L'];
16965
			}
16966
		}
16967
		if ($this->customlistindent >= 0) {
16968
			$this->listindent = $this->customlistindent;
16969
		} else {
16970
			$this->listindent = $this->GetStringWidth('000000');
16971
		}
16972
		$this->listindentlevel = 0;
16973
		// save previous states
16974
		$prev_cell_height_ratio = $this->cell_height_ratio;
16975
		$prev_listnum = $this->listnum;
16976
		$prev_listordered = $this->listordered;
16977
		$prev_listcount = $this->listcount;
16978
		$prev_lispacer = $this->lispacer;
16979
		$this->listnum = 0;
16980
		$this->listordered = array();
16981
		$this->listcount = array();
16982
		$this->lispacer = '';
16983
		if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
16984
			// reset row height
16985
			$this->resetLastH();
16986
		}
16987
		$dom = $this->getHtmlDomArray($html);
16988
		$maxel = count($dom);
16989
		$key = 0;
16990
		$hidden_node_key = -1;
16991
		while ($key < $maxel) {
16992
			if ($dom[$key]['tag']) {
16993
				if ($dom[$key]['opening']) {
16994
					if (($hidden_node_key <= 0) AND $dom[$key]['hide']) {
16995
						// store the node key
16996
						$hidden_node_key = $key;
16997
					}
16998
				} elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) {
16999
					// we have reached the closing tag of the hidden node
17000
					$hidden_node_key = 0;
17001
				}
17002
			}
17003
			if ($hidden_node_key >= 0) {
17004
				// skip this node
17005
				++$key;
17006
				if ($hidden_node_key == 0) {
17007
					// reset hidden mode
17008
					$hidden_node_key = -1;
17009
				}
17010
				continue;
17011
			}
17012
			if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17013
				// check for pagebreak
17014
				if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17015
					// add a page (or trig AcceptPageBreak() for multicolumn mode)
17016
					$this->checkPageBreak($this->PageBreakTrigger + 1);
17017
					$this->htmlvspace = ($this->PageBreakTrigger + 1);
17018
				}
17019
				if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17020
					OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17021
					// add a page (or trig AcceptPageBreak() for multicolumn mode)
17022
					$this->checkPageBreak($this->PageBreakTrigger + 1);
17023
					$this->htmlvspace = ($this->PageBreakTrigger + 1);
17024
				}
17025
			}
17026
			if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17027
				if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17028
					$dom[$key]['attribute']['nobr'] = false;
17029
				} else {
17030
					// store current object
17031
					$this->startTransaction();
17032
					// save this method vars
17033
					$this_method_vars['html'] = $html;
17034
					$this_method_vars['ln'] = $ln;
17035
					$this_method_vars['fill'] = $fill;
17036
					$this_method_vars['reseth'] = $reseth;
17037
					$this_method_vars['cell'] = $cell;
17038
					$this_method_vars['align'] = $align;
17039
					$this_method_vars['gvars'] = $gvars;
17040
					$this_method_vars['prevPage'] = $prevPage;
17041
					$this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17042
					$this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17043
					$this_method_vars['prevlMargin'] = $prevlMargin;
17044
					$this_method_vars['prevrMargin'] = $prevrMargin;
17045
					$this_method_vars['curfontname'] = $curfontname;
17046
					$this_method_vars['curfontstyle'] = $curfontstyle;
17047
					$this_method_vars['curfontsize'] = $curfontsize;
17048
					$this_method_vars['curfontascent'] = $curfontascent;
17049
					$this_method_vars['curfontdescent'] = $curfontdescent;
17050
					$this_method_vars['curfontstretcing'] = $curfontstretcing;
17051
					$this_method_vars['curfonttracking'] = $curfonttracking;
17052
					$this_method_vars['minstartliney'] = $minstartliney;
17053
					$this_method_vars['maxbottomliney'] = $maxbottomliney;
17054
					$this_method_vars['yshift'] = $yshift;
17055
					$this_method_vars['startlinepage'] = $startlinepage;
17056
					$this_method_vars['startlinepos'] = $startlinepos;
17057
					$this_method_vars['startlinex'] = $startlinex;
17058
					$this_method_vars['startliney'] = $startliney;
17059
					$this_method_vars['newline'] = $newline;
17060
					$this_method_vars['loop'] = $loop;
17061
					$this_method_vars['curpos'] = $curpos;
17062
					$this_method_vars['pask'] = $pask;
17063
					$this_method_vars['lalign'] = $lalign;
17064
					$this_method_vars['plalign'] = $plalign;
17065
					$this_method_vars['w'] = $w;
17066
					$this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17067
					$this_method_vars['prev_listnum'] = $prev_listnum;
17068
					$this_method_vars['prev_listordered'] = $prev_listordered;
17069
					$this_method_vars['prev_listcount'] = $prev_listcount;
17070
					$this_method_vars['prev_lispacer'] = $prev_lispacer;
17071
					$this_method_vars['fontaligned'] = $fontaligned;
17072
					$this_method_vars['key'] = $key;
17073
					$this_method_vars['dom'] = $dom;
17074
				}
17075
			}
17076
			// print THEAD block
17077
			if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17078
				if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17079
					$this->inthead = true;
17080
					// print table header (thead)
17081
					$this->writeHTML($this->thead, false, false, false, false, '');
17082
					// check if we are on a new page or on a new column
17083
					if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17084
						// we are on a new page or on a new column and the total object height is less than the available vertical space.
17085
						// restore previous object
17086
						$this->rollbackTransaction(true);
17087
						// restore previous values
17088
						foreach ($this_method_vars as $vkey => $vval) {
17089
							$$vkey = $vval;
17090
						}
17091
						// disable table header
17092
						$tmp_thead = $this->thead;
17093
						$this->thead = '';
17094
						// add a page (or trig AcceptPageBreak() for multicolumn mode)
17095
						$pre_y = $this->y;
17096
						if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17097
							// fix for multicolumn mode
17098
							$startliney = $this->y;
17099
						}
17100
						$this->start_transaction_page = $this->page;
17101
						$this->start_transaction_y = $this->y;
17102
						// restore table header
17103
						$this->thead = $tmp_thead;
17104
						// fix table border properties
17105
						if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17106
							$tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17107
						} elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17108
							$tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17109
						} else {
17110
							$tmp_cellspacing = 0;
17111
						}
17112
						$dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17113
						$dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17114
						$dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17115
						$xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17116
						$dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17117
						$dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17118
						// print table header (thead)
17119
						$this->writeHTML($this->thead, false, false, false, false, '');
17120
					}
17121
				}
17122
				// move $key index forward to skip THEAD block
17123
				while ( ($key < $maxel) AND (!(
17124
					($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17125
					OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17126
					++$key;
17127
				}
17128
			}
17129
			if ($dom[$key]['tag'] OR ($key == 0)) {
17130
				if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17131
					$dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17132
				}
17133
				// vertically align image in line
17134
				if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17135
					// get image height
17136
					$imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
17137
					$autolinebreak = false;
17138
					if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
17139
						$imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
17140
						if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17141
							AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17142
							OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17143
							// add automatic line break
17144
							$autolinebreak = true;
17145
							$this->Ln('', $cell);
17146
							if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17147
								// go back to evaluate this line break
17148
								--$key;
17149
							}
17150
						}
17151
					}
17152
					if (!$autolinebreak) {
17153
						if ($this->inPageBody()) {
17154
							$pre_y = $this->y;
17155
							// check for page break
17156
							if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17157
								// fix for multicolumn mode
17158
								$startliney = $this->y;
17159
							}
17160
						}
17161
						if ($this->page > $startlinepage) {
17162
							// fix line splitted over two pages
17163
							if (isset($this->footerlen[$startlinepage])) {
17164
								$curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17165
							}
17166
							// line to be moved one page forward
17167
							$pagebuff = $this->getPageBuffer($startlinepage);
17168
							$linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17169
							$tstart = substr($pagebuff, 0, $startlinepos);
17170
							$tend = substr($this->getPageBuffer($startlinepage), $curpos);
17171
							// remove line from previous page
17172
							$this->setPageBuffer($startlinepage, $tstart.''.$tend);
17173
							$pagebuff = $this->getPageBuffer($this->page);
17174
							$tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17175
							$tend = substr($pagebuff, $this->cntmrk[$this->page]);
17176
							// add line start to current page
17177
							$yshift = ($minstartliney - $this->y);
17178
							if ($fontaligned) {
17179
								$yshift += ($curfontsize / $this->k);
17180
							}
17181
							$try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17182
							$this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17183
							// shift the annotations and links
17184
							if (isset($this->PageAnnots[$this->page])) {
17185
								$next_pask = count($this->PageAnnots[$this->page]);
17186
							} else {
17187
								$next_pask = 0;
17188
							}
17189
							if (isset($this->PageAnnots[$startlinepage])) {
17190
								foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17191
									if ($pak >= $pask) {
17192
										$this->PageAnnots[$this->page][] = $pac;
17193
										unset($this->PageAnnots[$startlinepage][$pak]);
17194
										$npak = count($this->PageAnnots[$this->page]) - 1;
17195
										$this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17196
									}
17197
								}
17198
							}
17199
							$pask = $next_pask;
17200
							$startlinepos = $this->cntmrk[$this->page];
17201
							$startlinepage = $this->page;
17202
							$startliney = $this->y;
17203
							$this->newline = false;
17204
						}
17205
						$this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
17206
						$minstartliney = min($this->y, $minstartliney);
17207
						$maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
17208
					}
17209
				} elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17210
					// account for different font size
17211
					$pfontname = $curfontname;
17212
					$pfontstyle = $curfontstyle;
17213
					$pfontsize = $curfontsize;
17214
					$fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
17215
					$fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
17216
					$fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
17217
					$fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17218
					$fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17219
					if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17220
						OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17221
						OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17222
						if (($key < ($maxel - 1)) AND (
17223
								($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17224
								OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17225
								OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
17226
							)) {
17227
							if ($this->page > $startlinepage) {
17228
								// fix lines splitted over two pages
17229
								if (isset($this->footerlen[$startlinepage])) {
17230
									$curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17231
								}
17232
								// line to be moved one page forward
17233
								$pagebuff = $this->getPageBuffer($startlinepage);
17234
								$linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17235
								$tstart = substr($pagebuff, 0, $startlinepos);
17236
								$tend = substr($this->getPageBuffer($startlinepage), $curpos);
17237
								// remove line start from previous page
17238
								$this->setPageBuffer($startlinepage, $tstart.''.$tend);
17239
								$pagebuff = $this->getPageBuffer($this->page);
17240
								$tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17241
								$tend = substr($pagebuff, $this->cntmrk[$this->page]);
17242
								// add line start to current page
17243
								$yshift = ($minstartliney - $this->y);
17244
								$try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17245
								$this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17246
								// shift the annotations and links
17247
								if (isset($this->PageAnnots[$this->page])) {
17248
									$next_pask = count($this->PageAnnots[$this->page]);
17249
								} else {
17250
									$next_pask = 0;
17251
								}
17252
								if (isset($this->PageAnnots[$startlinepage])) {
17253
									foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17254
										if ($pak >= $pask) {
17255
											$this->PageAnnots[$this->page][] = $pac;
17256
											unset($this->PageAnnots[$startlinepage][$pak]);
17257
											$npak = count($this->PageAnnots[$this->page]) - 1;
17258
											$this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17259
										}
17260
									}
17261
								}
17262
								$pask = $next_pask;
17263
								$startlinepos = $this->cntmrk[$this->page];
17264
								$startlinepage = $this->page;
17265
								$startliney = $this->y;
17266
							}
17267
							if (!isset($dom[$key]['line-height'])) {
17268
								$dom[$key]['line-height'] = $this->cell_height_ratio;
17269
							}
17270
							if (!$dom[$key]['block']) {
17271
								if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) {
17272
									$this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17273
								}
17274
								if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17275
									$current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17276
									if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) {
17277
										$minstartliney = min($this->y, $line_align_data[1]);
17278
										$maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]);
17279
									} else {
17280
										$minstartliney = min($this->y, $minstartliney);
17281
										$maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
17282
									}
17283
									$line_align_data = $current_line_align_data;
17284
								}
17285
							}
17286
							$this->cell_height_ratio = $dom[$key]['line-height'];
17287
							$fontaligned = true;
17288
						}
17289
						$this->SetFont($fontname, $fontstyle, $fontsize);
17290
						// reset row height
17291
						$this->resetLastH();
17292
						$curfontname = $fontname;
17293
						$curfontstyle = $fontstyle;
17294
						$curfontsize = $fontsize;
17295
						$curfontascent = $fontascent;
17296
						$curfontdescent = $fontdescent;
17297
					}
17298
				}
17299
				// set text rendering mode
17300
				$textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17301
				$textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17302
				$textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17303
				$this->setTextRenderingMode($textstroke, $textfill, $textclip);
17304
				if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17305
					$this->setFontStretching($dom[$key]['font-stretch']);
17306
				}
17307
				if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17308
					$this->setFontSpacing($dom[$key]['letter-spacing']);
17309
				}
17310
				if (($plalign == 'J') AND $dom[$key]['block']) {
17311
					$plalign = '';
17312
				}
17313
				// get current position on page buffer
17314
				$curpos = $this->pagelen[$startlinepage];
17315
				if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17316
					$this->SetFillColorArray($dom[$key]['bgcolor']);
17317
					$wfill = true;
17318
				} else {
17319
					$wfill = $fill | false;
17320
				}
17321
				if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17322
					$this->SetTextColorArray($dom[$key]['fgcolor']);
17323
				}
17324
				if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17325
					$this->SetDrawColorArray($dom[$key]['strokecolor']);
17326
				}
17327
				if (isset($dom[$key]['align'])) {
17328
					$lalign = $dom[$key]['align'];
17329
				}
17330
				if (TCPDF_STATIC::empty_string($lalign)) {
17331
					$lalign = $align;
17332
				}
17333
			}
17334
			// align lines
17335
			if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17336
				$newline = true;
17337
				$fontaligned = false;
17338
				// we are at the beginning of a new line
17339
				if (isset($startlinex)) {
17340
					$yshift = ($minstartliney - $startliney);
17341
					if (($yshift > 0) OR ($this->page > $startlinepage)) {
17342
						$yshift = 0;
17343
					}
17344
					$t_x = 0;
17345
					// the last line must be shifted to be aligned as requested
17346
					$linew = abs($this->endlinex - $startlinex);
17347
					if ($this->inxobj) {
17348
						// we are inside an XObject template
17349
						$pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17350
						if (isset($opentagpos)) {
17351
							$midpos = $opentagpos;
17352
						} else {
17353
							$midpos = 0;
17354
						}
17355
						if ($midpos > 0) {
17356
							$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17357
							$pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17358
						} else {
17359
							$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17360
							$pend = '';
17361
						}
17362
					} else {
17363
						$pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17364
						if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17365
							$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17366
							$midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17367
						} elseif (isset($opentagpos)) {
17368
							$midpos = $opentagpos;
17369
						} elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17370
							$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17371
							$midpos = $this->footerpos[$startlinepage];
17372
						} else {
17373
							$midpos = 0;
17374
						}
17375
						if ($midpos > 0) {
17376
							$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17377
							$pend = substr($this->getPageBuffer($startlinepage), $midpos);
17378
						} else {
17379
							$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17380
							$pend = '';
17381
						}
17382
					}
17383
					if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17384
						// calculate shifting amount
17385
						$tw = $w;
17386
						if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17387
							$tw += $this->cell_padding['R'];
17388
						}
17389
						if ($this->lMargin != $prevlMargin) {
17390
							$tw += ($prevlMargin - $this->lMargin);
17391
						}
17392
						if ($this->rMargin != $prevrMargin) {
17393
							$tw += ($prevrMargin - $this->rMargin);
17394
						}
17395
						$one_space_width = $this->GetStringWidth(chr(32));
17396
						$no = 0; // number of spaces on a line contained on a single block
17397
						if ($this->isRTLTextDir()) { // RTL
17398
							// remove left space if exist
17399
							$pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17400
							if ($pos1 > 0) {
17401
								$pos1 = intval($pos1);
17402
								if ($this->isUnicodeFont()) {
17403
									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17404
									$spacelen = 2;
17405
								} else {
17406
									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17407
									$spacelen = 1;
17408
								}
17409
								if ($pos1 == $pos2) {
17410
									$pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17411
									if (substr($pmid, $pos1, 4) == '[()]') {
17412
										$linew -= $one_space_width;
17413
									} elseif ($pos1 == strpos($pmid, '[(')) {
17414
										$no = 1;
17415
									}
17416
								}
17417
							}
17418
						} else { // LTR
17419
							// remove right space if exist
17420
							$pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17421
							if ($pos1 > 0) {
17422
								$pos1 = intval($pos1);
17423
								if ($this->isUnicodeFont()) {
17424
									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17425
									$spacelen = 2;
17426
								} else {
17427
									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17428
									$spacelen = 1;
17429
								}
17430
								if ($pos1 == $pos2) {
17431
									$pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17432
									$linew -= $one_space_width;
17433
								}
17434
							}
17435
						}
17436
						$mdiff = ($tw - $linew);
17437
						if ($plalign == 'C') {
17438
							if ($this->rtl) {
17439
								$t_x = -($mdiff / 2);
17440
							} else {
17441
								$t_x = ($mdiff / 2);
17442
							}
17443
						} elseif ($plalign == 'R') {
17444
							// right alignment on LTR document
17445
							$t_x = $mdiff;
17446
						} elseif ($plalign == 'L') {
17447
							// left alignment on RTL document
17448
							$t_x = -$mdiff;
17449
						} elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17450
							// Justification
17451
							if ($this->isRTLTextDir()) {
17452
								// align text on the left
17453
								$t_x = -$mdiff;
17454
							}
17455
							$ns = 0; // number of spaces
17456
							$pmidtemp = $pmid;
17457
							// escape special characters
17458
							$pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17459
							$pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17460
							// search spaces
17461
							if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17462
								$spacestr = $this->getSpaceString();
17463
								$maxkk = count($lnstring[1]) - 1;
17464
								for ($kk=0; $kk <= $maxkk; ++$kk) {
17465
									// restore special characters
17466
									$lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17467
									$lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17468
									// store number of spaces on the strings
17469
									$lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17470
									// count total spaces on line
17471
									$ns += $lnstring[2][$kk];
17472
									$lnstring[3][$kk] = $ns;
17473
								}
17474
								if ($ns == 0) {
17475
									$ns = 1;
17476
								}
17477
								// calculate additional space to add to each existing space
17478
								$spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17479
								$spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17480
								if ($this->font_spacing != 0) {
17481
									// fixed spacing mode
17482
									$osw = -1000 * $this->font_spacing / $this->FontSize;
17483
									$spacewidthu += $osw;
17484
								}
17485
								$nsmax = $ns;
17486
								$ns = 0;
17487
								reset($lnstring);
17488
								$offset = 0;
17489
								$strcount = 0;
17490
								$prev_epsposbeg = 0;
17491
								$textpos = 0;
17492
								if ($this->isRTLTextDir()) {
17493
									$textpos = $this->wPt;
17494
								}
17495
								global $spacew;
17496
								while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17497
									// check if we are inside a string section '[( ... )]'
17498
									$stroffset = strpos($pmid, '[(', $offset);
17499
									if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17500
										// set offset to the end of string section
17501
										$offset = strpos($pmid, ')]', $stroffset);
17502
										while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17503
											$offset = strpos($pmid, ')]', ($offset + 1));
17504
										}
17505
										if ($offset === false) {
17506
											$this->Error('HTML Justification: malformed PDF code.');
17507
										}
17508
										continue;
17509
									}
17510
									if ($this->isRTLTextDir()) {
17511
										$spacew = ($spacewidth * ($nsmax - $ns));
17512
									} else {
17513
										$spacew = ($spacewidth * $ns);
17514
									}
17515
									$offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17516
									$epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17517
									$epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
17518
									if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
17519
										OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
17520
										// shift EPS images
17521
										$trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17522
										$epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17523
										$pmid_b = substr($pmid, 0, $epsposbeg);
17524
										$pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17525
										$pmid_e = substr($pmid, $epsposend);
17526
										$pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17527
										$offset = $epsposend;
17528
										continue;
17529
 
17530
									}
17531
									$prev_epsposbeg = $epsposbeg;
17532
									$currentxpos = 0;
17533
									// shift blocks of code
17534
									switch ($strpiece[2][0]) {
17535
										case 'Td':
17536
										case 'cm':
17537
										case 'm':
17538
										case 'l': {
17539
											// get current X position
17540
											preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17541
											$currentxpos = $xmatches[1];
17542
											$textpos = $currentxpos;
17543
											if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17544
												$ns = $lnstring[3][$strcount];
17545
												if ($this->isRTLTextDir()) {
17546
													$spacew = ($spacewidth * ($nsmax - $ns));
17547
												}
17548
												++$strcount;
17549
											}
17550
											// justify block
17551
											$pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
17552
												create_function('$matches', 'global $spacew;
17553
												$newx = sprintf("%F",(floatval($matches[1]) + $spacew));
17554
												return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
17555
											break;
17556
										}
17557
										case 're': {
17558
											// justify block
17559
											if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17560
												$this->lispacer = '';
17561
												continue;
17562
											}
17563
											preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17564
											$currentxpos = $xmatches[1];
17565
											global $x_diff, $w_diff;
17566
											$x_diff = 0;
17567
											$w_diff = 0;
17568
											if ($this->isRTLTextDir()) { // RTL
17569
												if ($currentxpos < $textpos) {
17570
													$x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17571
													$w_diff = ($spacewidth * $lnstring[2][$strcount]);
17572
												} else {
17573
													if ($strcount > 0) {
17574
														$x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17575
														$w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17576
													}
17577
												}
17578
											} else { // LTR
17579
												if ($currentxpos > $textpos) {
17580
													if ($strcount > 0) {
17581
														$x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17582
													}
17583
													$w_diff = ($spacewidth * $lnstring[2][$strcount]);
17584
												} else {
17585
													if ($strcount > 1) {
17586
														$x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17587
													}
17588
													if ($strcount > 0) {
17589
														$w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17590
													}
17591
												}
17592
											}
17593
											$pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
17594
												create_function('$matches', 'global $x_diff, $w_diff;
17595
												$newx = sprintf("%F",(floatval($matches[1]) + $x_diff));
17596
												$neww = sprintf("%F",(floatval($matches[3]) + $w_diff));
17597
												return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
17598
											break;
17599
										}
17600
										case 'c': {
17601
											// get current X position
17602
											preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches);
17603
											$currentxpos = $xmatches[1];
17604
											// justify block
17605
											$pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x',
17606
												create_function('$matches', 'global $spacew;
17607
												$newx1 = sprintf("%F",(floatval($matches[1]) + $spacew));
17608
												$newx2 = sprintf("%F",(floatval($matches[3]) + $spacew));
17609
												$newx3 = sprintf("%F",(floatval($matches[5]) + $spacew));
17610
												return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
17611
											break;
17612
										}
17613
									}
17614
									// shift the annotations and links
17615
									$cxpos = ($currentxpos / $this->k);
17616
									$lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17617
									if ($this->inxobj) {
17618
										// we are inside an XObject template
17619
										foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17620
											if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17621
												if ($cxpos > $lmpos) {
17622
													$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17623
													$this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17624
												} else {
17625
													$this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17626
												}
17627
												break;
17628
											}
17629
										}
17630
									} elseif (isset($this->PageAnnots[$this->page])) {
17631
										foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17632
											if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17633
												if ($cxpos > $lmpos) {
17634
													$this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17635
													$this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17636
												} else {
17637
													$this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17638
												}
17639
												break;
17640
											}
17641
										}
17642
									}
17643
								} // end of while
17644
								// remove markers
17645
								$pmid = str_replace('x*#!#*x', '', $pmid);
17646
								if ($this->isUnicodeFont()) {
17647
									// multibyte characters
17648
									$spacew = $spacewidthu;
17649
									if ($this->font_stretching != 100) {
17650
										// word spacing is affected by stretching
17651
										$spacew /= ($this->font_stretching / 100);
17652
									}
17653
									$pmidtemp = $pmid;
17654
									// escape special characters
17655
									$pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17656
									$pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17657
									$pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
17658
												create_function('$matches', 'global $spacew;
17659
												$matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
17660
												$matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
17661
												return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
17662
									if ($this->inxobj) {
17663
										// we are inside an XObject template
17664
										$this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17665
									} else {
17666
										$this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17667
									}
17668
									$endlinepos = strlen($pstart."\n".$pmid."\n");
17669
								} else {
17670
									// non-unicode (single-byte characters)
17671
									if ($this->font_stretching != 100) {
17672
										// word spacing (Tw) is affected by stretching
17673
										$spacewidth /= ($this->font_stretching / 100);
17674
									}
17675
									$rs = sprintf('%F Tw', $spacewidth);
17676
									$pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
17677
									if ($this->inxobj) {
17678
										// we are inside an XObject template
17679
										$this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17680
									} else {
17681
										$this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17682
									}
17683
									$endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17684
								}
17685
							}
17686
						} // end of J
17687
					} // end if $startlinex
17688
					if (($t_x != 0) OR ($yshift < 0)) {
17689
						// shift the line
17690
						$trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17691
						$pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17692
						$endlinepos = strlen($pstart);
17693
						if ($this->inxobj) {
17694
							// we are inside an XObject template
17695
							$this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17696
							foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17697
								if ($pak >= $pask) {
17698
									$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17699
									$this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17700
								}
17701
							}
17702
						} else {
17703
							$this->setPageBuffer($startlinepage, $pstart.$pend);
17704
							// shift the annotations and links
17705
							if (isset($this->PageAnnots[$this->page])) {
17706
								foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17707
									if ($pak >= $pask) {
17708
										$this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17709
										$this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17710
									}
17711
								}
17712
							}
17713
						}
17714
						$this->y -= $yshift;
17715
					}
17716
				}
17717
				$pbrk = $this->checkPageBreak($this->lasth);
17718
				$this->newline = false;
17719
				$startlinex = $this->x;
17720
				$startliney = $this->y;
17721
				if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17722
					$startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17723
				} elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17724
					$startliney -= (($this->FontSizePt / 0.7) / $this->k);
17725
				} else {
17726
					$minstartliney = $startliney;
17727
					$maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
17728
				}
17729
				$startlinepage = $this->page;
17730
				if (isset($endlinepos) AND (!$pbrk)) {
17731
					$startlinepos = $endlinepos;
17732
				} else {
17733
					if ($this->inxobj) {
17734
						// we are inside an XObject template
17735
						$startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17736
					} elseif (!$this->InFooter) {
17737
						if (isset($this->footerlen[$this->page])) {
17738
							$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17739
						} else {
17740
							$this->footerpos[$this->page] = $this->pagelen[$this->page];
17741
						}
17742
						$startlinepos = $this->footerpos[$this->page];
17743
					} else {
17744
						$startlinepos = $this->pagelen[$this->page];
17745
					}
17746
				}
17747
				unset($endlinepos);
17748
				$plalign = $lalign;
17749
				if (isset($this->PageAnnots[$this->page])) {
17750
					$pask = count($this->PageAnnots[$this->page]);
17751
				} else {
17752
					$pask = 0;
17753
				}
17754
				if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
17755
					AND (isset($this->emptypagemrk[$this->page]))
17756
					AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
17757
					$this->SetFont($fontname, $fontstyle, $fontsize);
17758
					if ($wfill) {
17759
						$this->SetFillColorArray($this->bgcolor);
17760
					}
17761
				}
17762
			} // end newline
17763
			if (isset($opentagpos)) {
17764
				unset($opentagpos);
17765
			}
17766
			if ($dom[$key]['tag']) {
17767
				if ($dom[$key]['opening']) {
17768
					// get text indentation (if any)
17769
					if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
17770
						$this->textindent = $dom[$key]['text-indent'];
17771
						$this->newline = true;
17772
					}
17773
					// table
17774
					if ($dom[$key]['value'] == 'table') {
17775
						// available page width
17776
						if ($this->rtl) {
17777
							$wtmp = $this->x - $this->lMargin;
17778
						} else {
17779
							$wtmp = $this->w - $this->rMargin - $this->x;
17780
						}
17781
						// get cell spacing
17782
						if (isset($dom[$key]['attribute']['cellspacing'])) {
17783
							$clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
17784
							$cellspacing = array('H' => $clsp, 'V' => $clsp);
17785
						} elseif (isset($dom[$key]['border-spacing'])) {
17786
							$cellspacing = $dom[$key]['border-spacing'];
17787
						} else {
17788
							$cellspacing = array('H' => 0, 'V' => 0);
17789
						}
17790
						// table width
17791
						if (isset($dom[$key]['width'])) {
17792
							$table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
17793
						} else {
17794
							$table_width = $wtmp;
17795
						}
17796
						$table_width -= (2 * $cellspacing['H']);
17797
						if (!$this->inthead) {
17798
							$this->y += $cellspacing['V'];
17799
						}
17800
						if ($this->rtl) {
17801
							$cellspacingx = -$cellspacing['H'];
17802
						} else {
17803
							$cellspacingx = $cellspacing['H'];
17804
						}
17805
						// total table width without cellspaces
17806
						$table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
17807
						// minimum column width
17808
						$table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
17809
						// array of custom column widths
17810
						$table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
17811
					}
17812
					// table row
17813
					if ($dom[$key]['value'] == 'tr') {
17814
						// reset column counter
17815
						$colid = 0;
17816
					}
17817
					// table cell
17818
					if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
17819
						$trid = $dom[$key]['parent'];
17820
						$table_el = $dom[$trid]['parent'];
17821
						if (!isset($dom[$table_el]['cols'])) {
17822
							$dom[$table_el]['cols'] = $dom[$trid]['cols'];
17823
						}
17824
						// store border info
17825
						$tdborder = 0;
17826
						if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
17827
							$tdborder = $dom[$key]['border'];
17828
						}
17829
						$colspan = $dom[$key]['attribute']['colspan'];
17830
						$old_cell_padding = $this->cell_padding;
17831
						if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
17832
							$crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
17833
							$current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
17834
						} elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
17835
							$current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
17836
						} else {
17837
							$current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
17838
						}
17839
						$this->cell_padding = $current_cell_padding;
17840
						if (isset($dom[$key]['height'])) {
17841
							// minimum cell height
17842
							$cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
17843
						} else {
17844
							$cellh = 0;
17845
						}
17846
						if (isset($dom[$key]['content'])) {
17847
							$cell_content = stripslashes($dom[$key]['content']);
17848
						} else {
17849
							$cell_content = '&nbsp;';
17850
						}
17851
						$tagtype = $dom[$key]['value'];
17852
						$parentid = $key;
17853
						while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
17854
							// move $key index forward
17855
							++$key;
17856
						}
17857
						if (!isset($dom[$trid]['startpage'])) {
17858
							$dom[$trid]['startpage'] = $this->page;
17859
						} else {
17860
							$this->setPage($dom[$trid]['startpage']);
17861
						}
17862
						if (!isset($dom[$trid]['startcolumn'])) {
17863
							$dom[$trid]['startcolumn'] = $this->current_column;
17864
						} elseif ($this->current_column != $dom[$trid]['startcolumn']) {
17865
							$tmpx = $this->x;
17866
							$this->selectColumn($dom[$trid]['startcolumn']);
17867
							$this->x = $tmpx;
17868
						}
17869
						if (!isset($dom[$trid]['starty'])) {
17870
							$dom[$trid]['starty'] = $this->y;
17871
						} else {
17872
							$this->y = $dom[$trid]['starty'];
17873
						}
17874
						if (!isset($dom[$trid]['startx'])) {
17875
							$dom[$trid]['startx'] = $this->x;
17876
							$this->x += $cellspacingx;
17877
						} else {
17878
							$this->x += ($cellspacingx / 2);
17879
						}
17880
						if (isset($dom[$parentid]['attribute']['rowspan'])) {
17881
							$rowspan = intval($dom[$parentid]['attribute']['rowspan']);
17882
						} else {
17883
							$rowspan = 1;
17884
						}
17885
						// skip row-spanned cells started on the previous rows
17886
						if (isset($dom[$table_el]['rowspans'])) {
17887
							$rsk = 0;
17888
							$rskmax = count($dom[$table_el]['rowspans']);
17889
							while ($rsk < $rskmax) {
17890
								$trwsp = $dom[$table_el]['rowspans'][$rsk];
17891
								$rsstartx = $trwsp['startx'];
17892
								$rsendx = $trwsp['endx'];
17893
								// account for margin changes
17894
								if ($trwsp['startpage'] < $this->page) {
17895
									if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
17896
										$dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
17897
										$rsstartx -= $dl;
17898
										$rsendx -= $dl;
17899
									} elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
17900
										$dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
17901
										$rsstartx += $dl;
17902
										$rsendx += $dl;
17903
									}
17904
								}
17905
								if (($trwsp['rowspan'] > 0)
17906
									AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
17907
									AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
17908
									AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
17909
									// set the starting X position of the current cell
17910
									$this->x = $rsendx + $cellspacingx;
17911
									// increment column indicator
17912
									$colid += $trwsp['colspan'];
17913
									if (($trwsp['rowspan'] == 1)
17914
										AND (isset($dom[$trid]['endy']))
17915
										AND (isset($dom[$trid]['endpage']))
17916
										AND (isset($dom[$trid]['endcolumn']))
17917
										AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
17918
										AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
17919
										// set ending Y position for row
17920
										$dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
17921
										$dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
17922
									}
17923
									$rsk = 0;
17924
								} else {
17925
									++$rsk;
17926
								}
17927
							}
17928
						}
17929
						if (isset($dom[$parentid]['width'])) {
17930
							// user specified width
17931
							$cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
17932
							$tmpcw = ($cellw / $colspan);
17933
							for ($i = 0; $i < $colspan; ++$i) {
17934
								$table_colwidths[($colid + $i)] = $tmpcw;
17935
							}
17936
						} else {
17937
							// inherit column width
17938
							$cellw = 0;
17939
							for ($i = 0; $i < $colspan; ++$i) {
17940
								$cellw += $table_colwidths[($colid + $i)];
17941
							}
17942
						}
17943
						$cellw += (($colspan - 1) * $cellspacing['H']);
17944
						// increment column indicator
17945
						$colid += $colspan;
17946
						// add rowspan information to table element
17947
						if ($rowspan > 1) {
17948
							$trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y));
17949
						}
17950
						$cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
17951
						if ($rowspan > 1) {
17952
							$dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
17953
						}
17954
						// push background colors
17955
						if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
17956
							$dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
17957
						}
17958
						// store border info
17959
						if (isset($tdborder) AND !empty($tdborder)) {
17960
							$dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
17961
						}
17962
						$prevLastH = $this->lasth;
17963
						// store some info for multicolumn mode
17964
						if ($this->rtl) {
17965
							$this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
17966
						} else {
17967
							$this->colxshift['x'] = $this->x - $this->lMargin;
17968
						}
17969
						$this->colxshift['s'] = $cellspacing;
17970
						$this->colxshift['p'] = $current_cell_padding;
17971
						// ****** write the cell content ******
17972
						$this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
17973
						// restore some values
17974
						$this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
17975
						$this->lasth = $prevLastH;
17976
						$this->cell_padding = $old_cell_padding;
17977
						$dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
17978
						// update the end of row position
17979
						if ($rowspan <= 1) {
17980
							if (isset($dom[$trid]['endy'])) {
17981
								if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
17982
									$dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
17983
								} elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
17984
									$dom[$trid]['endy'] = $this->y;
17985
								}
17986
							} else {
17987
								$dom[$trid]['endy'] = $this->y;
17988
							}
17989
							if (isset($dom[$trid]['endpage'])) {
17990
								$dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
17991
							} else {
17992
								$dom[$trid]['endpage'] = $this->page;
17993
							}
17994
							if (isset($dom[$trid]['endcolumn'])) {
17995
								$dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
17996
							} else {
17997
								$dom[$trid]['endcolumn'] = $this->current_column;
17998
							}
17999
						} else {
18000
							// account for row-spanned cells
18001
							$dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18002
							$dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18003
							$dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18004
							$dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18005
						}
18006
						if (isset($dom[$table_el]['rowspans'])) {
18007
							// update endy and endpage on rowspanned cells
18008
							foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18009
								if ($trwsp['rowspan'] > 0) {
18010
									if (isset($dom[$trid]['endpage'])) {
18011
										if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18012
											$dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18013
										} elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18014
											$dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18015
											$dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18016
											$dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18017
										} else {
18018
											$dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18019
										}
18020
									}
18021
								}
18022
							}
18023
						}
18024
						$this->x += ($cellspacingx / 2);
18025
					} else {
18026
						// opening tag (or self-closing tag)
18027
						if (!isset($opentagpos)) {
18028
							if ($this->inxobj) {
18029
								// we are inside an XObject template
18030
								$opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18031
							} elseif (!$this->InFooter) {
18032
								if (isset($this->footerlen[$this->page])) {
18033
									$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18034
								} else {
18035
									$this->footerpos[$this->page] = $this->pagelen[$this->page];
18036
								}
18037
								$opentagpos = $this->footerpos[$this->page];
18038
							}
18039
						}
18040
						$dom = $this->openHTMLTagHandler($dom, $key, $cell);
18041
					}
18042
				} else { // closing tag
18043
					$prev_numpages = $this->numpages;
18044
					$old_bordermrk = $this->bordermrk[$this->page];
18045
					$dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18046
					if ($this->bordermrk[$this->page] > $old_bordermrk) {
18047
						$startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18048
					}
18049
					if ($prev_numpages > $this->numpages) {
18050
						$startlinepage = $this->page;
18051
					}
18052
				}
18053
			} elseif (strlen($dom[$key]['value']) > 0) {
18054
				// print list-item
18055
				if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18056
					$this->SetFont($pfontname, $pfontstyle, $pfontsize);
18057
					$this->resetLastH();
18058
					$minstartliney = $this->y;
18059
					$maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
18060
					$this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18061
					$this->SetFont($curfontname, $curfontstyle, $curfontsize);
18062
					$this->resetLastH();
18063
					if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18064
						$pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18065
						$pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18066
						$this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18067
						$minstartliney = min($this->y, $minstartliney);
18068
						$maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
18069
					}
18070
				}
18071
				// text
18072
				$this->htmlvspace = 0;
18073
				if ((!$this->premode) AND $this->isRTLTextDir()) {
18074
					// reverse spaces order
18075
					$lsp = ''; // left spaces
18076
					$rsp = ''; // right spaces
18077
					if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18078
						$lsp = $matches[1];
18079
					}
18080
					if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18081
						$rsp = $matches[1];
18082
					}
18083
					$dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18084
				}
18085
				if ($newline) {
18086
					if (!$this->premode) {
18087
						$prelen = strlen($dom[$key]['value']);
18088
						if ($this->isRTLTextDir()) {
18089
							// right trim except non-breaking space
18090
							$dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18091
						} else {
18092
							// left trim except non-breaking space
18093
							$dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18094
						}
18095
						$postlen = strlen($dom[$key]['value']);
18096
						if (($postlen == 0) AND ($prelen > 0)) {
18097
							$dom[$key]['trimmed_space'] = true;
18098
						}
18099
					}
18100
					$newline = false;
18101
					$firstblock = true;
18102
				} else {
18103
					$firstblock = false;
18104
					// replace empty multiple spaces string with a single space
18105
					$dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18106
				}
18107
				$strrest = '';
18108
				if ($this->rtl) {
18109
					$this->x -= $this->textindent;
18110
				} else {
18111
					$this->x += $this->textindent;
18112
				}
18113
				if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18114
					$strlinelen = $this->GetStringWidth($dom[$key]['value']);
18115
					if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18116
						// HTML <a> Link
18117
						$hrefcolor = '';
18118
						if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18119
							$hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18120
						}
18121
						$hrefstyle = -1;
18122
						if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18123
							$hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18124
						}
18125
						$strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18126
					} else {
18127
						$wadj = 0; // space to leave for block continuity
18128
						if ($this->rtl) {
18129
							$cwa = ($this->x - $this->lMargin);
18130
						} else {
18131
							$cwa = ($this->w - $this->rMargin - $this->x);
18132
						}
18133
						if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18134
							// check the next text blocks for continuity
18135
							$nkey = ($key + 1);
18136
							$write_block = true;
18137
							$same_textdir = true;
18138
							$tmp_fontname = $this->FontFamily;
18139
							$tmp_fontstyle = $this->FontStyle;
18140
							$tmp_fontsize = $this->FontSizePt;
18141
							while ($write_block AND isset($dom[$nkey])) {
18142
								if ($dom[$nkey]['tag']) {
18143
									if ($dom[$nkey]['block']) {
18144
										// end of block
18145
										$write_block = false;
18146
									}
18147
									$tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18148
									$tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18149
									$tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18150
									$same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18151
								} else {
18152
									$nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']);
18153
									if (isset($nextstr[0]) AND $same_textdir) {
18154
										$wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18155
										if (isset($nextstr[1])) {
18156
											$write_block = false;
18157
										}
18158
									}
18159
								}
18160
								++$nkey;
18161
							}
18162
						}
18163
						if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18164
							$wadj = 0;
18165
							$nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']);
18166
							$numblks = count($nextstr);
18167
							if ($numblks > 1) {
18168
								// try to split on blank spaces
18169
								$wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18170
							} else {
18171
								// set the entire block on new line
18172
								$wadj = $this->GetStringWidth($nextstr[0]);
18173
							}
18174
						}
18175
						// check for reversed text direction
18176
						if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18177
							// LTR text on RTL direction or RTL text on LTR direction
18178
							$reverse_dir = true;
18179
							$this->rtl = !$this->rtl;
18180
							$revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18181
							if ($this->rtl) {
18182
								$this->x += $revshift;
18183
							} else {
18184
								$this->x -= $revshift;
18185
							}
18186
							$xws = $this->x;
18187
						}
18188
						// ****** write only until the end of the line and get the rest ******
18189
						$strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18190
						// restore default direction
18191
						if ($reverse_dir AND ($wadj == 0)) {
18192
							$this->x = $xws;
18193
							$this->rtl = !$this->rtl;
18194
							$reverse_dir = false;
18195
						}
18196
					}
18197
				}
18198
				$this->textindent = 0;
18199
				if (strlen($strrest) > 0) {
18200
					// store the remaining string on the previous $key position
18201
					$this->newline = true;
18202
					if ($strrest == $dom[$key]['value']) {
18203
						// used to avoid infinite loop
18204
						++$loop;
18205
					} else {
18206
						$loop = 0;
18207
					}
18208
					$dom[$key]['value'] = $strrest;
18209
					if ($cell) {
18210
						if ($this->rtl) {
18211
							$this->x -= $this->cell_padding['R'];
18212
						} else {
18213
							$this->x += $this->cell_padding['L'];
18214
						}
18215
					}
18216
					if ($loop < 3) {
18217
						--$key;
18218
					}
18219
				} else {
18220
					$loop = 0;
18221
					// add the positive font spacing of the last character (if any)
18222
					 if ($this->font_spacing > 0) {
18223
					 	if ($this->rtl) {
18224
							$this->x -= $this->font_spacing;
18225
						} else {
18226
							$this->x += $this->font_spacing;
18227
						}
18228
					}
18229
				}
18230
			}
18231
			++$key;
18232
			if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
18233
				// check if we are on a new page or on a new column
18234
				if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18235
					// we are on a new page or on a new column and the total object height is less than the available vertical space.
18236
					// restore previous object
18237
					$this->rollbackTransaction(true);
18238
					// restore previous values
18239
					foreach ($this_method_vars as $vkey => $vval) {
18240
						$$vkey = $vval;
18241
					}
18242
					// add a page (or trig AcceptPageBreak() for multicolumn mode)
18243
					$pre_y = $this->y;
18244
					if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18245
						$startliney = $this->y;
18246
					}
18247
					$undo = true; // avoid infinite loop
18248
				} else {
18249
					$undo = false;
18250
				}
18251
			}
18252
		} // end for each $key
18253
		// align the last line
18254
		if (isset($startlinex)) {
18255
			$yshift = ($minstartliney - $startliney);
18256
			if (($yshift > 0) OR ($this->page > $startlinepage)) {
18257
				$yshift = 0;
18258
			}
18259
			$t_x = 0;
18260
			// the last line must be shifted to be aligned as requested
18261
			$linew = abs($this->endlinex - $startlinex);
18262
			if ($this->inxobj) {
18263
				// we are inside an XObject template
18264
				$pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18265
				if (isset($opentagpos)) {
18266
					$midpos = $opentagpos;
18267
				} else {
18268
					$midpos = 0;
18269
				}
18270
				if ($midpos > 0) {
18271
					$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18272
					$pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18273
				} else {
18274
					$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18275
					$pend = '';
18276
				}
18277
			} else {
18278
				$pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18279
				if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18280
					$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18281
					$midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18282
				} elseif (isset($opentagpos)) {
18283
					$midpos = $opentagpos;
18284
				} elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18285
					$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18286
					$midpos = $this->footerpos[$startlinepage];
18287
				} else {
18288
					$midpos = 0;
18289
				}
18290
				if ($midpos > 0) {
18291
					$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18292
					$pend = substr($this->getPageBuffer($startlinepage), $midpos);
18293
				} else {
18294
					$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18295
					$pend = '';
18296
				}
18297
			}
18298
			if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18299
				// calculate shifting amount
18300
				$tw = $w;
18301
				if ($this->lMargin != $prevlMargin) {
18302
					$tw += ($prevlMargin - $this->lMargin);
18303
				}
18304
				if ($this->rMargin != $prevrMargin) {
18305
					$tw += ($prevrMargin - $this->rMargin);
18306
				}
18307
				$one_space_width = $this->GetStringWidth(chr(32));
18308
				$no = 0; // number of spaces on a line contained on a single block
18309
				if ($this->isRTLTextDir()) { // RTL
18310
					// remove left space if exist
18311
					$pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18312
					if ($pos1 > 0) {
18313
						$pos1 = intval($pos1);
18314
						if ($this->isUnicodeFont()) {
18315
							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18316
							$spacelen = 2;
18317
						} else {
18318
							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18319
							$spacelen = 1;
18320
						}
18321
						if ($pos1 == $pos2) {
18322
							$pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18323
							if (substr($pmid, $pos1, 4) == '[()]') {
18324
								$linew -= $one_space_width;
18325
							} elseif ($pos1 == strpos($pmid, '[(')) {
18326
								$no = 1;
18327
							}
18328
						}
18329
					}
18330
				} else { // LTR
18331
					// remove right space if exist
18332
					$pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18333
					if ($pos1 > 0) {
18334
						$pos1 = intval($pos1);
18335
						if ($this->isUnicodeFont()) {
18336
							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18337
							$spacelen = 2;
18338
						} else {
18339
							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18340
							$spacelen = 1;
18341
						}
18342
						if ($pos1 == $pos2) {
18343
							$pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18344
							$linew -= $one_space_width;
18345
						}
18346
					}
18347
				}
18348
				$mdiff = ($tw - $linew);
18349
				if ($plalign == 'C') {
18350
					if ($this->rtl) {
18351
						$t_x = -($mdiff / 2);
18352
					} else {
18353
						$t_x = ($mdiff / 2);
18354
					}
18355
				} elseif ($plalign == 'R') {
18356
					// right alignment on LTR document
18357
					$t_x = $mdiff;
18358
				} elseif ($plalign == 'L') {
18359
					// left alignment on RTL document
18360
					$t_x = -$mdiff;
18361
				}
18362
			} // end if startlinex
18363
			if (($t_x != 0) OR ($yshift < 0)) {
18364
				// shift the line
18365
				$trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18366
				$pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18367
				$endlinepos = strlen($pstart);
18368
				if ($this->inxobj) {
18369
					// we are inside an XObject template
18370
					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18371
					foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18372
						if ($pak >= $pask) {
18373
							$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18374
							$this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18375
						}
18376
					}
18377
				} else {
18378
					$this->setPageBuffer($startlinepage, $pstart.$pend);
18379
					// shift the annotations and links
18380
					if (isset($this->PageAnnots[$this->page])) {
18381
						foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18382
							if ($pak >= $pask) {
18383
								$this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18384
								$this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18385
							}
18386
						}
18387
					}
18388
				}
18389
				$this->y -= $yshift;
18390
				$yshift = 0;
18391
			}
18392
		}
18393
		// restore previous values
18394
		$this->setGraphicVars($gvars);
18395
		if ($this->num_columns > 1) {
18396
			$this->selectColumn();
18397
		} elseif ($this->page > $prevPage) {
18398
			$this->lMargin = $this->pagedim[$this->page]['olm'];
18399
			$this->rMargin = $this->pagedim[$this->page]['orm'];
18400
		}
18401
		// restore previous list state
18402
		$this->cell_height_ratio = $prev_cell_height_ratio;
18403
		$this->listnum = $prev_listnum;
18404
		$this->listordered = $prev_listordered;
18405
		$this->listcount = $prev_listcount;
18406
		$this->lispacer = $prev_lispacer;
18407
		if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18408
			$this->Ln($this->lasth);
18409
			if ($this->y < $maxbottomliney) {
18410
				$this->y = $maxbottomliney;
18411
			}
18412
		}
18413
		unset($dom);
18414
	}
18415
 
18416
	/**
18417
	 * Process opening tags.
18418
	 * @param $dom (array) html dom array
18419
	 * @param $key (int) current element id
18420
	 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
18421
	 * @return $dom array
18422
	 * @protected
18423
	 */
18424
	protected function openHTMLTagHandler($dom, $key, $cell) {
18425
		$tag = $dom[$key];
18426
		$parent = $dom[($dom[$key]['parent'])];
18427
		$firsttag = ($key == 1);
18428
		// check for text direction attribute
18429
		if (isset($tag['dir'])) {
18430
			$this->setTempRTL($tag['dir']);
18431
		} else {
18432
			$this->tmprtl = false;
18433
		}
18434
		if ($tag['block']) {
18435
			$hbz = 0; // distance from y to line bottom
18436
			$hb = 0; // vertical space between block tags
18437
			// calculate vertical space for block tags
18438
			if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18439
				$cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18440
			} elseif (isset($tag['fontsize'])) {
18441
				$cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
18442
			} else {
18443
				$cur_h = $this->FontSize * $this->cell_height_ratio;
18444
			}
18445
			if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18446
				$n = $this->tagvspaces[$tag['value']][0]['n'];
18447
			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18448
				$n = 0.6;
18449
			} else {
18450
				$n = 1;
18451
			}
18452
			if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
18453
				$hb = 0;
18454
			} else {
18455
				$hb = ($n * $cur_h);
18456
			}
18457
			if (($this->htmlvspace <= 0) AND ($n > 0)) {
18458
				if (isset($parent['fontsize'])) {
18459
					$hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18460
				} else {
18461
					$hbz = $this->FontSize * $this->cell_height_ratio;
18462
				}
18463
			}
18464
			if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18465
				// fix vertical space after table
18466
				$hbz = 0;
18467
			}
18468
		}
18469
		// Opening tag
18470
		switch($tag['value']) {
18471
			case 'table': {
18472
				$cp = 0;
18473
				$cs = 0;
18474
				$dom[$key]['rowspans'] = array();
18475
				if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18476
					$this->htmlvspace = 0;
18477
					// set table header
18478
					if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18479
						// set table header
18480
						$this->thead = $dom[$key]['thead'];
18481
						if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18482
							$this->theadMargins = array();
18483
							$this->theadMargins['cell_padding'] = $this->cell_padding;
18484
							$this->theadMargins['lmargin'] = $this->lMargin;
18485
							$this->theadMargins['rmargin'] = $this->rMargin;
18486
							$this->theadMargins['page'] = $this->page;
18487
							$this->theadMargins['cell'] = $cell;
18488
						}
18489
					}
18490
				}
18491
				// store current margins and page
18492
				$dom[$key]['old_cell_padding'] = $this->cell_padding;
18493
				if (isset($tag['attribute']['cellpadding'])) {
18494
					$pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18495
					$this->SetCellPadding($pad);
18496
				} elseif (isset($tag['padding'])) {
18497
					$this->cell_padding = $tag['padding'];
18498
				}
18499
				if (isset($tag['attribute']['cellspacing'])) {
18500
					$cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18501
				} elseif (isset($tag['border-spacing'])) {
18502
					$cs = $tag['border-spacing']['V'];
18503
				}
18504
				$prev_y = $this->y;
18505
				if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18506
					$this->inthead = true;
18507
					// add a page (or trig AcceptPageBreak() for multicolumn mode)
18508
					$this->checkPageBreak($this->PageBreakTrigger + 1);
18509
				}
18510
				break;
18511
			}
18512
			case 'tr': {
18513
				// array of columns positions
18514
				$dom[$key]['cellpos'] = array();
18515
				break;
18516
			}
18517
			case 'hr': {
18518
				if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18519
					$hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18520
				} else {
18521
					$hrHeight = $this->GetLineWidth();
18522
				}
18523
				$this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
18524
				$x = $this->GetX();
18525
				$y = $this->GetY();
18526
				$wtmp = $this->w - $this->lMargin - $this->rMargin;
18527
				if ($cell) {
18528
					$wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18529
				}
18530
				if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18531
					$hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18532
				} else {
18533
					$hrWidth = $wtmp;
18534
				}
18535
				$prevlinewidth = $this->GetLineWidth();
18536
				$this->SetLineWidth($hrHeight);
18537
				$this->Line($x, $y, $x + $hrWidth, $y);
18538
				$this->SetLineWidth($prevlinewidth);
18539
				$this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
18540
				break;
18541
			}
18542
			case 'a': {
18543
				if (array_key_exists('href', $tag['attribute'])) {
18544
					$this->HREF['url'] = $tag['attribute']['href'];
18545
				}
18546
				break;
18547
			}
18548
			case 'img': {
18549
				if (isset($tag['attribute']['src'])) {
18550
					if ($tag['attribute']['src']{0} === '@') {
18551
						// data stream
18552
						$tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18553
						$type = '';
18554
					} else {
18555
						// check for images without protocol
18556
						if (preg_match('%^/{2}%', $tag['attribute']['src'])) {
18557
							$tag['attribute']['src'] = 'http:'.$tag['attribute']['src'];
18558
						}
18559
						// replace relative path with real server path
18560
						if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
18561
							$findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
18562
							if (($findroot === false) OR ($findroot > 1)) {
18563
								if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
18564
									$tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src'];
18565
								} else {
18566
									$tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
18567
								}
18568
							}
18569
						}
18570
						$tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src']));
18571
						$type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18572
						$testscrtype = @parse_url($tag['attribute']['src']);
18573
						if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
18574
							// convert URL to server path
18575
							$tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
18576
						}
18577
					}
18578
					if (!isset($tag['width'])) {
18579
						$tag['width'] = 0;
18580
					}
18581
					if (!isset($tag['height'])) {
18582
						$tag['height'] = 0;
18583
					}
18584
					//if (!isset($tag['attribute']['align'])) {
18585
						// the only alignment supported is "bottom"
18586
						// further development is required for other modes.
18587
						$tag['attribute']['align'] = 'bottom';
18588
					//}
18589
					switch($tag['attribute']['align']) {
18590
						case 'top': {
18591
							$align = 'T';
18592
							break;
18593
						}
18594
						case 'middle': {
18595
							$align = 'M';
18596
							break;
18597
						}
18598
						case 'bottom': {
18599
							$align = 'B';
18600
							break;
18601
						}
18602
						default: {
18603
							$align = 'B';
18604
							break;
18605
						}
18606
					}
18607
					$prevy = $this->y;
18608
					$xpos = $this->x;
18609
					$imglink = '';
18610
					if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18611
						$imglink = $this->HREF['url'];
18612
						if ($imglink{0} == '#') {
18613
							// convert url to internal link
18614
							$lnkdata = explode(',', $imglink);
18615
							if (isset($lnkdata[0])) {
18616
								$page = intval(substr($lnkdata[0], 1));
18617
								if (empty($page) OR ($page <= 0)) {
18618
									$page = $this->page;
18619
								}
18620
								if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18621
									$lnky = floatval($lnkdata[1]);
18622
								} else {
18623
									$lnky = 0;
18624
								}
18625
								$imglink = $this->AddLink();
18626
								$this->SetLink($imglink, $lnky, $page);
18627
							}
18628
						}
18629
					}
18630
					$border = 0;
18631
					if (isset($tag['border']) AND !empty($tag['border'])) {
18632
						// currently only support 1 (frame) or a combination of 'LTRB'
18633
						$border = $tag['border'];
18634
					}
18635
					$iw = '';
18636
					if (isset($tag['width'])) {
18637
						$iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
18638
					}
18639
					$ih = '';
18640
					if (isset($tag['height'])) {
18641
						$ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
18642
					}
18643
					if (($type == 'eps') OR ($type == 'ai')) {
18644
						$this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18645
					} elseif ($type == 'svg') {
18646
						$this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18647
					} else {
18648
						$this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18649
					}
18650
					switch($align) {
18651
						case 'T': {
18652
							$this->y = $prevy;
18653
							break;
18654
						}
18655
						case 'M': {
18656
							$this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
18657
							break;
18658
						}
18659
						case 'B': {
18660
							$this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
18661
							break;
18662
						}
18663
					}
18664
				}
18665
				break;
18666
			}
18667
			case 'dl': {
18668
				++$this->listnum;
18669
				if ($this->listnum == 1) {
18670
					$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18671
				} else {
18672
					$this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18673
				}
18674
				break;
18675
			}
18676
			case 'dt': {
18677
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18678
				break;
18679
			}
18680
			case 'dd': {
18681
				if ($this->rtl) {
18682
					$this->rMargin += $this->listindent;
18683
				} else {
18684
					$this->lMargin += $this->listindent;
18685
				}
18686
				++$this->listindentlevel;
18687
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18688
				break;
18689
			}
18690
			case 'ul':
18691
			case 'ol': {
18692
				++$this->listnum;
18693
				if ($tag['value'] == 'ol') {
18694
					$this->listordered[$this->listnum] = true;
18695
				} else {
18696
					$this->listordered[$this->listnum] = false;
18697
				}
18698
				if (isset($tag['attribute']['start'])) {
18699
					$this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18700
				} else {
18701
					$this->listcount[$this->listnum] = 0;
18702
				}
18703
				if ($this->rtl) {
18704
					$this->rMargin += $this->listindent;
18705
					$this->x -= $this->listindent;
18706
				} else {
18707
					$this->lMargin += $this->listindent;
18708
					$this->x += $this->listindent;
18709
				}
18710
				++$this->listindentlevel;
18711
				if ($this->listnum == 1) {
18712
					if ($key > 1) {
18713
						$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18714
					}
18715
				} else {
18716
					$this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18717
				}
18718
				break;
18719
			}
18720
			case 'li': {
18721
				if ($key > 2) {
18722
					$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18723
				}
18724
				if ($this->listordered[$this->listnum]) {
18725
					// ordered item
18726
					if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18727
						$this->lispacer = $parent['attribute']['type'];
18728
					} elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18729
						$this->lispacer = $parent['listtype'];
18730
					} elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18731
						$this->lispacer = $this->lisymbol;
18732
					} else {
18733
						$this->lispacer = '#';
18734
					}
18735
					++$this->listcount[$this->listnum];
18736
					if (isset($tag['attribute']['value'])) {
18737
						$this->listcount[$this->listnum] = intval($tag['attribute']['value']);
18738
					}
18739
				} else {
18740
					// unordered item
18741
					if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18742
						$this->lispacer = $parent['attribute']['type'];
18743
					} elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18744
						$this->lispacer = $parent['listtype'];
18745
					} elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18746
						$this->lispacer = $this->lisymbol;
18747
					} else {
18748
						$this->lispacer = '!';
18749
					}
18750
				}
18751
				break;
18752
			}
18753
			case 'blockquote': {
18754
				if ($this->rtl) {
18755
					$this->rMargin += $this->listindent;
18756
				} else {
18757
					$this->lMargin += $this->listindent;
18758
				}
18759
				++$this->listindentlevel;
18760
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18761
				break;
18762
			}
18763
			case 'br': {
18764
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18765
				break;
18766
			}
18767
			case 'div': {
18768
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18769
				break;
18770
			}
18771
			case 'p': {
18772
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18773
				break;
18774
			}
18775
			case 'pre': {
18776
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18777
				$this->premode = true;
18778
				break;
18779
			}
18780
			case 'sup': {
18781
				$this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
18782
				break;
18783
			}
18784
			case 'sub': {
18785
				$this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
18786
				break;
18787
			}
18788
			case 'h1':
18789
			case 'h2':
18790
			case 'h3':
18791
			case 'h4':
18792
			case 'h5':
18793
			case 'h6': {
18794
				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18795
				break;
18796
			}
18797
			// Form fields (since 4.8.000 - 2009-09-07)
18798
			case 'form': {
18799
				if (isset($tag['attribute']['action'])) {
18800
					$this->form_action = $tag['attribute']['action'];
18801
				} else {
18802
					$this->Error('Please explicitly set action attribute path!');
18803
				}
18804
				if (isset($tag['attribute']['enctype'])) {
18805
					$this->form_enctype = $tag['attribute']['enctype'];
18806
				} else {
18807
					$this->form_enctype = 'application/x-www-form-urlencoded';
18808
				}
18809
				if (isset($tag['attribute']['method'])) {
18810
					$this->form_mode = $tag['attribute']['method'];
18811
				} else {
18812
					$this->form_mode = 'post';
18813
				}
18814
				break;
18815
			}
18816
			case 'input': {
18817
				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
18818
					$name = $tag['attribute']['name'];
18819
				} else {
18820
					break;
18821
				}
18822
				$prop = array();
18823
				$opt = array();
18824
				if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
18825
					$prop['readonly'] = true;
18826
				}
18827
				if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
18828
					$value = $tag['attribute']['value'];
18829
				}
18830
				if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
18831
					$opt['maxlen'] = intval($tag['attribute']['maxlength']);
18832
				}
18833
				$h = $this->FontSize * $this->cell_height_ratio;
18834
				if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
18835
					$w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
18836
				} else {
18837
					$w = $h;
18838
				}
18839
				if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
18840
					$checked = true;
18841
				} else {
18842
					$checked = false;
18843
				}
18844
				if (isset($tag['align'])) {
18845
					switch ($tag['align']) {
18846
						case 'C': {
18847
							$opt['q'] = 1;
18848
							break;
18849
						}
18850
						case 'R': {
18851
							$opt['q'] = 2;
18852
							break;
18853
						}
18854
						case 'L':
18855
						default: {
18856
							break;
18857
						}
18858
					}
18859
				}
18860
				switch ($tag['attribute']['type']) {
18861
					case 'text': {
18862
						if (isset($value)) {
18863
							$opt['v'] = $value;
18864
						}
18865
						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
18866
						break;
18867
					}
18868
					case 'password': {
18869
						if (isset($value)) {
18870
							$opt['v'] = $value;
18871
						}
18872
						$prop['password'] = 'true';
18873
						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
18874
						break;
18875
					}
18876
					case 'checkbox': {
18877
						if (!isset($value)) {
18878
							break;
18879
						}
18880
						$this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
18881
						break;
18882
					}
18883
					case 'radio': {
18884
						if (!isset($value)) {
18885
							break;
18886
						}
18887
						$this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
18888
						break;
18889
					}
18890
					case 'submit': {
18891
						if (!isset($value)) {
18892
							$value = 'submit';
18893
						}
18894
						$w = $this->GetStringWidth($value) * 1.5;
18895
						$h *= 1.6;
18896
						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18897
						$action = array();
18898
						$action['S'] = 'SubmitForm';
18899
						$action['F'] = $this->form_action;
18900
						if ($this->form_enctype != 'FDF') {
18901
							$action['Flags'] = array('ExportFormat');
18902
						}
18903
						if ($this->form_mode == 'get') {
18904
							$action['Flags'] = array('GetMethod');
18905
						}
18906
						$this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
18907
						break;
18908
					}
18909
					case 'reset': {
18910
						if (!isset($value)) {
18911
							$value = 'reset';
18912
						}
18913
						$w = $this->GetStringWidth($value) * 1.5;
18914
						$h *= 1.6;
18915
						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18916
						$this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
18917
						break;
18918
					}
18919
					case 'file': {
18920
						$prop['fileSelect'] = 'true';
18921
						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
18922
						if (!isset($value)) {
18923
							$value = '*';
18924
						}
18925
						$w = $this->GetStringWidth($value) * 2;
18926
						$h *= 1.2;
18927
						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18928
						$jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
18929
						$this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
18930
						break;
18931
					}
18932
					case 'hidden': {
18933
						if (isset($value)) {
18934
							$opt['v'] = $value;
18935
						}
18936
						$opt['f'] = array('invisible', 'hidden');
18937
						$this->TextField($name, 0, 0, $prop, $opt, '', '', false);
18938
						break;
18939
					}
18940
					case 'image': {
18941
						// THIS TYPE MUST BE FIXED
18942
						if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
18943
							$img = $tag['attribute']['src'];
18944
						} else {
18945
							break;
18946
						}
18947
						$value = 'img';
18948
						//$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
18949
						if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
18950
							$jsaction = $tag['attribute']['onclick'];
18951
						} else {
18952
							$jsaction = '';
18953
						}
18954
						$this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
18955
						break;
18956
					}
18957
					case 'button': {
18958
						if (!isset($value)) {
18959
							$value = ' ';
18960
						}
18961
						$w = $this->GetStringWidth($value) * 1.5;
18962
						$h *= 1.6;
18963
						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18964
						if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
18965
							$jsaction = $tag['attribute']['onclick'];
18966
						} else {
18967
							$jsaction = '';
18968
						}
18969
						$this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
18970
						break;
18971
					}
18972
				}
18973
				break;
18974
			}
18975
			case 'textarea': {
18976
				$prop = array();
18977
				$opt = array();
18978
				if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
18979
					$prop['readonly'] = true;
18980
				}
18981
				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
18982
					$name = $tag['attribute']['name'];
18983
				} else {
18984
					break;
18985
				}
18986
				if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
18987
					$opt['v'] = $tag['attribute']['value'];
18988
				}
18989
				if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
18990
					$w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
18991
				} else {
18992
					$w = 40;
18993
				}
18994
				if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
18995
					$h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
18996
				} else {
18997
					$h = 10;
18998
				}
18999
				$prop['multiline'] = 'true';
19000
				$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19001
				break;
19002
			}
19003
			case 'select': {
19004
				$h = $this->FontSize * $this->cell_height_ratio;
19005
				if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19006
					$h *= ($tag['attribute']['size'] + 1);
19007
				}
19008
				$prop = array();
19009
				$opt = array();
19010
				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19011
					$name = $tag['attribute']['name'];
19012
				} else {
19013
					break;
19014
				}
19015
				$w = 0;
19016
				if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19017
					$options = explode('#!NwL!#', $tag['attribute']['opt']);
19018
					$values = array();
19019
					foreach ($options as $val) {
19020
						if (strpos($val, '#!TaB!#') !== false) {
19021
							$opts = explode('#!TaB!#', $val);
19022
							$values[] = $opts;
19023
							$w = max($w, $this->GetStringWidth($opts[1]));
19024
						} else {
19025
							$values[] = $val;
19026
							$w = max($w, $this->GetStringWidth($val));
19027
						}
19028
					}
19029
				} else {
19030
					break;
19031
				}
19032
				$w *= 2;
19033
				if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19034
					$prop['multipleSelection'] = 'true';
19035
					$this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19036
				} else {
19037
					$this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19038
				}
19039
				break;
19040
			}
19041
			case 'tcpdf': {
19042
				if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19043
					// Special tag used to call TCPDF methods
19044
					if (isset($tag['attribute']['method'])) {
19045
						$tcpdf_method = $tag['attribute']['method'];
19046
						if (method_exists($this, $tcpdf_method)) {
19047
							if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19048
								$params = unserialize(urldecode($tag['attribute']['params']));
19049
								call_user_func_array(array($this, $tcpdf_method), $params);
19050
							} else {
19051
								$this->$tcpdf_method();
19052
							}
19053
							$this->newline = true;
19054
						}
19055
					}
19056
				}
19057
				break;
19058
			}
19059
			default: {
19060
				break;
19061
			}
19062
		}
19063
		// define tags that support borders and background colors
19064
		$bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19065
		if (in_array($tag['value'], $bordertags)) {
19066
			// set border
19067
			$dom[$key]['borderposition'] = $this->getBorderStartPosition();
19068
		}
19069
		if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19070
			$pba = $dom[$key]['attribute']['pagebreakafter'];
19071
			// check for pagebreak
19072
			if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19073
				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19074
				$this->checkPageBreak($this->PageBreakTrigger + 1);
19075
			}
19076
			if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19077
				OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19078
				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19079
				$this->checkPageBreak($this->PageBreakTrigger + 1);
19080
			}
19081
		}
19082
		return $dom;
19083
	}
19084
 
19085
	/**
19086
	 * Process closing tags.
19087
	 * @param $dom (array) html dom array
19088
	 * @param $key (int) current element id
19089
	 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19090
	 * @param $maxbottomliney (int) maximum y value of current line
19091
	 * @return $dom array
19092
	 * @protected
19093
	 */
19094
	protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19095
		$tag = $dom[$key];
19096
		$parent = $dom[($dom[$key]['parent'])];
19097
		$lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19098
		$in_table_head = false;
19099
		// maximum x position (used to draw borders)
19100
		if ($this->rtl) {
19101
			$xmax = $this->w;
19102
		} else {
19103
			$xmax = 0;
19104
		}
19105
		if ($tag['block']) {
19106
			$hbz = 0; // distance from y to line bottom
19107
			$hb = 0; // vertical space between block tags
19108
			// calculate vertical space for block tags
19109
			if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19110
				$pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19111
			} elseif (isset($parent['fontsize'])) {
19112
				$pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
19113
			} else {
19114
				$pre_h = $this->FontSize * $this->cell_height_ratio;
19115
			}
19116
			if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19117
				$n = $this->tagvspaces[$tag['value']][1]['n'];
19118
			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19119
				$n = 0.6;
19120
			} else {
19121
				$n = 1;
19122
			}
19123
			if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19124
				$hb = 0;
19125
			} else {
19126
				$hb = ($n * $pre_h);
19127
			}
19128
			if ($maxbottomliney > $this->PageBreakTrigger) {
19129
				$hbz = ($this->FontSize * $this->cell_height_ratio);
19130
			} elseif ($this->y < $maxbottomliney) {
19131
				$hbz = ($maxbottomliney - $this->y);
19132
			}
19133
		}
19134
		// Closing tag
19135
		switch($tag['value']) {
19136
			case 'tr': {
19137
				$table_el = $dom[($dom[$key]['parent'])]['parent'];
19138
				if (!isset($parent['endy'])) {
19139
					$dom[($dom[$key]['parent'])]['endy'] = $this->y;
19140
					$parent['endy'] = $this->y;
19141
				}
19142
				if (!isset($parent['endpage'])) {
19143
					$dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19144
					$parent['endpage'] = $this->page;
19145
				}
19146
				if (!isset($parent['endcolumn'])) {
19147
					$dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19148
					$parent['endcolumn'] = $this->current_column;
19149
				}
19150
				// update row-spanned cells
19151
				if (isset($dom[$table_el]['rowspans'])) {
19152
					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19153
						$dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19154
						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19155
							if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19156
								$dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19157
							} elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19158
								$dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19159
								$dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19160
								$dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19161
							}
19162
						}
19163
					}
19164
					// report new endy and endpage to the rowspanned cells
19165
					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19166
						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19167
							$dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19168
							$dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19169
							$dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19170
							$dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19171
							$dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19172
							$dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19173
						}
19174
					}
19175
					// update remaining rowspanned cells
19176
					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19177
						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19178
							$dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19179
							$dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19180
							$dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19181
						}
19182
					}
19183
				}
19184
				$this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19185
				if ($this->num_columns > 1) {
19186
					$this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19187
				}
19188
				$this->y = $dom[($dom[$key]['parent'])]['endy'];
19189
				if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19190
					$this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19191
				} elseif (isset($dom[$table_el]['border-spacing'])) {
19192
					$this->y += $dom[$table_el]['border-spacing']['V'];
19193
				}
19194
				$this->Ln(0, $cell);
19195
				if ($this->current_column == $parent['startcolumn']) {
19196
					$this->x = $parent['startx'];
19197
				}
19198
				// account for booklet mode
19199
				if ($this->page > $parent['startpage']) {
19200
					if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19201
						$this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19202
					} elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19203
						$this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19204
					}
19205
				}
19206
				break;
19207
			}
19208
			case 'tablehead':
19209
				// closing tag used for the thead part
19210
				$in_table_head = true;
19211
				$this->inthead = false;
19212
			case 'table': {
19213
				$table_el = $parent;
19214
				// set default border
19215
				if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19216
					// set default border
19217
					$border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19218
				} else {
19219
					$border = 0;
19220
				}
19221
				$default_border = $border;
19222
				// fix bottom line alignment of last line before page break
19223
				foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19224
					// update row-spanned cells
19225
					if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19226
						foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19227
							if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19228
								$dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19229
							}
19230
							if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19231
								$dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19232
							}
19233
						}
19234
					}
19235
					if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19236
						$pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19237
						$dom[$prevtrkey]['endy'] = $pgendy;
19238
						// update row-spanned cells
19239
						if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19240
							foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19241
								if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19242
									$dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19243
									$dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19244
								}
19245
							}
19246
						}
19247
					}
19248
					$prevtrkey = $trkey;
19249
					$table_el = $dom[($dom[$key]['parent'])];
19250
				}
19251
				// for each row
19252
				if (count($table_el['trids']) > 0) {
19253
					unset($xmax);
19254
				}
19255
				foreach ($table_el['trids'] as $j => $trkey) {
19256
					$parent = $dom[$trkey];
19257
					if (!isset($xmax)) {
19258
						$xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19259
					}
19260
					// for each cell on the row
19261
					foreach ($parent['cellpos'] as $k => $cellpos) {
19262
						if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19263
							$cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19264
							$cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19265
							$endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19266
							$startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19267
							$endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19268
							$startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19269
							$endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19270
						} else {
19271
							$endy = $parent['endy'];
19272
							$startpage = $parent['startpage'];
19273
							$endpage = $parent['endpage'];
19274
							$startcolumn = $parent['startcolumn'];
19275
							$endcolumn = $parent['endcolumn'];
19276
						}
19277
						if ($this->num_columns == 0) {
19278
							$this->num_columns = 1;
19279
						}
19280
						if (isset($cellpos['border'])) {
19281
							$border = $cellpos['border'];
19282
						}
19283
						if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19284
							$this->SetFillColorArray($cellpos['bgcolor']);
19285
							$fill = true;
19286
						} else {
19287
							$fill = false;
19288
						}
19289
						$x = $cellpos['startx'];
19290
						$y = $parent['starty'];
19291
						$starty = $y;
19292
						$w = abs($cellpos['endx'] - $cellpos['startx']);
19293
						// get border modes
19294
						$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19295
						$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19296
						$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19297
						// design borders around HTML cells.
19298
						for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19299
							$ccode = '';
19300
							$this->setPage($page);
19301
							if ($this->num_columns < 2) {
19302
								// single-column mode
19303
								$this->x = $x;
19304
								$this->y = $this->tMargin;
19305
							}
19306
							// account for margin changes
19307
							if ($page > $startpage) {
19308
								if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19309
									$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19310
								} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19311
									$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19312
								}
19313
							}
19314
							if ($startpage == $endpage) { // single page
19315
								$deltacol = 0;
19316
								$deltath = 0;
19317
								for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19318
									$this->selectColumn($column);
19319
									if ($startcolumn == $endcolumn) { // single column
19320
										$cborder = $border;
19321
										$h = $endy - $parent['starty'];
19322
										$this->y = $y;
19323
										$this->x = $x;
19324
									} elseif ($column == $startcolumn) { // first column
19325
										$cborder = $border_start;
19326
										$this->y = $starty;
19327
										$this->x = $x;
19328
										$h = $this->h - $this->y - $this->bMargin;
19329
										if ($this->rtl) {
19330
											$deltacol = $this->x + $this->rMargin - $this->w;
19331
										} else {
19332
											$deltacol = $this->x - $this->lMargin;
19333
										}
19334
									} elseif ($column == $endcolumn) { // end column
19335
										$cborder = $border_end;
19336
										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19337
											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19338
										}
19339
										$this->x += $deltacol;
19340
										$h = $endy - $this->y;
19341
									} else { // middle column
19342
										$cborder = $border_middle;
19343
										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19344
											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19345
										}
19346
										$this->x += $deltacol;
19347
										$h = $this->h - $this->y - $this->bMargin;
19348
									}
19349
									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19350
								} // end for each column
19351
							} elseif ($page == $startpage) { // first page
19352
								$deltacol = 0;
19353
								$deltath = 0;
19354
								for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19355
									$this->selectColumn($column);
19356
									if ($column == $startcolumn) { // first column
19357
										$cborder = $border_start;
19358
										$this->y = $starty;
19359
										$this->x = $x;
19360
										$h = $this->h - $this->y - $this->bMargin;
19361
										if ($this->rtl) {
19362
											$deltacol = $this->x + $this->rMargin - $this->w;
19363
										} else {
19364
											$deltacol = $this->x - $this->lMargin;
19365
										}
19366
									} else { // middle column
19367
										$cborder = $border_middle;
19368
										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19369
											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19370
										}
19371
										$this->x += $deltacol;
19372
										$h = $this->h - $this->y - $this->bMargin;
19373
									}
19374
									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19375
								} // end for each column
19376
							} elseif ($page == $endpage) { // last page
19377
								$deltacol = 0;
19378
								$deltath = 0;
19379
								for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19380
									$this->selectColumn($column);
19381
									if ($column == $endcolumn) { // end column
19382
										$cborder = $border_end;
19383
										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19384
											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19385
										}
19386
										$this->x += $deltacol;
19387
										$h = $endy - $this->y;
19388
									} else { // middle column
19389
										$cborder = $border_middle;
19390
										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19391
											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19392
										}
19393
										$this->x += $deltacol;
19394
										$h = $this->h - $this->y - $this->bMargin;
19395
									}
19396
									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19397
								} // end for each column
19398
							} else { // middle page
19399
								$deltacol = 0;
19400
								$deltath = 0;
19401
								for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19402
									$this->selectColumn($column);
19403
									$cborder = $border_middle;
19404
									if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19405
										$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19406
									}
19407
									$this->x += $deltacol;
19408
									$h = $this->h - $this->y - $this->bMargin;
19409
									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19410
								} // end for each column
19411
							}
19412
							if ($cborder OR $fill) {
19413
								$offsetlen = strlen($ccode);
19414
								// draw border and fill
19415
								if ($this->inxobj) {
19416
									// we are inside an XObject template
19417
									if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19418
										$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19419
										$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19420
										$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19421
									} else {
19422
										$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19423
										$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19424
									}
19425
									$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19426
									$pstart = substr($pagebuff, 0, $pagemark);
19427
									$pend = substr($pagebuff, $pagemark);
19428
									$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19429
								} else {
19430
									// draw border and fill
19431
									if (end($this->transfmrk[$this->page]) !== false) {
19432
										$pagemarkkey = key($this->transfmrk[$this->page]);
19433
										$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19434
									} elseif ($this->InFooter) {
19435
										$pagemark = $this->footerpos[$this->page];
19436
									} else {
19437
										$pagemark = $this->intmrk[$this->page];
19438
									}
19439
									$pagebuff = $this->getPageBuffer($this->page);
19440
									$pstart = substr($pagebuff, 0, $pagemark);
19441
									$pend = substr($pagebuff, $pagemark);
19442
									$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19443
								}
19444
							}
19445
						} // end for each page
19446
						// restore default border
19447
						$border = $default_border;
19448
					} // end for each cell on the row
19449
					if (isset($table_el['attribute']['cellspacing'])) {
19450
						$this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19451
					} elseif (isset($table_el['border-spacing'])) {
19452
						$this->y += $table_el['border-spacing']['V'];
19453
					}
19454
					$this->Ln(0, $cell);
19455
					$this->x = $parent['startx'];
19456
					if ($endpage > $startpage) {
19457
						if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19458
							$this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19459
						} elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19460
							$this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19461
						}
19462
					}
19463
				}
19464
				if (!$in_table_head) { // we are not inside a thead section
19465
					$this->cell_padding = $table_el['old_cell_padding'];
19466
					// reset row height
19467
					$this->resetLastH();
19468
					if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19469
						$plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19470
						if (($plendiff > 0) AND ($plendiff < 60)) {
19471
							$pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19472
							if (substr($pagediff, 0, 5) == 'BT /F') {
19473
								// the difference is only a font setting
19474
								$plendiff = 0;
19475
							}
19476
						}
19477
						if ($plendiff == 0) {
19478
							// remove last blank page
19479
							$this->deletePage($this->numpages);
19480
						}
19481
					}
19482
					if (isset($this->theadMargins['top'])) {
19483
						// restore top margin
19484
						$this->tMargin = $this->theadMargins['top'];
19485
					}
19486
					if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19487
						// reset main table header
19488
						$this->thead = '';
19489
						$this->theadMargins = array();
19490
						$this->pagedim[$this->page]['tm'] = $this->tMargin;
19491
					}
19492
				}
19493
				$parent = $table_el;
19494
				break;
19495
			}
19496
			case 'a': {
19497
				$this->HREF = '';
19498
				break;
19499
			}
19500
			case 'sup': {
19501
				$this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19502
				break;
19503
			}
19504
			case 'sub': {
19505
				$this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19506
				break;
19507
			}
19508
			case 'div': {
19509
				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19510
				break;
19511
			}
19512
			case 'blockquote': {
19513
				if ($this->rtl) {
19514
					$this->rMargin -= $this->listindent;
19515
				} else {
19516
					$this->lMargin -= $this->listindent;
19517
				}
19518
				--$this->listindentlevel;
19519
				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19520
				break;
19521
			}
19522
			case 'p': {
19523
				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19524
				break;
19525
			}
19526
			case 'pre': {
19527
				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19528
				$this->premode = false;
19529
				break;
19530
			}
19531
			case 'dl': {
19532
				--$this->listnum;
19533
				if ($this->listnum <= 0) {
19534
					$this->listnum = 0;
19535
					$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19536
				} else {
19537
					$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19538
				}
19539
				$this->resetLastH();
19540
				break;
19541
			}
19542
			case 'dt': {
19543
				$this->lispacer = '';
19544
				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19545
				break;
19546
			}
19547
			case 'dd': {
19548
				$this->lispacer = '';
19549
				if ($this->rtl) {
19550
					$this->rMargin -= $this->listindent;
19551
				} else {
19552
					$this->lMargin -= $this->listindent;
19553
				}
19554
				--$this->listindentlevel;
19555
				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19556
				break;
19557
			}
19558
			case 'ul':
19559
			case 'ol': {
19560
				--$this->listnum;
19561
				$this->lispacer = '';
19562
				if ($this->rtl) {
19563
					$this->rMargin -= $this->listindent;
19564
				} else {
19565
					$this->lMargin -= $this->listindent;
19566
				}
19567
				--$this->listindentlevel;
19568
				if ($this->listnum <= 0) {
19569
					$this->listnum = 0;
19570
					$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19571
				} else {
19572
					$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19573
				}
19574
				$this->resetLastH();
19575
				break;
19576
			}
19577
			case 'li': {
19578
				$this->lispacer = '';
19579
				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19580
				break;
19581
			}
19582
			case 'h1':
19583
			case 'h2':
19584
			case 'h3':
19585
			case 'h4':
19586
			case 'h5':
19587
			case 'h6': {
19588
				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19589
				break;
19590
			}
19591
			// Form fields (since 4.8.000 - 2009-09-07)
19592
			case 'form': {
19593
				$this->form_action = '';
19594
				$this->form_enctype = 'application/x-www-form-urlencoded';
19595
				break;
19596
			}
19597
			default : {
19598
				break;
19599
			}
19600
		}
19601
		// draw border and background (if any)
19602
		$this->drawHTMLTagBorder($parent, $xmax);
19603
		if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19604
			$pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19605
			// check for pagebreak
19606
			if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19607
				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19608
				$this->checkPageBreak($this->PageBreakTrigger + 1);
19609
			}
19610
			if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19611
				OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19612
				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19613
				$this->checkPageBreak($this->PageBreakTrigger + 1);
19614
			}
19615
		}
19616
		$this->tmprtl = false;
19617
		return $dom;
19618
	}
19619
 
19620
	/**
19621
	 * Add vertical spaces if needed.
19622
	 * @param $hbz (string) Distance between current y and line bottom.
19623
	 * @param $hb (string) The height of the break.
19624
	 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19625
	 * @param $firsttag (boolean) set to true when the tag is the first.
19626
	 * @param $lasttag (boolean) set to true when the tag is the last.
19627
	 * @protected
19628
	 */
19629
	protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19630
		if ($firsttag) {
19631
			$this->Ln(0, $cell);
19632
			$this->htmlvspace = 0;
19633
			return;
19634
		}
19635
		if ($lasttag) {
19636
			$this->Ln($hbz, $cell);
19637
			$this->htmlvspace = 0;
19638
			return;
19639
		}
19640
		if ($hb < $this->htmlvspace) {
19641
			$hd = 0;
19642
		} else {
19643
			$hd = $hb - $this->htmlvspace;
19644
			$this->htmlvspace = $hb;
19645
		}
19646
		$this->Ln(($hbz + $hd), $cell);
19647
	}
19648
 
19649
	/**
19650
	 * Return the starting coordinates to draw an html border
19651
	 * @return array containing top-left border coordinates
19652
	 * @protected
19653
	 * @since 5.7.000 (2010-08-03)
19654
	 */
19655
	protected function getBorderStartPosition() {
19656
		if ($this->rtl) {
19657
			$xmax = $this->lMargin;
19658
		} else {
19659
			$xmax = $this->w - $this->rMargin;
19660
		}
19661
		return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19662
	}
19663
 
19664
	/**
19665
	 * Draw an HTML block border and fill
19666
	 * @param $tag (array) array of tag properties.
19667
	 * @param $xmax (int) end X coordinate for border.
19668
	 * @protected
19669
	 * @since 5.7.000 (2010-08-03)
19670
	 */
19671
	protected function drawHTMLTagBorder($tag, $xmax) {
19672
		if (!isset($tag['borderposition'])) {
19673
			// nothing to draw
19674
			return;
19675
		}
19676
		$prev_x = $this->x;
19677
		$prev_y = $this->y;
19678
		$prev_lasth = $this->lasth;
19679
		$border = 0;
19680
		$fill = false;
19681
		$this->lasth = 0;
19682
		if (isset($tag['border']) AND !empty($tag['border'])) {
19683
			// get border style
19684
			$border = $tag['border'];
19685
			if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19686
				// border for table header
19687
				$border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19688
			}
19689
		}
19690
		if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19691
			// get background color
19692
			$old_bgcolor = $this->bgcolor;
19693
			$this->SetFillColorArray($tag['bgcolor']);
19694
			$fill = true;
19695
		}
19696
		if (!$border AND !$fill) {
19697
			// nothing to draw
19698
			return;
19699
		}
19700
		if (isset($tag['attribute']['cellspacing'])) {
19701
			$clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19702
			$cellspacing = array('H' => $clsp, 'V' => $clsp);
19703
		} elseif (isset($tag['border-spacing'])) {
19704
			$cellspacing = $tag['border-spacing'];
19705
		} else {
19706
			$cellspacing = array('H' => 0, 'V' => 0);
19707
		}
19708
		if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19709
			// draw the border externally respect the sqare edge.
19710
			$border['mode'] = 'ext';
19711
		}
19712
		if ($this->rtl) {
19713
			if ($xmax >= $tag['borderposition']['x']) {
19714
				$xmax = $tag['borderposition']['xmax'];
19715
			}
19716
			$w = ($tag['borderposition']['x'] - $xmax);
19717
		} else {
19718
			if ($xmax <= $tag['borderposition']['x']) {
19719
				$xmax = $tag['borderposition']['xmax'];
19720
			}
19721
			$w = ($xmax - $tag['borderposition']['x']);
19722
		}
19723
		if ($w <= 0) {
19724
			return;
19725
		}
19726
		$w += $cellspacing['H'];
19727
		$startpage = $tag['borderposition']['page'];
19728
		$startcolumn = $tag['borderposition']['column'];
19729
		$x = $tag['borderposition']['x'];
19730
		$y = $tag['borderposition']['y'];
19731
		$endpage = $this->page;
19732
		$starty = $tag['borderposition']['y'] - $cellspacing['V'];
19733
		$currentY = $this->y;
19734
		$this->x = $x;
19735
		// get latest column
19736
		$endcolumn = $this->current_column;
19737
		if ($this->num_columns == 0) {
19738
			$this->num_columns = 1;
19739
		}
19740
		// get border modes
19741
		$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19742
		$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19743
		$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19744
		// temporary disable page regions
19745
		$temp_page_regions = $this->page_regions;
19746
		$this->page_regions = array();
19747
		// design borders around HTML cells.
19748
		for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19749
			$ccode = '';
19750
			$this->setPage($page);
19751
			if ($this->num_columns < 2) {
19752
				// single-column mode
19753
				$this->x = $x;
19754
				$this->y = $this->tMargin;
19755
			}
19756
			// account for margin changes
19757
			if ($page > $startpage) {
19758
				if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19759
					$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19760
				} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19761
					$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19762
				}
19763
			}
19764
			if ($startpage == $endpage) {
19765
				// single page
19766
				for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19767
					$this->selectColumn($column);
19768
					if ($startcolumn == $endcolumn) { // single column
19769
						$cborder = $border;
19770
						$h = ($currentY - $y) + $cellspacing['V'];
19771
						$this->y = $starty;
19772
					} elseif ($column == $startcolumn) { // first column
19773
						$cborder = $border_start;
19774
						$this->y = $starty;
19775
						$h = $this->h - $this->y - $this->bMargin;
19776
					} elseif ($column == $endcolumn) { // end column
19777
						$cborder = $border_end;
19778
						$h = $currentY - $this->y;
19779
					} else { // middle column
19780
						$cborder = $border_middle;
19781
						$h = $this->h - $this->y - $this->bMargin;
19782
					}
19783
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19784
				} // end for each column
19785
			} elseif ($page == $startpage) { // first page
19786
				for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19787
					$this->selectColumn($column);
19788
					if ($column == $startcolumn) { // first column
19789
						$cborder = $border_start;
19790
						$this->y = $starty;
19791
						$h = $this->h - $this->y - $this->bMargin;
19792
					} else { // middle column
19793
						$cborder = $border_middle;
19794
						$h = $this->h - $this->y - $this->bMargin;
19795
					}
19796
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19797
				} // end for each column
19798
			} elseif ($page == $endpage) { // last page
19799
				for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19800
					$this->selectColumn($column);
19801
					if ($column == $endcolumn) {
19802
						// end column
19803
						$cborder = $border_end;
19804
						$h = $currentY - $this->y;
19805
					} else {
19806
						// middle column
19807
						$cborder = $border_middle;
19808
						$h = $this->h - $this->y - $this->bMargin;
19809
					}
19810
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19811
				} // end for each column
19812
			} else { // middle page
19813
				for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19814
					$this->selectColumn($column);
19815
					$cborder = $border_middle;
19816
					$h = $this->h - $this->y - $this->bMargin;
19817
					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19818
				} // end for each column
19819
			}
19820
			if ($cborder OR $fill) {
19821
				$offsetlen = strlen($ccode);
19822
				// draw border and fill
19823
				if ($this->inxobj) {
19824
					// we are inside an XObject template
19825
					if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19826
						$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19827
						$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19828
						$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19829
					} else {
19830
						$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19831
						$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19832
					}
19833
					$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19834
					$pstart = substr($pagebuff, 0, $pagemark);
19835
					$pend = substr($pagebuff, $pagemark);
19836
					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19837
				} else {
19838
					if (end($this->transfmrk[$this->page]) !== false) {
19839
						$pagemarkkey = key($this->transfmrk[$this->page]);
19840
						$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19841
					} elseif ($this->InFooter) {
19842
						$pagemark = $this->footerpos[$this->page];
19843
					} else {
19844
						$pagemark = $this->intmrk[$this->page];
19845
					}
19846
					$pagebuff = $this->getPageBuffer($this->page);
19847
					$pstart = substr($pagebuff, 0, $pagemark);
19848
					$pend = substr($pagebuff, $pagemark);
19849
					$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19850
					$this->bordermrk[$this->page] += $offsetlen;
19851
					$this->cntmrk[$this->page] += $offsetlen;
19852
				}
19853
			}
19854
		} // end for each page
19855
		// restore page regions
19856
		$this->page_regions = $temp_page_regions;
19857
		if (isset($old_bgcolor)) {
19858
			// restore background color
19859
			$this->SetFillColorArray($old_bgcolor);
19860
		}
19861
		// restore pointer position
19862
		$this->x = $prev_x;
19863
		$this->y = $prev_y;
19864
		$this->lasth = $prev_lasth;
19865
	}
19866
 
19867
	/**
19868
	 * Set the default bullet to be used as LI bullet symbol
19869
	 * @param $symbol (string) character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek', 'img|type|width|height|image.ext')
19870
	 * @public
19871
	 * @since 4.0.028 (2008-09-26)
19872
	 */
19873
	public function setLIsymbol($symbol='!') {
19874
		// check for custom image symbol
19875
		if (substr($symbol, 0, 4) == 'img|') {
19876
			$this->lisymbol = $symbol;
19877
			return;
19878
		}
19879
		$symbol = strtolower($symbol);
19880
		switch ($symbol) {
19881
			case '!' :
19882
			case '#' :
19883
			case 'disc' :
19884
			case 'circle' :
19885
			case 'square' :
19886
			case '1':
19887
			case 'decimal':
19888
			case 'decimal-leading-zero':
19889
			case 'i':
19890
			case 'lower-roman':
19891
			case 'I':
19892
			case 'upper-roman':
19893
			case 'a':
19894
			case 'lower-alpha':
19895
			case 'lower-latin':
19896
			case 'A':
19897
			case 'upper-alpha':
19898
			case 'upper-latin':
19899
			case 'lower-greek': {
19900
				$this->lisymbol = $symbol;
19901
				break;
19902
			}
19903
			default : {
19904
				$this->lisymbol = '';
19905
			}
19906
		}
19907
	}
19908
 
19909
	/**
19910
	 * Set the booklet mode for double-sided pages.
19911
	 * @param $booklet (boolean) true set the booklet mode on, false otherwise.
19912
	 * @param $inner (float) Inner page margin.
19913
	 * @param $outer (float) Outer page margin.
19914
	 * @public
19915
	 * @since 4.2.000 (2008-10-29)
19916
	 */
19917
	public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
19918
		$this->booklet = $booklet;
19919
		if ($inner >= 0) {
19920
			$this->lMargin = $inner;
19921
		}
19922
		if ($outer >= 0) {
19923
			$this->rMargin = $outer;
19924
		}
19925
	}
19926
 
19927
	/**
19928
	 * Swap the left and right margins.
19929
	 * @param $reverse (boolean) if true swap left and right margins.
19930
	 * @protected
19931
	 * @since 4.2.000 (2008-10-29)
19932
	 */
19933
	protected function swapMargins($reverse=true) {
19934
		if ($reverse) {
19935
			// swap left and right margins
19936
			$mtemp = $this->original_lMargin;
19937
			$this->original_lMargin = $this->original_rMargin;
19938
			$this->original_rMargin = $mtemp;
19939
			$deltam = $this->original_lMargin - $this->original_rMargin;
19940
			$this->lMargin += $deltam;
19941
			$this->rMargin -= $deltam;
19942
		}
19943
	}
19944
 
19945
	/**
19946
	 * Set the vertical spaces for HTML tags.
19947
	 * The array must have the following structure (example):
19948
	 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
19949
	 * The first array level contains the tag names,
19950
	 * the second level contains 0 for opening tags or 1 for closing tags,
19951
	 * the third level contains the vertical space unit (h) and the number spaces to add (n).
19952
	 * If the h parameter is not specified, default values are used.
19953
	 * @param $tagvs (array) array of tags and relative vertical spaces.
19954
	 * @public
19955
	 * @since 4.2.001 (2008-10-30)
19956
	 */
19957
	public function setHtmlVSpace($tagvs) {
19958
		$this->tagvspaces = $tagvs;
19959
	}
19960
 
19961
	/**
19962
	 * Set custom width for list indentation.
19963
	 * @param $width (float) width of the indentation. Use negative value to disable it.
19964
	 * @public
19965
	 * @since 4.2.007 (2008-11-12)
19966
	 */
19967
	public function setListIndentWidth($width) {
19968
		return $this->customlistindent = floatval($width);
19969
	}
19970
 
19971
	/**
19972
	 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
19973
	 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
19974
	 * @public
19975
	 * @since 4.2.010 (2008-11-14)
19976
	 */
19977
	public function setOpenCell($isopen) {
19978
		$this->opencell = $isopen;
19979
	}
19980
 
19981
	/**
19982
	 * Set the color and font style for HTML links.
19983
	 * @param $color (array) RGB array of colors
19984
	 * @param $fontstyle (string) additional font styles to add
19985
	 * @public
19986
	 * @since 4.4.003 (2008-12-09)
19987
	 */
19988
	public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
19989
		$this->htmlLinkColorArray = $color;
19990
		$this->htmlLinkFontStyle = $fontstyle;
19991
	}
19992
 
19993
	/**
19994
	 * Convert HTML string containing value and unit of measure to user's units or points.
19995
	 * @param $htmlval (string) String containing values and unit.
19996
	 * @param $refsize (string) Reference value in points.
19997
	 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
19998
	 * @param $points (boolean) If true returns points, otherwise returns value in user's units.
19999
	 * @return float value in user's unit or point if $points=true
20000
	 * @public
20001
	 * @since 4.4.004 (2008-12-10)
20002
	 */
20003
	public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20004
		$supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20005
		$retval = 0;
20006
		$value = 0;
20007
		$unit = 'px';
20008
		if ($points) {
20009
			$k = 1;
20010
		} else {
20011
			$k = $this->k;
20012
		}
20013
		if (in_array($defaultunit, $supportedunits)) {
20014
			$unit = $defaultunit;
20015
		}
20016
		if (is_numeric($htmlval)) {
20017
			$value = floatval($htmlval);
20018
		} elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20019
			$value = floatval($mnum[1]);
20020
			if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20021
				if (in_array($munit[1], $supportedunits)) {
20022
					$unit = $munit[1];
20023
				}
20024
			}
20025
		}
20026
		switch ($unit) {
20027
			// percentage
20028
			case '%': {
20029
				$retval = (($value * $refsize) / 100);
20030
				break;
20031
			}
20032
			// relative-size
20033
			case 'em': {
20034
				$retval = ($value * $refsize);
20035
				break;
20036
			}
20037
			// height of lower case 'x' (about half the font-size)
20038
			case 'ex': {
20039
				$retval = ($value * ($refsize / 2));
20040
				break;
20041
			}
20042
			// absolute-size
20043
			case 'in': {
20044
				$retval = (($value * $this->dpi) / $k);
20045
				break;
20046
			}
20047
			// centimeters
20048
			case 'cm': {
20049
				$retval = (($value / 2.54 * $this->dpi) / $k);
20050
				break;
20051
			}
20052
			// millimeters
20053
			case 'mm': {
20054
				$retval = (($value / 25.4 * $this->dpi) / $k);
20055
				break;
20056
			}
20057
			// one pica is 12 points
20058
			case 'pc': {
20059
				$retval = (($value * 12) / $k);
20060
				break;
20061
			}
20062
			// points
20063
			case 'pt': {
20064
				$retval = ($value / $k);
20065
				break;
20066
			}
20067
			// pixels
20068
			case 'px': {
20069
				$retval = $this->pixelsToUnits($value);
20070
				if ($points) {
20071
					$retval *= $this->k;
20072
				}
20073
				break;
20074
			}
20075
		}
20076
		return $retval;
20077
	}
20078
 
20079
	/**
20080
	 * Output an HTML list bullet or ordered item symbol
20081
	 * @param $listdepth (int) list nesting level
20082
	 * @param $listtype (string) type of list
20083
	 * @param $size (float) current font size
20084
	 * @protected
20085
	 * @since 4.4.004 (2008-12-10)
20086
	 */
20087
	protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20088
		if ($this->state != 2) {
20089
			return;
20090
		}
20091
		$size /= $this->k;
20092
		$fill = '';
20093
		$bgcolor = $this->bgcolor;
20094
		$color = $this->fgcolor;
20095
		$strokecolor = $this->strokecolor;
20096
		$width = 0;
20097
		$textitem = '';
20098
		$tmpx = $this->x;
20099
		$lspace = $this->GetStringWidth('  ');
20100
		if ($listtype == '^') {
20101
			// special symbol used for avoid justification of rect bullet
20102
			$this->lispacer = '';
20103
			return;
20104
		} elseif ($listtype == '!') {
20105
			// set default list type for unordered list
20106
			$deftypes = array('disc', 'circle', 'square');
20107
			$listtype = $deftypes[($listdepth - 1) % 3];
20108
		} elseif ($listtype == '#') {
20109
			// set default list type for ordered list
20110
			$listtype = 'decimal';
20111
		} elseif (substr($listtype, 0, 4) == 'img|') {
20112
			// custom image type ('img|type|width|height|image.ext')
20113
			$img = explode('|', $listtype);
20114
			$listtype = 'img';
20115
		}
20116
		switch ($listtype) {
20117
			// unordered types
20118
			case 'none': {
20119
				break;
20120
			}
20121
			case 'disc': {
20122
				$r = $size / 6;
20123
				$lspace += (2 * $r);
20124
				if ($this->rtl) {
20125
					$this->x += $lspace;
20126
				} else {
20127
					$this->x -= $lspace;
20128
				}
20129
				$this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20130
				break;
20131
			}
20132
			case 'circle': {
20133
				$r = $size / 6;
20134
				$lspace += (2 * $r);
20135
				if ($this->rtl) {
20136
					$this->x += $lspace;
20137
				} else {
20138
					$this->x -= $lspace;
20139
				}
20140
				$prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20141
				$new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20142
				$this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20143
				$this->_out($prev_line_style); // restore line settings
20144
				break;
20145
			}
20146
			case 'square': {
20147
				$l = $size / 3;
20148
				$lspace += $l;
20149
				if ($this->rtl) {;
20150
					$this->x += $lspace;
20151
				} else {
20152
					$this->x -= $lspace;
20153
				}
20154
				$this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20155
				break;
20156
			}
20157
			case 'img': {
20158
				// 1=>type, 2=>width, 3=>height, 4=>image.ext
20159
				$lspace += $img[2];
20160
				if ($this->rtl) {;
20161
					$this->x += $lspace;
20162
				} else {
20163
					$this->x -= $lspace;
20164
				}
20165
				$imgtype = strtolower($img[1]);
20166
				$prev_y = $this->y;
20167
				switch ($imgtype) {
20168
					case 'svg': {
20169
						$this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20170
						break;
20171
					}
20172
					case 'ai':
20173
					case 'eps': {
20174
						$this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20175
						break;
20176
					}
20177
					default: {
20178
						$this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false);
20179
						break;
20180
					}
20181
				}
20182
				$this->y = $prev_y;
20183
				break;
20184
			}
20185
			// ordered types
20186
			// $this->listcount[$this->listnum];
20187
			// $textitem
20188
			case '1':
20189
			case 'decimal': {
20190
				$textitem = $this->listcount[$this->listnum];
20191
				break;
20192
			}
20193
			case 'decimal-leading-zero': {
20194
				$textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20195
				break;
20196
			}
20197
			case 'i':
20198
			case 'lower-roman': {
20199
				$textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20200
				break;
20201
			}
20202
			case 'I':
20203
			case 'upper-roman': {
20204
				$textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20205
				break;
20206
			}
20207
			case 'a':
20208
			case 'lower-alpha':
20209
			case 'lower-latin': {
20210
				$textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20211
				break;
20212
			}
20213
			case 'A':
20214
			case 'upper-alpha':
20215
			case 'upper-latin': {
20216
				$textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20217
				break;
20218
			}
20219
			case 'lower-greek': {
20220
				$textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20221
				break;
20222
			}
20223
			/*
20224
			// Types to be implemented (special handling)
20225
			case 'hebrew': {
20226
				break;
20227
			}
20228
			case 'armenian': {
20229
				break;
20230
			}
20231
			case 'georgian': {
20232
				break;
20233
			}
20234
			case 'cjk-ideographic': {
20235
				break;
20236
			}
20237
			case 'hiragana': {
20238
				break;
20239
			}
20240
			case 'katakana': {
20241
				break;
20242
			}
20243
			case 'hiragana-iroha': {
20244
				break;
20245
			}
20246
			case 'katakana-iroha': {
20247
				break;
20248
			}
20249
			*/
20250
			default: {
20251
				$textitem = $this->listcount[$this->listnum];
20252
			}
20253
		}
20254
		if (!TCPDF_STATIC::empty_string($textitem)) {
20255
			// Check whether we need a new page or new column
20256
			$prev_y = $this->y;
20257
			$h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
20258
			if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20259
				$tmpx = $this->x;
20260
			}
20261
			// print ordered item
20262
			if ($this->rtl) {
20263
				$textitem = '.'.$textitem;
20264
			} else {
20265
				$textitem = $textitem.'.';
20266
			}
20267
			$lspace += $this->GetStringWidth($textitem);
20268
			if ($this->rtl) {
20269
				$this->x += $lspace;
20270
			} else {
20271
				$this->x -= $lspace;
20272
			}
20273
			$this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20274
		}
20275
		$this->x = $tmpx;
20276
		$this->lispacer = '^';
20277
		// restore colors
20278
		$this->SetFillColorArray($bgcolor);
20279
		$this->SetDrawColorArray($strokecolor);
20280
		$this->SettextColorArray($color);
20281
	}
20282
 
20283
	/**
20284
	 * Returns current graphic variables as array.
20285
	 * @return array of graphic variables
20286
	 * @protected
20287
	 * @since 4.2.010 (2008-11-14)
20288
	 */
20289
	protected function getGraphicVars() {
20290
		$grapvars = array(
20291
			'FontFamily' => $this->FontFamily,
20292
			'FontStyle' => $this->FontStyle,
20293
			'FontSizePt' => $this->FontSizePt,
20294
			'rMargin' => $this->rMargin,
20295
			'lMargin' => $this->lMargin,
20296
			'cell_padding' => $this->cell_padding,
20297
			'cell_margin' => $this->cell_margin,
20298
			'LineWidth' => $this->LineWidth,
20299
			'linestyleWidth' => $this->linestyleWidth,
20300
			'linestyleCap' => $this->linestyleCap,
20301
			'linestyleJoin' => $this->linestyleJoin,
20302
			'linestyleDash' => $this->linestyleDash,
20303
			'textrendermode' => $this->textrendermode,
20304
			'textstrokewidth' => $this->textstrokewidth,
20305
			'DrawColor' => $this->DrawColor,
20306
			'FillColor' => $this->FillColor,
20307
			'TextColor' => $this->TextColor,
20308
			'ColorFlag' => $this->ColorFlag,
20309
			'bgcolor' => $this->bgcolor,
20310
			'fgcolor' => $this->fgcolor,
20311
			'htmlvspace' => $this->htmlvspace,
20312
			'listindent' => $this->listindent,
20313
			'listindentlevel' => $this->listindentlevel,
20314
			'listnum' => $this->listnum,
20315
			'listordered' => $this->listordered,
20316
			'listcount' => $this->listcount,
20317
			'lispacer' => $this->lispacer,
20318
			'cell_height_ratio' => $this->cell_height_ratio,
20319
			'font_stretching' => $this->font_stretching,
20320
			'font_spacing' => $this->font_spacing,
20321
			'alpha' => $this->alpha,
20322
			// extended
20323
			'lasth' => $this->lasth,
20324
			'tMargin' => $this->tMargin,
20325
			'bMargin' => $this->bMargin,
20326
			'AutoPageBreak' => $this->AutoPageBreak,
20327
			'PageBreakTrigger' => $this->PageBreakTrigger,
20328
			'x' => $this->x,
20329
			'y' => $this->y,
20330
			'w' => $this->w,
20331
			'h' => $this->h,
20332
			'wPt' => $this->wPt,
20333
			'hPt' => $this->hPt,
20334
			'fwPt' => $this->fwPt,
20335
			'fhPt' => $this->fhPt,
20336
			'page' => $this->page,
20337
			'current_column' => $this->current_column,
20338
			'num_columns' => $this->num_columns
20339
			);
20340
		return $grapvars;
20341
	}
20342
 
20343
	/**
20344
	 * Set graphic variables.
20345
	 * @param $gvars (array) array of graphic variablesto restore
20346
	 * @param $extended (boolean) if true restore extended graphic variables
20347
	 * @protected
20348
	 * @since 4.2.010 (2008-11-14)
20349
	 */
20350
	protected function setGraphicVars($gvars, $extended=false) {
20351
		if ($this->state != 2) {
20352
			 return;
20353
		}
20354
		$this->FontFamily = $gvars['FontFamily'];
20355
		$this->FontStyle = $gvars['FontStyle'];
20356
		$this->FontSizePt = $gvars['FontSizePt'];
20357
		$this->rMargin = $gvars['rMargin'];
20358
		$this->lMargin = $gvars['lMargin'];
20359
		$this->cell_padding = $gvars['cell_padding'];
20360
		$this->cell_margin = $gvars['cell_margin'];
20361
		$this->LineWidth = $gvars['LineWidth'];
20362
		$this->linestyleWidth = $gvars['linestyleWidth'];
20363
		$this->linestyleCap = $gvars['linestyleCap'];
20364
		$this->linestyleJoin = $gvars['linestyleJoin'];
20365
		$this->linestyleDash = $gvars['linestyleDash'];
20366
		$this->textrendermode = $gvars['textrendermode'];
20367
		$this->textstrokewidth = $gvars['textstrokewidth'];
20368
		$this->DrawColor = $gvars['DrawColor'];
20369
		$this->FillColor = $gvars['FillColor'];
20370
		$this->TextColor = $gvars['TextColor'];
20371
		$this->ColorFlag = $gvars['ColorFlag'];
20372
		$this->bgcolor = $gvars['bgcolor'];
20373
		$this->fgcolor = $gvars['fgcolor'];
20374
		$this->htmlvspace = $gvars['htmlvspace'];
20375
		$this->listindent = $gvars['listindent'];
20376
		$this->listindentlevel = $gvars['listindentlevel'];
20377
		$this->listnum = $gvars['listnum'];
20378
		$this->listordered = $gvars['listordered'];
20379
		$this->listcount = $gvars['listcount'];
20380
		$this->lispacer = $gvars['lispacer'];
20381
		$this->cell_height_ratio = $gvars['cell_height_ratio'];
20382
		$this->font_stretching = $gvars['font_stretching'];
20383
		$this->font_spacing = $gvars['font_spacing'];
20384
		$this->alpha = $gvars['alpha'];
20385
		if ($extended) {
20386
			// restore extended values
20387
			$this->lasth = $gvars['lasth'];
20388
			$this->tMargin = $gvars['tMargin'];
20389
			$this->bMargin = $gvars['bMargin'];
20390
			$this->AutoPageBreak = $gvars['AutoPageBreak'];
20391
			$this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20392
			$this->x = $gvars['x'];
20393
			$this->y = $gvars['y'];
20394
			$this->w = $gvars['w'];
20395
			$this->h = $gvars['h'];
20396
			$this->wPt = $gvars['wPt'];
20397
			$this->hPt = $gvars['hPt'];
20398
			$this->fwPt = $gvars['fwPt'];
20399
			$this->fhPt = $gvars['fhPt'];
20400
			$this->page = $gvars['page'];
20401
			$this->current_column = $gvars['current_column'];
20402
			$this->num_columns = $gvars['num_columns'];
20403
		}
20404
		$this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20405
		if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20406
			$this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20407
		}
20408
	}
20409
 
20410
	/**
20411
	 * Writes data to a temporary file on filesystem.
20412
	 * @param $filename (string) file name
20413
	 * @param $data (mixed) data to write on file
20414
	 * @param $append (boolean) if true append data, false replace.
20415
	 * @since 4.5.000 (2008-12-31)
20416
	 * @protected
20417
	 */
20418
	protected function writeDiskCache($filename, $data, $append=false) {
20419
		if ($append) {
20420
			$fmode = 'ab+';
20421
		} else {
20422
			$fmode = 'wb+';
20423
		}
20424
		$f = @fopen($filename, $fmode);
20425
		if (!$f) {
20426
			$this->Error('Unable to write cache file: '.$filename);
20427
		} else {
20428
			fwrite($f, $data);
20429
			fclose($f);
20430
		}
20431
		// update file length (needed for transactions)
20432
		if (!isset($this->cache_file_length['_'.$filename])) {
20433
			$this->cache_file_length['_'.$filename] = strlen($data);
20434
		} else {
20435
			$this->cache_file_length['_'.$filename] += strlen($data);
20436
		}
20437
	}
20438
 
20439
	/**
20440
	 * Read data from a temporary file on filesystem.
20441
	 * @param $filename (string) file name
20442
	 * @return mixed retrieved data
20443
	 * @since 4.5.000 (2008-12-31)
20444
	 * @protected
20445
	 */
20446
	protected function readDiskCache($filename) {
20447
		return file_get_contents($filename);
20448
	}
20449
 
20450
	/**
20451
	 * Set buffer content (always append data).
20452
	 * @param $data (string) data
20453
	 * @protected
20454
	 * @since 4.5.000 (2009-01-02)
20455
	 */
20456
	protected function setBuffer($data) {
20457
		$this->bufferlen += strlen($data);
20458
		if ($this->diskcache) {
20459
			if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20460
				$this->buffer = TCPDF_STATIC::getObjFilename('buffer');
20461
			}
20462
			$this->writeDiskCache($this->buffer, $data, true);
20463
		} else {
20464
			$this->buffer .= $data;
20465
		}
20466
	}
20467
 
20468
	/**
20469
	 * Replace the buffer content
20470
	 * @param $data (string) data
20471
	 * @protected
20472
	 * @since 5.5.000 (2010-06-22)
20473
	 */
20474
	protected function replaceBuffer($data) {
20475
		$this->bufferlen = strlen($data);
20476
		if ($this->diskcache) {
20477
			if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20478
				$this->buffer = TCPDF_STATIC::getObjFilename('buffer');
20479
			}
20480
			$this->writeDiskCache($this->buffer, $data, false);
20481
		} else {
20482
			$this->buffer = $data;
20483
		}
20484
	}
20485
 
20486
	/**
20487
	 * Get buffer content.
20488
	 * @return string buffer content
20489
	 * @protected
20490
	 * @since 4.5.000 (2009-01-02)
20491
	 */
20492
	protected function getBuffer() {
20493
		if ($this->diskcache) {
20494
			return $this->readDiskCache($this->buffer);
20495
		} else {
20496
			return $this->buffer;
20497
		}
20498
	}
20499
 
20500
	/**
20501
	 * Set page buffer content.
20502
	 * @param $page (int) page number
20503
	 * @param $data (string) page data
20504
	 * @param $append (boolean) if true append data, false replace.
20505
	 * @protected
20506
	 * @since 4.5.000 (2008-12-31)
20507
	 */
20508
	protected function setPageBuffer($page, $data, $append=false) {
20509
		if ($this->diskcache) {
20510
			if (!isset($this->pages[$page])) {
20511
				$this->pages[$page] = TCPDF_STATIC::getObjFilename('page'.$page);
20512
			}
20513
			$this->writeDiskCache($this->pages[$page], $data, $append);
20514
		} else {
20515
			if ($append) {
20516
				$this->pages[$page] .= $data;
20517
			} else {
20518
				$this->pages[$page] = $data;
20519
			}
20520
		}
20521
		if ($append AND isset($this->pagelen[$page])) {
20522
			$this->pagelen[$page] += strlen($data);
20523
		} else {
20524
			$this->pagelen[$page] = strlen($data);
20525
		}
20526
	}
20527
 
20528
	/**
20529
	 * Get page buffer content.
20530
	 * @param $page (int) page number
20531
	 * @return string page buffer content or false in case of error
20532
	 * @protected
20533
	 * @since 4.5.000 (2008-12-31)
20534
	 */
20535
	protected function getPageBuffer($page) {
20536
		if ($this->diskcache) {
20537
			return $this->readDiskCache($this->pages[$page]);
20538
		} elseif (isset($this->pages[$page])) {
20539
			return $this->pages[$page];
20540
		}
20541
		return false;
20542
	}
20543
 
20544
	/**
20545
	 * Set image buffer content.
20546
	 * @param $image (string) image key
20547
	 * @param $data (array) image data
20548
	 * @return int image index number
20549
	 * @protected
20550
	 * @since 4.5.000 (2008-12-31)
20551
	 */
20552
	protected function setImageBuffer($image, $data) {
20553
		if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20554
			$this->imagekeys[$this->numimages] = $image;
20555
			$data['i'] = $this->numimages;
20556
			++$this->numimages;
20557
		}
20558
		if ($this->diskcache) {
20559
			if (!isset($this->images[$image])) {
20560
				$this->images[$image] = TCPDF_STATIC::getObjFilename('image'.$image);
20561
			}
20562
			$this->writeDiskCache($this->images[$image], serialize($data));
20563
		} else {
20564
			$this->images[$image] = $data;
20565
		}
20566
		return $data['i'];
20567
	}
20568
 
20569
	/**
20570
	 * Set image buffer content for a specified sub-key.
20571
	 * @param $image (string) image key
20572
	 * @param $key (string) image sub-key
20573
	 * @param $data (array) image data
20574
	 * @protected
20575
	 * @since 4.5.000 (2008-12-31)
20576
	 */
20577
	protected function setImageSubBuffer($image, $key, $data) {
20578
		if (!isset($this->images[$image])) {
20579
			$this->setImageBuffer($image, array());
20580
		}
20581
		if ($this->diskcache) {
20582
			$tmpimg = $this->getImageBuffer($image);
20583
			$tmpimg[$key] = $data;
20584
			$this->writeDiskCache($this->images[$image], serialize($tmpimg));
20585
		} else {
20586
			$this->images[$image][$key] = $data;
20587
		}
20588
	}
20589
 
20590
	/**
20591
	 * Get image buffer content.
20592
	 * @param $image (string) image key
20593
	 * @return string image buffer content or false in case of error
20594
	 * @protected
20595
	 * @since 4.5.000 (2008-12-31)
20596
	 */
20597
	protected function getImageBuffer($image) {
20598
		if ($this->diskcache AND isset($this->images[$image])) {
20599
			return unserialize($this->readDiskCache($this->images[$image]));
20600
		} elseif (isset($this->images[$image])) {
20601
			return $this->images[$image];
20602
		}
20603
		return false;
20604
	}
20605
 
20606
	/**
20607
	 * Set font buffer content.
20608
	 * @param $font (string) font key
20609
	 * @param $data (array) font data
20610
	 * @protected
20611
	 * @since 4.5.000 (2009-01-02)
20612
	 */
20613
	protected function setFontBuffer($font, $data) {
20614
		if ($this->diskcache) {
20615
			if (!isset($this->fonts[$font])) {
20616
				$this->fonts[$font] = TCPDF_STATIC::getObjFilename('font');
20617
			}
20618
			$this->writeDiskCache($this->fonts[$font], serialize($data));
20619
		} else {
20620
			$this->fonts[$font] = $data;
20621
		}
20622
		if (!in_array($font, $this->fontkeys)) {
20623
			$this->fontkeys[] = $font;
20624
			// store object ID for current font
20625
			++$this->n;
20626
			$this->font_obj_ids[$font] = $this->n;
20627
			$this->setFontSubBuffer($font, 'n', $this->n);
20628
		}
20629
	}
20630
 
20631
	/**
20632
	 * Set font buffer content.
20633
	 * @param $font (string) font key
20634
	 * @param $key (string) font sub-key
20635
	 * @param $data (array) font data
20636
	 * @protected
20637
	 * @since 4.5.000 (2009-01-02)
20638
	 */
20639
	protected function setFontSubBuffer($font, $key, $data) {
20640
		if (!isset($this->fonts[$font])) {
20641
			$this->setFontBuffer($font, array());
20642
		}
20643
		if ($this->diskcache) {
20644
			$tmpfont = $this->getFontBuffer($font);
20645
			$tmpfont[$key] = $data;
20646
			$this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
20647
		} else {
20648
			$this->fonts[$font][$key] = $data;
20649
		}
20650
	}
20651
 
20652
	/**
20653
	 * Get font buffer content.
20654
	 * @param $font (string) font key
20655
	 * @return string font buffer content or false in case of error
20656
	 * @protected
20657
	 * @since 4.5.000 (2009-01-02)
20658
	 */
20659
	protected function getFontBuffer($font) {
20660
		if ($this->diskcache AND isset($this->fonts[$font])) {
20661
			return unserialize($this->readDiskCache($this->fonts[$font]));
20662
		} elseif (isset($this->fonts[$font])) {
20663
			return $this->fonts[$font];
20664
		}
20665
		return false;
20666
	}
20667
 
20668
	/**
20669
	 * Move a page to a previous position.
20670
	 * @param $frompage (int) number of the source page
20671
	 * @param $topage (int) number of the destination page (must be less than $frompage)
20672
	 * @return true in case of success, false in case of error.
20673
	 * @public
20674
	 * @since 4.5.000 (2009-01-02)
20675
	 */
20676
	public function movePage($frompage, $topage) {
20677
		if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20678
			return false;
20679
		}
20680
		if ($frompage == $this->page) {
20681
			// close the page before moving it
20682
			$this->endPage();
20683
		}
20684
		// move all page-related states
20685
		$tmppage = $this->getPageBuffer($frompage);
20686
		$tmppagedim = $this->pagedim[$frompage];
20687
		$tmppagelen = $this->pagelen[$frompage];
20688
		$tmpintmrk = $this->intmrk[$frompage];
20689
		$tmpbordermrk = $this->bordermrk[$frompage];
20690
		$tmpcntmrk = $this->cntmrk[$frompage];
20691
		$tmppageobjects = $this->pageobjects[$frompage];
20692
		if (isset($this->footerpos[$frompage])) {
20693
			$tmpfooterpos = $this->footerpos[$frompage];
20694
		}
20695
		if (isset($this->footerlen[$frompage])) {
20696
			$tmpfooterlen = $this->footerlen[$frompage];
20697
		}
20698
		if (isset($this->transfmrk[$frompage])) {
20699
			$tmptransfmrk = $this->transfmrk[$frompage];
20700
		}
20701
		if (isset($this->PageAnnots[$frompage])) {
20702
			$tmpannots = $this->PageAnnots[$frompage];
20703
		}
20704
		if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20705
			for ($i = $frompage; $i > $topage; --$i) {
20706
				if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20707
					--$this->pagegroups[$this->newpagegroup[$i]];
20708
					break;
20709
				}
20710
			}
20711
			for ($i = $topage; $i > 0; --$i) {
20712
				if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20713
					++$this->pagegroups[$this->newpagegroup[$i]];
20714
					break;
20715
				}
20716
			}
20717
		}
20718
		for ($i = $frompage; $i > $topage; --$i) {
20719
			$j = $i - 1;
20720
			// shift pages down
20721
			$this->setPageBuffer($i, $this->getPageBuffer($j));
20722
			$this->pagedim[$i] = $this->pagedim[$j];
20723
			$this->pagelen[$i] = $this->pagelen[$j];
20724
			$this->intmrk[$i] = $this->intmrk[$j];
20725
			$this->bordermrk[$i] = $this->bordermrk[$j];
20726
			$this->cntmrk[$i] = $this->cntmrk[$j];
20727
			$this->pageobjects[$i] = $this->pageobjects[$j];
20728
			if (isset($this->footerpos[$j])) {
20729
				$this->footerpos[$i] = $this->footerpos[$j];
20730
			} elseif (isset($this->footerpos[$i])) {
20731
				unset($this->footerpos[$i]);
20732
			}
20733
			if (isset($this->footerlen[$j])) {
20734
				$this->footerlen[$i] = $this->footerlen[$j];
20735
			} elseif (isset($this->footerlen[$i])) {
20736
				unset($this->footerlen[$i]);
20737
			}
20738
			if (isset($this->transfmrk[$j])) {
20739
				$this->transfmrk[$i] = $this->transfmrk[$j];
20740
			} elseif (isset($this->transfmrk[$i])) {
20741
				unset($this->transfmrk[$i]);
20742
			}
20743
			if (isset($this->PageAnnots[$j])) {
20744
				$this->PageAnnots[$i] = $this->PageAnnots[$j];
20745
			} elseif (isset($this->PageAnnots[$i])) {
20746
				unset($this->PageAnnots[$i]);
20747
			}
20748
			if (isset($this->newpagegroup[$j])) {
20749
				$this->newpagegroup[$i] = $this->newpagegroup[$j];
20750
				unset($this->newpagegroup[$j]);
20751
			}
20752
			if ($this->currpagegroup == $j) {
20753
				$this->currpagegroup = $i;
20754
			}
20755
		}
20756
		$this->setPageBuffer($topage, $tmppage);
20757
		$this->pagedim[$topage] = $tmppagedim;
20758
		$this->pagelen[$topage] = $tmppagelen;
20759
		$this->intmrk[$topage] = $tmpintmrk;
20760
		$this->bordermrk[$topage] = $tmpbordermrk;
20761
		$this->cntmrk[$topage] = $tmpcntmrk;
20762
		$this->pageobjects[$topage] = $tmppageobjects;
20763
		if (isset($tmpfooterpos)) {
20764
			$this->footerpos[$topage] = $tmpfooterpos;
20765
		} elseif (isset($this->footerpos[$topage])) {
20766
			unset($this->footerpos[$topage]);
20767
		}
20768
		if (isset($tmpfooterlen)) {
20769
			$this->footerlen[$topage] = $tmpfooterlen;
20770
		} elseif (isset($this->footerlen[$topage])) {
20771
			unset($this->footerlen[$topage]);
20772
		}
20773
		if (isset($tmptransfmrk)) {
20774
			$this->transfmrk[$topage] = $tmptransfmrk;
20775
		} elseif (isset($this->transfmrk[$topage])) {
20776
			unset($this->transfmrk[$topage]);
20777
		}
20778
		if (isset($tmpannots)) {
20779
			$this->PageAnnots[$topage] = $tmpannots;
20780
		} elseif (isset($this->PageAnnots[$topage])) {
20781
			unset($this->PageAnnots[$topage]);
20782
		}
20783
		// adjust outlines
20784
		$tmpoutlines = $this->outlines;
20785
		foreach ($tmpoutlines as $key => $outline) {
20786
			if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20787
				$this->outlines[$key]['p'] = ($outline['p'] + 1);
20788
			} elseif ($outline['p'] == $frompage) {
20789
				$this->outlines[$key]['p'] = $topage;
20790
			}
20791
		}
20792
		// adjust dests
20793
		$tmpdests = $this->dests;
20794
		foreach ($tmpdests as $key => $dest) {
20795
			if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20796
				$this->dests[$key]['p'] = ($dest['p'] + 1);
20797
			} elseif ($dest['p'] == $frompage) {
20798
				$this->dests[$key]['p'] = $topage;
20799
			}
20800
		}
20801
		// adjust links
20802
		$tmplinks = $this->links;
20803
		foreach ($tmplinks as $key => $link) {
20804
			if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
20805
				$this->links[$key][0] = ($link[0] + 1);
20806
			} elseif ($link[0] == $frompage) {
20807
				$this->links[$key][0] = $topage;
20808
			}
20809
		}
20810
		// adjust javascript
20811
		$tmpjavascript = $this->javascript;
20812
		global $jfrompage, $jtopage;
20813
		$jfrompage = $frompage;
20814
		$jtopage = $topage;
20815
		$this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
20816
			create_function('$matches', 'global $jfrompage, $jtopage;
20817
			$pagenum = intval($matches[3]) + 1;
20818
			if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20819
				$newpage = ($pagenum + 1);
20820
			} elseif ($pagenum == $jfrompage) {
20821
				$newpage = $jtopage;
20822
			} else {
20823
				$newpage = $pagenum;
20824
			}
20825
			--$newpage;
20826
			return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
20827
		// return to last page
20828
		$this->lastPage(true);
20829
		return true;
20830
	}
20831
 
20832
	/**
20833
	 * Remove the specified page.
20834
	 * @param $page (int) page to remove
20835
	 * @return true in case of success, false in case of error.
20836
	 * @public
20837
	 * @since 4.6.004 (2009-04-23)
20838
	 */
20839
	public function deletePage($page) {
20840
		if (($page < 1) OR ($page > $this->numpages)) {
20841
			return false;
20842
		}
20843
		// delete current page
20844
		unset($this->pages[$page]);
20845
		unset($this->pagedim[$page]);
20846
		unset($this->pagelen[$page]);
20847
		unset($this->intmrk[$page]);
20848
		unset($this->bordermrk[$page]);
20849
		unset($this->cntmrk[$page]);
20850
		foreach ($this->pageobjects[$page] as $oid) {
20851
			if (isset($this->offsets[$oid])){
20852
				unset($this->offsets[$oid]);
20853
			}
20854
		}
20855
		unset($this->pageobjects[$page]);
20856
		if (isset($this->footerpos[$page])) {
20857
			unset($this->footerpos[$page]);
20858
		}
20859
		if (isset($this->footerlen[$page])) {
20860
			unset($this->footerlen[$page]);
20861
		}
20862
		if (isset($this->transfmrk[$page])) {
20863
			unset($this->transfmrk[$page]);
20864
		}
20865
		if (isset($this->PageAnnots[$page])) {
20866
			unset($this->PageAnnots[$page]);
20867
		}
20868
		if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20869
			for ($i = $page; $i > 0; --$i) {
20870
				if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
20871
					--$this->pagegroups[$this->newpagegroup[$i]];
20872
					break;
20873
				}
20874
			}
20875
		}
20876
		if (isset($this->pageopen[$page])) {
20877
			unset($this->pageopen[$page]);
20878
		}
20879
		if ($page < $this->numpages) {
20880
			// update remaining pages
20881
			for ($i = $page; $i < $this->numpages; ++$i) {
20882
				$j = $i + 1;
20883
				// shift pages
20884
				$this->setPageBuffer($i, $this->getPageBuffer($j));
20885
				$this->pagedim[$i] = $this->pagedim[$j];
20886
				$this->pagelen[$i] = $this->pagelen[$j];
20887
				$this->intmrk[$i] = $this->intmrk[$j];
20888
				$this->bordermrk[$i] = $this->bordermrk[$j];
20889
				$this->cntmrk[$i] = $this->cntmrk[$j];
20890
				$this->pageobjects[$i] = $this->pageobjects[$j];
20891
				if (isset($this->footerpos[$j])) {
20892
					$this->footerpos[$i] = $this->footerpos[$j];
20893
				} elseif (isset($this->footerpos[$i])) {
20894
					unset($this->footerpos[$i]);
20895
				}
20896
				if (isset($this->footerlen[$j])) {
20897
					$this->footerlen[$i] = $this->footerlen[$j];
20898
				} elseif (isset($this->footerlen[$i])) {
20899
					unset($this->footerlen[$i]);
20900
				}
20901
				if (isset($this->transfmrk[$j])) {
20902
					$this->transfmrk[$i] = $this->transfmrk[$j];
20903
				} elseif (isset($this->transfmrk[$i])) {
20904
					unset($this->transfmrk[$i]);
20905
				}
20906
				if (isset($this->PageAnnots[$j])) {
20907
					$this->PageAnnots[$i] = $this->PageAnnots[$j];
20908
				} elseif (isset($this->PageAnnots[$i])) {
20909
					unset($this->PageAnnots[$i]);
20910
				}
20911
				if (isset($this->newpagegroup[$j])) {
20912
					$this->newpagegroup[$i] = $this->newpagegroup[$j];
20913
					unset($this->newpagegroup[$j]);
20914
				}
20915
				if ($this->currpagegroup == $j) {
20916
					$this->currpagegroup = $i;
20917
				}
20918
				if (isset($this->pageopen[$j])) {
20919
					$this->pageopen[$i] = $this->pageopen[$j];
20920
				} elseif (isset($this->pageopen[$i])) {
20921
					unset($this->pageopen[$i]);
20922
				}
20923
			}
20924
			// remove last page
20925
			unset($this->pages[$this->numpages]);
20926
			unset($this->pagedim[$this->numpages]);
20927
			unset($this->pagelen[$this->numpages]);
20928
			unset($this->intmrk[$this->numpages]);
20929
			unset($this->bordermrk[$this->numpages]);
20930
			unset($this->cntmrk[$this->numpages]);
20931
			foreach ($this->pageobjects[$this->numpages] as $oid) {
20932
				if (isset($this->offsets[$oid])){
20933
					unset($this->offsets[$oid]);
20934
				}
20935
			}
20936
			unset($this->pageobjects[$this->numpages]);
20937
			if (isset($this->footerpos[$this->numpages])) {
20938
				unset($this->footerpos[$this->numpages]);
20939
			}
20940
			if (isset($this->footerlen[$this->numpages])) {
20941
				unset($this->footerlen[$this->numpages]);
20942
			}
20943
			if (isset($this->transfmrk[$this->numpages])) {
20944
				unset($this->transfmrk[$this->numpages]);
20945
			}
20946
			if (isset($this->PageAnnots[$this->numpages])) {
20947
				unset($this->PageAnnots[$this->numpages]);
20948
			}
20949
			if (isset($this->newpagegroup[$this->numpages])) {
20950
				unset($this->newpagegroup[$this->numpages]);
20951
			}
20952
			if ($this->currpagegroup == $this->numpages) {
20953
				$this->currpagegroup = ($this->numpages - 1);
20954
			}
20955
			if (isset($this->pagegroups[$this->numpages])) {
20956
				unset($this->pagegroups[$this->numpages]);
20957
			}
20958
			if (isset($this->pageopen[$this->numpages])) {
20959
				unset($this->pageopen[$this->numpages]);
20960
			}
20961
		}
20962
		--$this->numpages;
20963
		$this->page = $this->numpages;
20964
		// adjust outlines
20965
		$tmpoutlines = $this->outlines;
20966
		foreach ($tmpoutlines as $key => $outline) {
20967
			if ($outline['p'] > $page) {
20968
				$this->outlines[$key]['p'] = $outline['p'] - 1;
20969
			} elseif ($outline['p'] == $page) {
20970
				unset($this->outlines[$key]);
20971
			}
20972
		}
20973
		// adjust dests
20974
		$tmpdests = $this->dests;
20975
		foreach ($tmpdests as $key => $dest) {
20976
			if ($dest['p'] > $page) {
20977
				$this->dests[$key]['p'] = $dest['p'] - 1;
20978
			} elseif ($dest['p'] == $page) {
20979
				unset($this->dests[$key]);
20980
			}
20981
		}
20982
		// adjust links
20983
		$tmplinks = $this->links;
20984
		foreach ($tmplinks as $key => $link) {
20985
			if ($link[0] > $page) {
20986
				$this->links[$key][0] = $link[0] - 1;
20987
			} elseif ($link[0] == $page) {
20988
				unset($this->links[$key]);
20989
			}
20990
		}
20991
		// adjust javascript
20992
		$tmpjavascript = $this->javascript;
20993
		global $jpage;
20994
		$jpage = $page;
20995
		$this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
20996
			create_function('$matches', 'global $jpage;
20997
			$pagenum = intval($matches[3]) + 1;
20998
			if ($pagenum >= $jpage) {
20999
				$newpage = ($pagenum - 1);
21000
			} elseif ($pagenum == $jpage) {
21001
				$newpage = 1;
21002
			} else {
21003
				$newpage = $pagenum;
21004
			}
21005
			--$newpage;
21006
			return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
21007
		// return to last page
21008
		if ($this->numpages > 0) {
21009
			$this->lastPage(true);
21010
		}
21011
		return true;
21012
	}
21013
 
21014
	/**
21015
	 * Clone the specified page to a new page.
21016
	 * @param $page (int) number of page to copy (0 = current page)
21017
	 * @return true in case of success, false in case of error.
21018
	 * @public
21019
	 * @since 4.9.015 (2010-04-20)
21020
	 */
21021
	public function copyPage($page=0) {
21022
		if ($page == 0) {
21023
			// default value
21024
			$page = $this->page;
21025
		}
21026
		if (($page < 1) OR ($page > $this->numpages)) {
21027
			return false;
21028
		}
21029
		// close the last page
21030
		$this->endPage();
21031
		// copy all page-related states
21032
		++$this->numpages;
21033
		$this->page = $this->numpages;
21034
		$this->setPageBuffer($this->page, $this->getPageBuffer($page));
21035
		$this->pagedim[$this->page] = $this->pagedim[$page];
21036
		$this->pagelen[$this->page] = $this->pagelen[$page];
21037
		$this->intmrk[$this->page] = $this->intmrk[$page];
21038
		$this->bordermrk[$this->page] = $this->bordermrk[$page];
21039
		$this->cntmrk[$this->page] = $this->cntmrk[$page];
21040
		$this->pageobjects[$this->page] = $this->pageobjects[$page];
21041
		$this->pageopen[$this->page] = false;
21042
		if (isset($this->footerpos[$page])) {
21043
			$this->footerpos[$this->page] = $this->footerpos[$page];
21044
		}
21045
		if (isset($this->footerlen[$page])) {
21046
			$this->footerlen[$this->page] = $this->footerlen[$page];
21047
		}
21048
		if (isset($this->transfmrk[$page])) {
21049
			$this->transfmrk[$this->page] = $this->transfmrk[$page];
21050
		}
21051
		if (isset($this->PageAnnots[$page])) {
21052
			$this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21053
		}
21054
		if (isset($this->newpagegroup[$page])) {
21055
			// start a new group
21056
			$this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21057
			$this->currpagegroup = $this->newpagegroup[$this->page];
21058
			$this->pagegroups[$this->currpagegroup] = 1;
21059
		} elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21060
			++$this->pagegroups[$this->currpagegroup];
21061
		}
21062
		// copy outlines
21063
		$tmpoutlines = $this->outlines;
21064
		foreach ($tmpoutlines as $key => $outline) {
21065
			if ($outline['p'] == $page) {
21066
				$this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']);
21067
			}
21068
		}
21069
		// copy links
21070
		$tmplinks = $this->links;
21071
		foreach ($tmplinks as $key => $link) {
21072
			if ($link[0] == $page) {
21073
				$this->links[] = array($this->page, $link[1]);
21074
			}
21075
		}
21076
		// return to last page
21077
		$this->lastPage(true);
21078
		return true;
21079
	}
21080
 
21081
	/**
21082
	 * Output a Table of Content Index (TOC).
21083
	 * This method must be called after all Bookmarks were set.
21084
	 * Before calling this method you have to open the page using the addTOCPage() method.
21085
	 * After calling this method you have to call endTOCPage() to close the TOC page.
21086
	 * You can override this method to achieve different styles.
21087
	 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21088
	 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
21089
	 * @param $filler (string) string used to fill the space between text and page number.
21090
	 * @param $toc_name (string) name to use for TOC bookmark.
21091
	 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21092
	 * @param $color (array) RGB color array for bookmark title (values from 0 to 255).
21093
	 * @public
21094
	 * @author Nicola Asuni
21095
	 * @since 4.5.000 (2009-01-02)
21096
	 * @see addTOCPage(), endTOCPage(), addHTMLTOC()
21097
	 */
21098
	public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21099
		$fontsize = $this->FontSizePt;
21100
		$fontfamily = $this->FontFamily;
21101
		$fontstyle = $this->FontStyle;
21102
		$w = $this->w - $this->lMargin - $this->rMargin;
21103
		$spacer = $this->GetStringWidth(chr(32)) * 4;
21104
		$lmargin = $this->lMargin;
21105
		$rmargin = $this->rMargin;
21106
		$x_start = $this->GetX();
21107
		$page_first = $this->page;
21108
		$current_page = $this->page;
21109
		$page_fill_start = false;
21110
		$page_fill_end = false;
21111
		$current_column = $this->current_column;
21112
		if (TCPDF_STATIC::empty_string($numbersfont)) {
21113
			$numbersfont = $this->default_monospaced_font;
21114
		}
21115
		if (TCPDF_STATIC::empty_string($filler)) {
21116
			$filler = ' ';
21117
		}
21118
		if (TCPDF_STATIC::empty_string($page)) {
21119
			$gap = ' ';
21120
		} else {
21121
			$gap = '';
21122
			if ($page < 1) {
21123
				$page = 1;
21124
			}
21125
		}
21126
		$this->SetFont($numbersfont, $fontstyle, $fontsize);
21127
		$numwidth = $this->GetStringWidth('00000');
21128
		$maxpage = 0; //used for pages on attached documents
21129
		foreach ($this->outlines as $key => $outline) {
21130
			// check for extra pages (used for attachments)
21131
			if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21132
				$outline['p'] += ($this->page - $page_first);
21133
			}
21134
			if ($this->rtl) {
21135
				$aligntext = 'R';
21136
				$alignnum = 'L';
21137
			} else {
21138
				$aligntext = 'L';
21139
				$alignnum = 'R';
21140
			}
21141
			if ($outline['l'] == 0) {
21142
				$this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21143
			} else {
21144
				$this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21145
			}
21146
			$this->SetTextColorArray($outline['c']);
21147
			// check for page break
21148
			$this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio));
21149
			// set margins and X position
21150
			if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21151
				$this->lMargin = $lmargin;
21152
				$this->rMargin = $rmargin;
21153
			} else {
21154
				if ($this->current_column != $current_column) {
21155
					if ($this->rtl) {
21156
						$x_start = $this->w - $this->columns[$this->current_column]['x'];
21157
					} else {
21158
						$x_start = $this->columns[$this->current_column]['x'];
21159
					}
21160
				}
21161
				$lmargin = $this->lMargin;
21162
				$rmargin = $this->rMargin;
21163
				$current_page = $this->page;
21164
				$current_column = $this->current_column;
21165
			}
21166
			$this->SetX($x_start);
21167
			$indent = ($spacer * $outline['l']);
21168
			if ($this->rtl) {
21169
				$this->x -= $indent;
21170
				$this->rMargin = $this->w - $this->x;
21171
			} else {
21172
				$this->x += $indent;
21173
				$this->lMargin = $this->x;
21174
			}
21175
			$link = $this->AddLink();
21176
			$this->SetLink($link, $outline['y'], $outline['p']);
21177
			// write the text
21178
			if ($this->rtl) {
21179
				$txt = ' '.$outline['t'];
21180
			} else {
21181
				$txt = $outline['t'].' ';
21182
			}
21183
			$this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21184
			if ($this->rtl) {
21185
				$tw = $this->x - $this->lMargin;
21186
			} else {
21187
				$tw = $this->w - $this->rMargin - $this->x;
21188
			}
21189
			$this->SetFont($numbersfont, $fontstyle, $fontsize);
21190
			if (TCPDF_STATIC::empty_string($page)) {
21191
				$pagenum = $outline['p'];
21192
			} else {
21193
				// placemark to be replaced with the correct number
21194
				$pagenum = '{#'.($outline['p']).'}';
21195
				if ($this->isUnicodeFont()) {
21196
					$pagenum = '{'.$pagenum.'}';
21197
				}
21198
				$maxpage = max($maxpage, $outline['p']);
21199
			}
21200
			$fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21201
			$numfills = floor($fw / $this->GetStringWidth($filler));
21202
			if ($numfills > 0) {
21203
				$rowfill = str_repeat($filler, $numfills);
21204
			} else {
21205
				$rowfill = '';
21206
			}
21207
			if ($this->rtl) {
21208
				$pagenum = $pagenum.$gap.$rowfill;
21209
			} else {
21210
				$pagenum = $rowfill.$gap.$pagenum;
21211
			}
21212
			// write the number
21213
			$this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21214
		}
21215
		$page_last = $this->getPage();
21216
		$numpages = ($page_last - $page_first + 1);
21217
		// account for booklet mode
21218
		if ($this->booklet) {
21219
			// check if a blank page is required before TOC
21220
			$page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21221
			$page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21222
			if ($page_fill_start) {
21223
				// add a page at the end (to be moved before TOC)
21224
				$this->addPage();
21225
				++$page_last;
21226
				++$numpages;
21227
			}
21228
			if ($page_fill_end) {
21229
				// add a page at the end
21230
				$this->addPage();
21231
				++$page_last;
21232
				++$numpages;
21233
			}
21234
		}
21235
		$maxpage = max($maxpage, $page_last);
21236
		if (!TCPDF_STATIC::empty_string($page)) {
21237
			for ($p = $page_first; $p <= $page_last; ++$p) {
21238
				// get page data
21239
				$temppage = $this->getPageBuffer($p);
21240
				for ($n = 1; $n <= $maxpage; ++$n) {
21241
					// update page numbers
21242
					$a = '{#'.$n.'}';
21243
					// get page number aliases
21244
					$pnalias = $this->getInternalPageNumberAliases($a);
21245
					// calculate replacement number
21246
					if (($n >= $page) AND ($n <= $this->numpages)) {
21247
						$np = $n + $numpages;
21248
					} else {
21249
						$np = $n;
21250
					}
21251
					$na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21252
					$nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21253
					// replace aliases with numbers
21254
					foreach ($pnalias['u'] as $u) {
21255
						$sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21256
						if ($this->rtl) {
21257
							$nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21258
						} else {
21259
							$nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21260
						}
21261
						$temppage = str_replace($u, $nr, $temppage);
21262
					}
21263
					foreach ($pnalias['a'] as $a) {
21264
						$sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21265
						if ($this->rtl) {
21266
							$nr = $na.' '.$sfill;
21267
						} else {
21268
							$nr = $sfill.' '.$na;
21269
						}
21270
						$temppage = str_replace($a, $nr, $temppage);
21271
					}
21272
				}
21273
				// save changes
21274
				$this->setPageBuffer($p, $temppage);
21275
			}
21276
			// move pages
21277
			$this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21278
			if ($page_fill_start) {
21279
				$this->movePage($page_last, $page_first);
21280
			}
21281
			for ($i = 0; $i < $numpages; ++$i) {
21282
				$this->movePage($page_last, $page);
21283
			}
21284
		}
21285
	}
21286
 
21287
	/**
21288
	 * Output a Table Of Content Index (TOC) using HTML templates.
21289
	 * This method must be called after all Bookmarks were set.
21290
	 * Before calling this method you have to open the page using the addTOCPage() method.
21291
	 * After calling this method you have to call endTOCPage() to close the TOC page.
21292
	 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21293
	 * @param $toc_name (string) name to use for TOC bookmark.
21294
	 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21295
	 * @param $correct_align (boolean) if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL)
21296
	 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21297
	 * @param $color (array) RGB color array for title (values from 0 to 255).
21298
	 * @public
21299
	 * @author Nicola Asuni
21300
	 * @since 5.0.001 (2010-05-06)
21301
	 * @see addTOCPage(), endTOCPage(), addTOC()
21302
	 */
21303
	public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21304
		$filler = ' ';
21305
		$prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21306
		$prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21307
		// set new style for link
21308
		$this->htmlLinkColorArray = array();
21309
		$this->htmlLinkFontStyle = '';
21310
		$page_first = $this->getPage();
21311
		$page_fill_start = false;
21312
		$page_fill_end = false;
21313
		// get the font type used for numbers in each template
21314
		$current_font = $this->FontFamily;
21315
		foreach ($templates as $level => $html) {
21316
			$dom = $this->getHtmlDomArray($html);
21317
			foreach ($dom as $key => $value) {
21318
				if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21319
					$this->SetFont($dom[($key - 1)]['fontname']);
21320
					$templates['F'.$level] = $this->isUnicodeFont();
21321
				}
21322
			}
21323
		}
21324
		$this->SetFont($current_font);
21325
		$maxpage = 0; //used for pages on attached documents
21326
		foreach ($this->outlines as $key => $outline) {
21327
			// get HTML template
21328
			$row = $templates[$outline['l']];
21329
			if (TCPDF_STATIC::empty_string($page)) {
21330
				$pagenum = $outline['p'];
21331
			} else {
21332
				// placemark to be replaced with the correct number
21333
				$pagenum = '{#'.($outline['p']).'}';
21334
				if ($templates['F'.$outline['l']]) {
21335
					$pagenum = '{'.$pagenum.'}';
21336
				}
21337
				$maxpage = max($maxpage, $outline['p']);
21338
			}
21339
			// replace templates with current values
21340
			$row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21341
			$row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21342
			// add link to page
21343
			$row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21344
			// write bookmark entry
21345
			$this->writeHTML($row, false, false, true, false, '');
21346
		}
21347
		// restore link styles
21348
		$this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21349
		$this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21350
		// move TOC page and replace numbers
21351
		$page_last = $this->getPage();
21352
		$numpages = ($page_last - $page_first + 1);
21353
		// account for booklet mode
21354
		if ($this->booklet) {
21355
			// check if a blank page is required before TOC
21356
			$page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21357
			$page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21358
			if ($page_fill_start) {
21359
				// add a page at the end (to be moved before TOC)
21360
				$this->addPage();
21361
				++$page_last;
21362
				++$numpages;
21363
			}
21364
			if ($page_fill_end) {
21365
				// add a page at the end
21366
				$this->addPage();
21367
				++$page_last;
21368
				++$numpages;
21369
			}
21370
		}
21371
		$maxpage = max($maxpage, $page_last);
21372
		if (!TCPDF_STATIC::empty_string($page)) {
21373
			for ($p = $page_first; $p <= $page_last; ++$p) {
21374
				// get page data
21375
				$temppage = $this->getPageBuffer($p);
21376
				for ($n = 1; $n <= $maxpage; ++$n) {
21377
					// update page numbers
21378
					$a = '{#'.$n.'}';
21379
					// get page number aliases
21380
					$pnalias = $this->getInternalPageNumberAliases($a);
21381
					// calculate replacement number
21382
					if ($n >= $page) {
21383
						$np = $n + $numpages;
21384
					} else {
21385
						$np = $n;
21386
					}
21387
					$na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21388
					$nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21389
					// replace aliases with numbers
21390
					foreach ($pnalias['u'] as $u) {
21391
						if ($correct_align) {
21392
							$sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21393
							if ($this->rtl) {
21394
								$nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21395
							} else {
21396
								$nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21397
							}
21398
						} else {
21399
							$nr = $nu;
21400
						}
21401
						$temppage = str_replace($u, $nr, $temppage);
21402
					}
21403
					foreach ($pnalias['a'] as $a) {
21404
						if ($correct_align) {
21405
							$sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21406
							if ($this->rtl) {
21407
								$nr = $na.' '.$sfill;
21408
							} else {
21409
								$nr = $sfill.' '.$na;
21410
							}
21411
						} else {
21412
							$nr = $na;
21413
						}
21414
						$temppage = str_replace($a, $nr, $temppage);
21415
					}
21416
				}
21417
				// save changes
21418
				$this->setPageBuffer($p, $temppage);
21419
			}
21420
			// move pages
21421
			$this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21422
			if ($page_fill_start) {
21423
				$this->movePage($page_last, $page_first);
21424
			}
21425
			for ($i = 0; $i < $numpages; ++$i) {
21426
				$this->movePage($page_last, $page);
21427
			}
21428
		}
21429
	}
21430
 
21431
	/**
21432
	 * Stores a copy of the current TCPDF object used for undo operation.
21433
	 * @public
21434
	 * @since 4.5.029 (2009-03-19)
21435
	 */
21436
	public function startTransaction() {
21437
		if (isset($this->objcopy)) {
21438
			// remove previous copy
21439
			$this->commitTransaction();
21440
		}
21441
		// record current page number and Y position
21442
		$this->start_transaction_page = $this->page;
21443
		$this->start_transaction_y = $this->y;
21444
		// clone current object
21445
		$this->objcopy = TCPDF_STATIC::objclone($this);
21446
	}
21447
 
21448
	/**
21449
	 * Delete the copy of the current TCPDF object used for undo operation.
21450
	 * @public
21451
	 * @since 4.5.029 (2009-03-19)
21452
	 */
21453
	public function commitTransaction() {
21454
		if (isset($this->objcopy)) {
21455
			$this->objcopy->_destroy(true, true);
21456
			unset($this->objcopy);
21457
		}
21458
	}
21459
 
21460
	/**
21461
	 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21462
	 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
21463
	 * @return TCPDF object.
21464
	 * @public
21465
	 * @since 4.5.029 (2009-03-19)
21466
	 */
21467
	public function rollbackTransaction($self=false) {
21468
		if (isset($this->objcopy)) {
21469
			if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
21470
				// truncate files to previous values
21471
				foreach ($this->objcopy->cache_file_length as $file => $length) {
21472
					$file = substr($file, 1);
21473
					$handle = fopen($file, 'r+');
21474
					ftruncate($handle, $length);
21475
				}
21476
			}
21477
			$this->_destroy(true, true);
21478
			if ($self) {
21479
				$objvars = get_object_vars($this->objcopy);
21480
				foreach ($objvars as $key => $value) {
21481
					$this->$key = $value;
21482
				}
21483
			}
21484
			return $this->objcopy;
21485
		}
21486
		return $this;
21487
	}
21488
 
21489
	// --- MULTI COLUMNS METHODS -----------------------
21490
 
21491
	/**
21492
	 * Set multiple columns of the same size
21493
	 * @param $numcols (int) number of columns (set to zero to disable columns mode)
21494
	 * @param $width (int) column width
21495
	 * @param $y (int) column starting Y position (leave empty for current Y position)
21496
	 * @public
21497
	 * @since 4.9.001 (2010-03-28)
21498
	 */
21499
	public function setEqualColumns($numcols=0, $width=0, $y='') {
21500
		$this->columns = array();
21501
		if ($numcols < 2) {
21502
			$numcols = 0;
21503
			$this->columns = array();
21504
		} else {
21505
			// maximum column width
21506
			$maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21507
			if (($width == 0) OR ($width > $maxwidth)) {
21508
				$width = $maxwidth;
21509
			}
21510
			if (TCPDF_STATIC::empty_string($y)) {
21511
				$y = $this->y;
21512
			}
21513
			// space between columns
21514
			$space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21515
			// fill the columns array (with, space, starting Y position)
21516
			for ($i = 0; $i < $numcols; ++$i) {
21517
				$this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21518
			}
21519
		}
21520
		$this->num_columns = $numcols;
21521
		$this->current_column = 0;
21522
		$this->column_start_page = $this->page;
21523
		$this->selectColumn(0);
21524
	}
21525
 
21526
	/**
21527
	 * Remove columns and reset page margins.
21528
	 * @public
21529
	 * @since 5.9.072 (2011-04-26)
21530
	 */
21531
	public function resetColumns() {
21532
		$this->lMargin = $this->original_lMargin;
21533
		$this->rMargin = $this->original_rMargin;
21534
		$this->setEqualColumns();
21535
	}
21536
 
21537
	/**
21538
	 * Set columns array.
21539
	 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21540
	 * @param $columns (array)
21541
	 * @public
21542
	 * @since 4.9.001 (2010-03-28)
21543
	 */
21544
	public function setColumnsArray($columns) {
21545
		$this->columns = $columns;
21546
		$this->num_columns = count($columns);
21547
		$this->current_column = 0;
21548
		$this->column_start_page = $this->page;
21549
		$this->selectColumn(0);
21550
	}
21551
 
21552
	/**
21553
	 * Set position at a given column
21554
	 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21555
	 * @public
21556
	 * @since 4.9.001 (2010-03-28)
21557
	 */
21558
	public function selectColumn($col='') {
21559
		if (is_string($col)) {
21560
			$col = $this->current_column;
21561
		} elseif ($col >= $this->num_columns) {
21562
			$col = 0;
21563
		}
21564
		$xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21565
		$enable_thead = false;
21566
		if ($this->num_columns > 1) {
21567
			if ($col != $this->current_column) {
21568
				// move Y pointer at the top of the column
21569
				if ($this->column_start_page == $this->page) {
21570
					$this->y = $this->columns[$col]['y'];
21571
				} else {
21572
					$this->y = $this->tMargin;
21573
				}
21574
				// Avoid to write table headers more than once
21575
				if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21576
					$enable_thead = true;
21577
					$this->maxselcol['page'] = $this->page;
21578
					$this->maxselcol['column'] = $col;
21579
				}
21580
			}
21581
			$xshift = $this->colxshift;
21582
			// set X position of the current column by case
21583
			$listindent = ($this->listindentlevel * $this->listindent);
21584
			// calculate column X position
21585
			$colpos = 0;
21586
			for ($i = 0; $i < $col; ++$i) {
21587
				$colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21588
			}
21589
			if ($this->rtl) {
21590
				$x = $this->w - $this->original_rMargin - $colpos;
21591
				$this->rMargin = ($this->w - $x + $listindent);
21592
				$this->lMargin = ($x - $this->columns[$col]['w']);
21593
				$this->x = $x - $listindent;
21594
			} else {
21595
				$x = $this->original_lMargin + $colpos;
21596
				$this->lMargin = ($x + $listindent);
21597
				$this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21598
				$this->x = $x + $listindent;
21599
			}
21600
			$this->columns[$col]['x'] = $x;
21601
		}
21602
		$this->current_column = $col;
21603
		// fix for HTML mode
21604
		$this->newline = true;
21605
		// print HTML table header (if any)
21606
		if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21607
			if ($enable_thead) {
21608
				// print table header
21609
				$this->writeHTML($this->thead, false, false, false, false, '');
21610
				$this->y += $xshift['s']['V'];
21611
				// store end of header position
21612
				if (!isset($this->columns[$col]['th'])) {
21613
					$this->columns[$col]['th'] = array();
21614
				}
21615
				$this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21616
				$this->lasth = 0;
21617
			} elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21618
				$this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21619
			}
21620
		}
21621
		// account for an html table cell over multiple columns
21622
		if ($this->rtl) {
21623
			$this->rMargin += $xshift['x'];
21624
			$this->x -= ($xshift['x'] + $xshift['p']['R']);
21625
		} else {
21626
			$this->lMargin += $xshift['x'];
21627
			$this->x += $xshift['x'] + $xshift['p']['L'];
21628
		}
21629
	}
21630
 
21631
	/**
21632
	 * Return the current column number
21633
	 * @return int current column number
21634
	 * @public
21635
	 * @since 5.5.011 (2010-07-08)
21636
	 */
21637
	public function getColumn() {
21638
		return $this->current_column;
21639
	}
21640
 
21641
	/**
21642
	 * Return the current number of columns.
21643
	 * @return int number of columns
21644
	 * @public
21645
	 * @since 5.8.018 (2010-08-25)
21646
	 */
21647
	public function getNumberOfColumns() {
21648
		return $this->num_columns;
21649
	}
21650
 
21651
	/**
21652
	 * Set Text rendering mode.
21653
	 * @param $stroke (int) outline size in user units (0 = disable).
21654
	 * @param $fill (boolean) if true fills the text (default).
21655
	 * @param $clip (boolean) if true activate clipping mode
21656
	 * @public
21657
	 * @since 4.9.008 (2009-04-02)
21658
	 */
21659
	public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21660
		// Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21661
		// convert text rendering parameters
21662
		if ($stroke < 0) {
21663
			$stroke = 0;
21664
		}
21665
		if ($fill === true) {
21666
			if ($stroke > 0) {
21667
				if ($clip === true) {
21668
					// Fill, then stroke text and add to path for clipping
21669
					$textrendermode = 6;
21670
				} else {
21671
					// Fill, then stroke text
21672
					$textrendermode = 2;
21673
				}
21674
				$textstrokewidth = $stroke;
21675
			} else {
21676
				if ($clip === true) {
21677
					// Fill text and add to path for clipping
21678
					$textrendermode = 4;
21679
				} else {
21680
					// Fill text
21681
					$textrendermode = 0;
21682
				}
21683
			}
21684
		} else {
21685
			if ($stroke > 0) {
21686
				if ($clip === true) {
21687
					// Stroke text and add to path for clipping
21688
					$textrendermode = 5;
21689
				} else {
21690
					// Stroke text
21691
					$textrendermode = 1;
21692
				}
21693
				$textstrokewidth = $stroke;
21694
			} else {
21695
				if ($clip === true) {
21696
					// Add text to path for clipping
21697
					$textrendermode = 7;
21698
				} else {
21699
					// Neither fill nor stroke text (invisible)
21700
					$textrendermode = 3;
21701
				}
21702
			}
21703
		}
21704
		$this->textrendermode = $textrendermode;
21705
		$this->textstrokewidth = $stroke;
21706
	}
21707
 
21708
	/**
21709
	 * Set parameters for drop shadow effect for text.
21710
	 * @param $params (array) Array of parameters: enabled (boolean) set to true to enable shadow; depth_w (float) shadow width in user units; depth_h (float) shadow height in user units; color (array) shadow color or false to use the stroke color; opacity (float) Alpha value: real value from 0 (transparent) to 1 (opaque); blend_mode (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity.
21711
	 * @since 5.9.174 (2012-07-25)
21712
	 * @public
21713
	*/
21714
	public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21715
		if (isset($params['enabled'])) {
21716
			$this->txtshadow['enabled'] = $params['enabled']?true:false;
21717
		} else {
21718
			$this->txtshadow['enabled'] = false;
21719
		}
21720
		if (isset($params['depth_w'])) {
21721
			$this->txtshadow['depth_w'] = floatval($params['depth_w']);
21722
		} else {
21723
			$this->txtshadow['depth_w'] = 0;
21724
		}
21725
		if (isset($params['depth_h'])) {
21726
			$this->txtshadow['depth_h'] = floatval($params['depth_h']);
21727
		} else {
21728
			$this->txtshadow['depth_h'] = 0;
21729
		}
21730
		if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21731
			$this->txtshadow['color'] = $params['color'];
21732
		} else {
21733
			$this->txtshadow['color'] = $this->strokecolor;
21734
		}
21735
		if (isset($params['opacity'])) {
21736
			$this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21737
		} else {
21738
			$this->txtshadow['opacity'] = 1;
21739
		}
21740
		if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
21741
			$this->txtshadow['blend_mode'] = $params['blend_mode'];
21742
		} else {
21743
			$this->txtshadow['blend_mode'] = 'Normal';
21744
		}
21745
		if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21746
			$this->txtshadow['enabled'] = false;
21747
		}
21748
	}
21749
 
21750
	/**
21751
	 * Return the text shadow parameters array.
21752
	 * @return Array of parameters.
21753
	 * @since 5.9.174 (2012-07-25)
21754
	 * @public
21755
	 */
21756
	public function getTextShadow() {
21757
		return $this->txtshadow;
21758
	}
21759
 
21760
	/**
21761
	 * Returns an array of chars containing soft hyphens.
21762
	 * @param $word (array) array of chars
21763
	 * @param $patterns (array) Array of hypenation patterns.
21764
	 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
21765
	 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
21766
	 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
21767
	 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
21768
	 * @param $charmax (int) Maximum length of broken piece of word.
21769
	 * @return array text with soft hyphens
21770
	 * @author Nicola Asuni
21771
	 * @since 4.9.012 (2010-04-12)
21772
	 * @protected
21773
	 */
21774
	protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21775
		$hyphenword = array(); // hyphens positions
21776
		$numchars = count($word);
21777
		if ($numchars <= $charmin) {
21778
			return $word;
21779
		}
21780
		$word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21781
		// some words will be returned as-is
21782
		$pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
21783
		if (preg_match($pattern, $word_string) > 0) {
21784
			// email
21785
			return $word;
21786
		}
21787
		$pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
21788
		if (preg_match($pattern, $word_string) > 0) {
21789
			// URL
21790
			return $word;
21791
		}
21792
		if (isset($dictionary[$word_string])) {
21793
			return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21794
		}
21795
		// suround word with '_' characters
21796
		$tmpword = array_merge(array(95), $word, array(95));
21797
		$tmpnumchars = $numchars + 2;
21798
		$maxpos = $tmpnumchars - $charmin;
21799
		for ($pos = 0; $pos < $maxpos; ++$pos) {
21800
			$imax = min(($tmpnumchars - $pos), $charmax);
21801
			for ($i = $charmin; $i <= $imax; ++$i) {
21802
				$subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21803
				if (isset($patterns[$subword])) {
21804
					$pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21805
					$pattern_length = count($pattern);
21806
					$digits = 1;
21807
					for ($j = 0; $j < $pattern_length; ++$j) {
21808
						// check if $pattern[$j] is a number
21809
						if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21810
							if ($j == 0) {
21811
								$zero = $pos - 1;
21812
							} else {
21813
								$zero = $pos + $j - $digits;
21814
							}
21815
							if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
21816
								$hyphenword[$zero] = TCPDF_FONTS::unichr($pattern[$j], $this->isunicode);
21817
							}
21818
							++$digits;
21819
						}
21820
					}
21821
				}
21822
			}
21823
		}
21824
		$inserted = 0;
21825
		$maxpos = $numchars - $rightmin;
21826
		for ($i = $leftmin; $i <= $maxpos; ++$i) {
21827
			if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
21828
				// 173 = soft hyphen character
21829
				array_splice($word, $i + $inserted, 0, 173);
21830
				++$inserted;
21831
			}
21832
		}
21833
		return $word;
21834
	}
21835
 
21836
	/**
21837
	 * Returns text with soft hyphens.
21838
	 * @param $text (string) text to process
21839
	 * @param $patterns (mixed) Array of hypenation patterns or a TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
21840
	 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
21841
	 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
21842
	 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
21843
	 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
21844
	 * @param $charmax (int) Maximum length of broken piece of word.
21845
	 * @return array text with soft hyphens
21846
	 * @author Nicola Asuni
21847
	 * @since 4.9.012 (2010-04-12)
21848
	 * @public
21849
	 */
21850
	public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21851
		$text = $this->unhtmlentities($text);
21852
		$word = array(); // last word
21853
		$txtarr = array(); // text to be returned
21854
		$intag = false; // true if we are inside an HTML tag
21855
		if (!is_array($patterns)) {
21856
			$patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
21857
		}
21858
		// get array of characters
21859
		$unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
21860
		// for each char
21861
		foreach ($unichars as $char) {
21862
			if ((!$intag) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
21863
				// letter character
21864
				$word[] = $char;
21865
			} else {
21866
				// other type of character
21867
				if (!TCPDF_STATIC::empty_string($word)) {
21868
					// hypenate the word
21869
					$txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
21870
					$word = array();
21871
				}
21872
				$txtarr[] = $char;
21873
				if (chr($char) == '<') {
21874
					// we are inside an HTML tag
21875
					$intag = true;
21876
				} elseif ($intag AND (chr($char) == '>')) {
21877
					// end of HTML tag
21878
					$intag = false;
21879
				}
21880
			}
21881
		}
21882
		if (!TCPDF_STATIC::empty_string($word)) {
21883
			// hypenate the word
21884
			$txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
21885
		}
21886
		// convert char array to string and return
21887
		return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
21888
	}
21889
 
21890
	/**
21891
	 * Enable/disable rasterization of vector images using ImageMagick library.
21892
	 * @param $mode (boolean) if true enable rasterization, false otherwise.
21893
	 * @public
21894
	 * @since 5.0.000 (2010-04-27)
21895
	 */
21896
	public function setRasterizeVectorImages($mode) {
21897
		$this->rasterize_vector_images = $mode;
21898
	}
21899
 
21900
	/**
21901
	 * Enable or disable default option for font subsetting.
21902
	 * @param $enable (boolean) if true enable font subsetting by default.
21903
	 * @author Nicola Asuni
21904
	 * @public
21905
	 * @since 5.3.002 (2010-06-07)
21906
	 */
21907
	public function setFontSubsetting($enable=true) {
21908
		if ($this->pdfa_mode) {
21909
			$this->font_subsetting = false;
21910
		} else {
21911
			$this->font_subsetting = $enable ? true : false;
21912
		}
21913
	}
21914
 
21915
	/**
21916
	 * Return the default option for font subsetting.
21917
	 * @return boolean default font subsetting state.
21918
	 * @author Nicola Asuni
21919
	 * @public
21920
	 * @since 5.3.002 (2010-06-07)
21921
	 */
21922
	public function getFontSubsetting() {
21923
		return $this->font_subsetting;
21924
	}
21925
 
21926
	/**
21927
	 * Left trim the input string
21928
	 * @param $str (string) string to trim
21929
	 * @param $replace (string) string that replace spaces.
21930
	 * @return left trimmed string
21931
	 * @author Nicola Asuni
21932
	 * @public
21933
	 * @since 5.8.000 (2010-08-11)
21934
	 */
21935
	public function stringLeftTrim($str, $replace='') {
21936
		return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
21937
	}
21938
 
21939
	/**
21940
	 * Right trim the input string
21941
	 * @param $str (string) string to trim
21942
	 * @param $replace (string) string that replace spaces.
21943
	 * @return right trimmed string
21944
	 * @author Nicola Asuni
21945
	 * @public
21946
	 * @since 5.8.000 (2010-08-11)
21947
	 */
21948
	public function stringRightTrim($str, $replace='') {
21949
		return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
21950
	}
21951
 
21952
	/**
21953
	 * Trim the input string
21954
	 * @param $str (string) string to trim
21955
	 * @param $replace (string) string that replace spaces.
21956
	 * @return trimmed string
21957
	 * @author Nicola Asuni
21958
	 * @public
21959
	 * @since 5.8.000 (2010-08-11)
21960
	 */
21961
	public function stringTrim($str, $replace='') {
21962
		$str = $this->stringLeftTrim($str, $replace);
21963
		$str = $this->stringRightTrim($str, $replace);
21964
		return $str;
21965
	}
21966
 
21967
	/**
21968
	 * Return true if the current font is unicode type.
21969
	 * @return true for unicode font, false otherwise.
21970
	 * @author Nicola Asuni
21971
	 * @public
21972
	 * @since 5.8.002 (2010-08-14)
21973
	 */
21974
	public function isUnicodeFont() {
21975
		return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
21976
	}
21977
 
21978
	/**
21979
	 * Return normalized font name
21980
	 * @param $fontfamily (string) property string containing font family names
21981
	 * @return string normalized font name
21982
	 * @author Nicola Asuni
21983
	 * @public
21984
	 * @since 5.8.004 (2010-08-17)
21985
	 */
21986
	public function getFontFamilyName($fontfamily) {
21987
		// remove spaces and symbols
21988
		$fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
21989
		// extract all font names
21990
		$fontslist = preg_split('/[,]/', $fontfamily);
21991
		// find first valid font name
21992
		foreach ($fontslist as $font) {
21993
			// replace font variations
21994
			$font = preg_replace('/italic$/', 'I', $font);
21995
			$font = preg_replace('/oblique$/', 'I', $font);
21996
			$font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
21997
			// replace common family names and core fonts
21998
			$pattern = array();
21999
			$replacement = array();
22000
			$pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22001
			$replacement[] = 'times';
22002
			$pattern[] = '/^sansserif/';
22003
			$replacement[] = 'helvetica';
22004
			$pattern[] = '/^monospace/';
22005
			$replacement[] = 'courier';
22006
			$font = preg_replace($pattern, $replacement, $font);
22007
			if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22008
				return $font;
22009
			}
22010
		}
22011
		// return current font as default
22012
		return $this->CurrentFont['fontkey'];
22013
	}
22014
 
22015
	/**
22016
	 * Start a new XObject Template.
22017
	 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22018
	 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22019
	 * Note: X,Y coordinates will be reset to 0,0.
22020
	 * @param $w (int) Template width in user units (empty string or zero = page width less margins).
22021
	 * @param $h (int) Template height in user units (empty string or zero = page height less margins).
22022
	 * @param $group (mixed) Set transparency group. Can be a boolean value or an array specifying optional parameters: 'CS' (solour space name), 'I' (boolean flag to indicate isolated group) and 'K' (boolean flag to indicate knockout group).
22023
	 * @return int the XObject Template ID in case of success or false in case of error.
22024
	 * @author Nicola Asuni
22025
	 * @public
22026
	 * @since 5.8.017 (2010-08-24)
22027
	 * @see endTemplate(), printTemplate()
22028
	 */
22029
	public function startTemplate($w=0, $h=0, $group=false) {
22030
		if ($this->inxobj) {
22031
			// we are already inside an XObject template
22032
			return false;
22033
		}
22034
		$this->inxobj = true;
22035
		++$this->n;
22036
		// XObject ID
22037
		$this->xobjid = 'XT'.$this->n;
22038
		// object ID
22039
		$this->xobjects[$this->xobjid] = array('n' => $this->n);
22040
		// store current graphic state
22041
		$this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22042
		// initialize data
22043
		$this->xobjects[$this->xobjid]['intmrk'] = 0;
22044
		$this->xobjects[$this->xobjid]['transfmrk'] = array();
22045
		$this->xobjects[$this->xobjid]['outdata'] = '';
22046
		$this->xobjects[$this->xobjid]['xobjects'] = array();
22047
		$this->xobjects[$this->xobjid]['images'] = array();
22048
		$this->xobjects[$this->xobjid]['fonts'] = array();
22049
		$this->xobjects[$this->xobjid]['annotations'] = array();
22050
		$this->xobjects[$this->xobjid]['extgstates'] = array();
22051
		$this->xobjects[$this->xobjid]['gradients'] = array();
22052
		$this->xobjects[$this->xobjid]['spot_colors'] = array();
22053
		// set new environment
22054
		$this->num_columns = 1;
22055
		$this->current_column = 0;
22056
		$this->SetAutoPageBreak(false);
22057
		if (($w === '') OR ($w <= 0)) {
22058
			$w = $this->w - $this->lMargin - $this->rMargin;
22059
		}
22060
		if (($h === '') OR ($h <= 0)) {
22061
			$h = $this->h - $this->tMargin - $this->bMargin;
22062
		}
22063
		$this->xobjects[$this->xobjid]['x'] = 0;
22064
		$this->xobjects[$this->xobjid]['y'] = 0;
22065
		$this->xobjects[$this->xobjid]['w'] = $w;
22066
		$this->xobjects[$this->xobjid]['h'] = $h;
22067
		$this->w = $w;
22068
		$this->h = $h;
22069
		$this->wPt = $this->w * $this->k;
22070
		$this->hPt = $this->h * $this->k;
22071
		$this->fwPt = $this->wPt;
22072
		$this->fhPt = $this->hPt;
22073
		$this->x = 0;
22074
		$this->y = 0;
22075
		$this->lMargin = 0;
22076
		$this->rMargin = 0;
22077
		$this->tMargin = 0;
22078
		$this->bMargin = 0;
22079
		// set group mode
22080
		$this->xobjects[$this->xobjid]['group'] = $group;
22081
		return $this->xobjid;
22082
	}
22083
 
22084
	/**
22085
	 * End the current XObject Template started with startTemplate() and restore the previous graphic state.
22086
	 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22087
	 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22088
	 * @return int the XObject Template ID in case of success or false in case of error.
22089
	 * @author Nicola Asuni
22090
	 * @public
22091
	 * @since 5.8.017 (2010-08-24)
22092
	 * @see startTemplate(), printTemplate()
22093
	 */
22094
	public function endTemplate() {
22095
		if (!$this->inxobj) {
22096
			// we are not inside a template
22097
			return false;
22098
		}
22099
		$this->inxobj = false;
22100
		// restore previous graphic state
22101
		$this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22102
		return $this->xobjid;
22103
	}
22104
 
22105
	/**
22106
	 * Print an XObject Template.
22107
	 * You can print an XObject Template inside the currently opened Template.
22108
	 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22109
	 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22110
	 * @param $id (string) The ID of XObject Template to print.
22111
	 * @param $x (int) X position in user units (empty string = current x position)
22112
	 * @param $y (int) Y position in user units (empty string = current y position)
22113
	 * @param $w (int) Width in user units (zero = remaining page width)
22114
	 * @param $h (int) Height in user units (zero = remaining page height)
22115
	 * @param $align (string) Indicates the alignment of the pointer next to template insertion relative to template height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
22116
	 * @param $palign (string) Allows to center or align the template on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
22117
	 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
22118
	 * @author Nicola Asuni
22119
	 * @public
22120
	 * @since 5.8.017 (2010-08-24)
22121
	 * @see startTemplate(), endTemplate()
22122
	 */
22123
	public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22124
		if ($this->state != 2) {
22125
			 return;
22126
		}
22127
		if (!isset($this->xobjects[$id])) {
22128
			$this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22129
		}
22130
		if ($this->inxobj) {
22131
			if ($id == $this->xobjid) {
22132
				// close current template
22133
				$this->endTemplate();
22134
			} else {
22135
				// use the template as resource for the template currently opened
22136
				$this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22137
			}
22138
		}
22139
		// set default values
22140
		if ($x === '') {
22141
			$x = $this->x;
22142
		}
22143
		if ($y === '') {
22144
			$y = $this->y;
22145
		}
22146
		// check page for no-write regions and adapt page margins if necessary
22147
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
22148
		$ow = $this->xobjects[$id]['w'];
22149
		$oh = $this->xobjects[$id]['h'];
22150
		// calculate template width and height on document
22151
		if (($w <= 0) AND ($h <= 0)) {
22152
			$w = $ow;
22153
			$h = $oh;
22154
		} elseif ($w <= 0) {
22155
			$w = $h * $ow / $oh;
22156
		} elseif ($h <= 0) {
22157
			$h = $w * $oh / $ow;
22158
		}
22159
		// fit the template on available space
22160
		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22161
		// set page alignment
22162
		$rb_y = $y + $h;
22163
		// set alignment
22164
		if ($this->rtl) {
22165
			if ($palign == 'L') {
22166
				$xt = $this->lMargin;
22167
			} elseif ($palign == 'C') {
22168
				$xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22169
			} elseif ($palign == 'R') {
22170
				$xt = $this->w - $this->rMargin - $w;
22171
			} else {
22172
				$xt = $x - $w;
22173
			}
22174
			$rb_x = $xt;
22175
		} else {
22176
			if ($palign == 'L') {
22177
				$xt = $this->lMargin;
22178
			} elseif ($palign == 'C') {
22179
				$xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22180
			} elseif ($palign == 'R') {
22181
				$xt = $this->w - $this->rMargin - $w;
22182
			} else {
22183
				$xt = $x;
22184
			}
22185
			$rb_x = $xt + $w;
22186
		}
22187
		// print XObject Template + Transformation matrix
22188
		$this->StartTransform();
22189
		// translate and scale
22190
		$sx = ($w / $this->xobjects[$id]['w']);
22191
		$sy = ($h / $this->xobjects[$id]['h']);
22192
		$tm = array();
22193
		$tm[0] = $sx;
22194
		$tm[1] = 0;
22195
		$tm[2] = 0;
22196
		$tm[3] = $sy;
22197
		$tm[4] = $xt * $this->k;
22198
		$tm[5] = ($this->h - $h - $y) * $this->k;
22199
		$this->Transform($tm);
22200
		// set object
22201
		$this->_out('/'.$id.' Do');
22202
		$this->StopTransform();
22203
		// add annotations
22204
		if (!empty($this->xobjects[$id]['annotations'])) {
22205
			foreach ($this->xobjects[$id]['annotations'] as $annot) {
22206
				// transform original coordinates
22207
				$coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22208
				$ax = ($coordlt[4] / $this->k);
22209
				$ay = ($this->h - $h - ($coordlt[5] / $this->k));
22210
				$coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22211
				$aw = ($coordrb[4] / $this->k) - $ax;
22212
				$ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22213
				$this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22214
			}
22215
		}
22216
		// set pointer to align the next text/objects
22217
		switch($align) {
22218
			case 'T': {
22219
				$this->y = $y;
22220
				$this->x = $rb_x;
22221
				break;
22222
			}
22223
			case 'M': {
22224
				$this->y = $y + round($h/2);
22225
				$this->x = $rb_x;
22226
				break;
22227
			}
22228
			case 'B': {
22229
				$this->y = $rb_y;
22230
				$this->x = $rb_x;
22231
				break;
22232
			}
22233
			case 'N': {
22234
				$this->SetY($rb_y);
22235
				break;
22236
			}
22237
			default:{
22238
				break;
22239
			}
22240
		}
22241
	}
22242
 
22243
	/**
22244
	 * Set the percentage of character stretching.
22245
	 * @param $perc (int) percentage of stretching (100 = no stretching)
22246
	 * @author Nicola Asuni
22247
	 * @public
22248
	 * @since 5.9.000 (2010-09-29)
22249
	 */
22250
	public function setFontStretching($perc=100) {
22251
		$this->font_stretching = $perc;
22252
	}
22253
 
22254
	/**
22255
	 * Get the percentage of character stretching.
22256
	 * @return float stretching value
22257
	 * @author Nicola Asuni
22258
	 * @public
22259
	 * @since 5.9.000 (2010-09-29)
22260
	 */
22261
	public function getFontStretching() {
22262
		return $this->font_stretching;
22263
	}
22264
 
22265
	/**
22266
	 * Set the amount to increase or decrease the space between characters in a text.
22267
	 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
22268
	 * @author Nicola Asuni
22269
	 * @public
22270
	 * @since 5.9.000 (2010-09-29)
22271
	 */
22272
	public function setFontSpacing($spacing=0) {
22273
		$this->font_spacing = $spacing;
22274
	}
22275
 
22276
	/**
22277
	 * Get the amount to increase or decrease the space between characters in a text.
22278
	 * @return int font spacing (tracking) value
22279
	 * @author Nicola Asuni
22280
	 * @public
22281
	 * @since 5.9.000 (2010-09-29)
22282
	 */
22283
	public function getFontSpacing() {
22284
		return $this->font_spacing;
22285
	}
22286
 
22287
	/**
22288
	 * Return an array of no-write page regions
22289
	 * @return array of no-write page regions
22290
	 * @author Nicola Asuni
22291
	 * @public
22292
	 * @since 5.9.003 (2010-10-13)
22293
	 * @see setPageRegions(), addPageRegion()
22294
	 */
22295
	public function getPageRegions() {
22296
		return $this->page_regions;
22297
	}
22298
 
22299
	/**
22300
	 * Set no-write regions on page.
22301
	 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22302
	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22303
	 * You can set multiple regions for the same page.
22304
	 * @param $regions (array) array of no-write regions. For each region you can define an array as follow: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). Omit this parameter to remove all regions.
22305
	 * @author Nicola Asuni
22306
	 * @public
22307
	 * @since 5.9.003 (2010-10-13)
22308
	 * @see addPageRegion(), getPageRegions()
22309
	 */
22310
	public function setPageRegions($regions=array()) {
22311
		// empty current regions array
22312
		$this->page_regions = array();
22313
		// add regions
22314
		foreach ($regions as $data) {
22315
			$this->addPageRegion($data);
22316
		}
22317
	}
22318
 
22319
	/**
22320
	 * Add a single no-write region on selected page.
22321
	 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22322
	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22323
	 * You can set multiple regions for the same page.
22324
	 * @param $region (array) array of a single no-write region array: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right).
22325
	 * @author Nicola Asuni
22326
	 * @public
22327
	 * @since 5.9.003 (2010-10-13)
22328
	 * @see setPageRegions(), getPageRegions()
22329
	 */
22330
	public function addPageRegion($region) {
22331
		if (!isset($region['page']) OR empty($region['page'])) {
22332
			$region['page'] = $this->page;
22333
		}
22334
		if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22335
			AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22336
			AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22337
			$this->page_regions[] = $region;
22338
		}
22339
	}
22340
 
22341
	/**
22342
	 * Remove a single no-write region.
22343
	 * @param $key (int) region key
22344
	 * @author Nicola Asuni
22345
	 * @public
22346
	 * @since 5.9.003 (2010-10-13)
22347
	 * @see setPageRegions(), getPageRegions()
22348
	 */
22349
	public function removePageRegion($key) {
22350
		if (isset($this->page_regions[$key])) {
22351
			unset($this->page_regions[$key]);
22352
		}
22353
	}
22354
 
22355
	/**
22356
	 * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22357
	 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22358
	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22359
	 * @param $h (float) height of the text/image/object to print in user units
22360
	 * @param $x (float) current X coordinate in user units
22361
	 * @param $y (float) current Y coordinate in user units
22362
	 * @return array($x, $y)
22363
	 * @author Nicola Asuni
22364
	 * @protected
22365
	 * @since 5.9.003 (2010-10-13)
22366
	 */
22367
	protected function checkPageRegions($h, $x, $y) {
22368
		// set default values
22369
		if ($x === '') {
22370
			$x = $this->x;
22371
		}
22372
		if ($y === '') {
22373
			$y = $this->y;
22374
		}
22375
		if (!$this->check_page_regions OR empty($this->page_regions)) {
22376
			// no page regions defined
22377
			return array($x, $y);
22378
		}
22379
		if (empty($h)) {
22380
			$h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
22381
		}
22382
		// check for page break
22383
		if ($this->checkPageBreak($h, $y)) {
22384
			// the content will be printed on a new page
22385
			$x = $this->x;
22386
			$y = $this->y;
22387
		}
22388
		if ($this->num_columns > 1) {
22389
			if ($this->rtl) {
22390
				$this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22391
			} else {
22392
				$this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22393
			}
22394
		} else {
22395
			if ($this->rtl) {
22396
				$this->lMargin = max($this->clMargin, $this->original_lMargin);
22397
			} else {
22398
				$this->rMargin = max($this->crMargin, $this->original_rMargin);
22399
			}
22400
		}
22401
		// adjust coordinates and page margins
22402
		foreach ($this->page_regions as $regid => $regdata) {
22403
			if ($regdata['page'] == $this->page) {
22404
				// check region boundaries
22405
				if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22406
					// Y is inside the region
22407
					$minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22408
					$yt = max($y, $regdata['yt']);
22409
					$yb = min(($yt + $h), $regdata['yb']);
22410
					$xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22411
					$xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22412
					if ($regdata['side'] == 'L') { // left side
22413
						$new_margin = max($xt, $xb);
22414
						if ($this->lMargin < $new_margin) {
22415
							if ($this->rtl) {
22416
								// adjust left page margin
22417
								$this->lMargin = max(0, $new_margin);
22418
							}
22419
							if ($x < $new_margin) {
22420
								// adjust x position
22421
								$x = $new_margin;
22422
								if ($new_margin > ($this->w - $this->rMargin)) {
22423
									// adjust y position
22424
									$y = $regdata['yb'] - $h;
22425
								}
22426
							}
22427
						}
22428
					} elseif ($regdata['side'] == 'R') { // right side
22429
						$new_margin = min($xt, $xb);
22430
						if (($this->w - $this->rMargin) > $new_margin) {
22431
							if (!$this->rtl) {
22432
								// adjust right page margin
22433
								$this->rMargin = max(0, ($this->w - $new_margin));
22434
							}
22435
							if ($x > $new_margin) {
22436
								// adjust x position
22437
								$x = $new_margin;
22438
								if ($new_margin > $this->lMargin) {
22439
									// adjust y position
22440
									$y = $regdata['yb'] - $h;
22441
								}
22442
							}
22443
						}
22444
					}
22445
				}
22446
			}
22447
		}
22448
		return array($x, $y);
22449
	}
22450
 
22451
	// --- SVG METHODS ---------------------------------------------------------
22452
 
22453
	/**
22454
	 * Embedd a Scalable Vector Graphics (SVG) image.
22455
	 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22456
	 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
22457
	 * @param $x (float) Abscissa of the upper-left corner.
22458
	 * @param $y (float) Ordinate of the upper-left corner.
22459
	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22460
	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22461
	 * @param $link (mixed) URL or identifier returned by AddLink().
22462
	 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> If the alignment is an empty string, then the pointer will be restored on the starting SVG position.
22463
	 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
22464
	 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
22465
	 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
22466
	 * @author Nicola Asuni
22467
	 * @since 5.0.000 (2010-05-02)
22468
	 * @public
22469
	 */
22470
	public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22471
		if ($this->state != 2) {
22472
			 return;
22473
		}
22474
		if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22475
			// convert SVG to raster image using GD or ImageMagick libraries
22476
			return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22477
		}
22478
		if ($file{0} === '@') { // image from string
22479
			$this->svgdir = '';
22480
			$svgdata = substr($file, 1);
22481
		} else { // SVG file
22482
			$this->svgdir = dirname($file);
22483
			$svgdata = file_get_contents($file);
22484
		}
22485
		if ($svgdata === false) {
22486
			$this->Error('SVG file not found: '.$file);
22487
		}
22488
		if ($x === '') {
22489
			$x = $this->x;
22490
		}
22491
		if ($y === '') {
22492
			$y = $this->y;
22493
		}
22494
		// check page for no-write regions and adapt page margins if necessary
22495
		list($x, $y) = $this->checkPageRegions($h, $x, $y);
22496
		$k = $this->k;
22497
		$ox = 0;
22498
		$oy = 0;
22499
		$ow = $w;
22500
		$oh = $h;
22501
		$aspect_ratio_align = 'xMidYMid';
22502
		$aspect_ratio_ms = 'meet';
22503
		$regs = array();
22504
		// get original image width and height
22505
		preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22506
		if (isset($regs[1]) AND !empty($regs[1])) {
22507
			$tmp = array();
22508
			if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22509
				$ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22510
			}
22511
			$tmp = array();
22512
			if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22513
				$oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22514
			}
22515
			$tmp = array();
22516
			if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22517
				$ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22518
			}
22519
			$tmp = array();
22520
			if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22521
				$oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22522
			}
22523
			$tmp = array();
22524
			$view_box = array();
22525
			if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22526
				if (count($tmp) == 5) {
22527
					array_shift($tmp);
22528
					foreach ($tmp as $key => $val) {
22529
						$view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22530
					}
22531
					$ox = $view_box[0];
22532
					$oy = $view_box[1];
22533
				}
22534
				// get aspect ratio
22535
				$tmp = array();
22536
				if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22537
					$aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22538
					switch (count($aspect_ratio)) {
22539
						case 3: {
22540
							$aspect_ratio_align = $aspect_ratio[1];
22541
							$aspect_ratio_ms = $aspect_ratio[2];
22542
							break;
22543
						}
22544
						case 2: {
22545
							$aspect_ratio_align = $aspect_ratio[0];
22546
							$aspect_ratio_ms = $aspect_ratio[1];
22547
							break;
22548
						}
22549
						case 1: {
22550
							$aspect_ratio_align = $aspect_ratio[0];
22551
							$aspect_ratio_ms = 'meet';
22552
							break;
22553
						}
22554
					}
22555
				}
22556
			}
22557
		}
22558
		// calculate image width and height on document
22559
		if (($w <= 0) AND ($h <= 0)) {
22560
			// convert image size to document unit
22561
			$w = $ow;
22562
			$h = $oh;
22563
		} elseif ($w <= 0) {
22564
			$w = $h * $ow / $oh;
22565
		} elseif ($h <= 0) {
22566
			$h = $w * $oh / $ow;
22567
		}
22568
		// fit the image on available space
22569
		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22570
		if ($this->rasterize_vector_images) {
22571
			// convert SVG to raster image using GD or ImageMagick libraries
22572
			return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22573
		}
22574
		// set alignment
22575
		$this->img_rb_y = $y + $h;
22576
		// set alignment
22577
		if ($this->rtl) {
22578
			if ($palign == 'L') {
22579
				$ximg = $this->lMargin;
22580
			} elseif ($palign == 'C') {
22581
				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22582
			} elseif ($palign == 'R') {
22583
				$ximg = $this->w - $this->rMargin - $w;
22584
			} else {
22585
				$ximg = $x - $w;
22586
			}
22587
			$this->img_rb_x = $ximg;
22588
		} else {
22589
			if ($palign == 'L') {
22590
				$ximg = $this->lMargin;
22591
			} elseif ($palign == 'C') {
22592
				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22593
			} elseif ($palign == 'R') {
22594
				$ximg = $this->w - $this->rMargin - $w;
22595
			} else {
22596
				$ximg = $x;
22597
			}
22598
			$this->img_rb_x = $ximg + $w;
22599
		}
22600
		// store current graphic vars
22601
		$gvars = $this->getGraphicVars();
22602
		// store SVG position and scale factors
22603
		$svgoffset_x = ($ximg - $ox) * $this->k;
22604
		$svgoffset_y = -($y - $oy) * $this->k;
22605
		if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22606
			$ow = $view_box[2];
22607
			$oh = $view_box[3];
22608
		} else {
22609
			if ($ow <= 0) {
22610
				$ow = $w;
22611
			}
22612
			if ($oh <= 0) {
22613
				$oh = $h;
22614
			}
22615
		}
22616
		$svgscale_x = $w / $ow;
22617
		$svgscale_y = $h / $oh;
22618
		// scaling and alignment
22619
		if ($aspect_ratio_align != 'none') {
22620
			// store current scaling values
22621
			$svgscale_old_x = $svgscale_x;
22622
			$svgscale_old_y = $svgscale_y;
22623
			// force uniform scaling
22624
			if ($aspect_ratio_ms == 'slice') {
22625
				// the entire viewport is covered by the viewBox
22626
				if ($svgscale_x > $svgscale_y) {
22627
					$svgscale_y = $svgscale_x;
22628
				} elseif ($svgscale_x < $svgscale_y) {
22629
					$svgscale_x = $svgscale_y;
22630
				}
22631
			} else { // meet
22632
				// the entire viewBox is visible within the viewport
22633
				if ($svgscale_x < $svgscale_y) {
22634
					$svgscale_y = $svgscale_x;
22635
				} elseif ($svgscale_x > $svgscale_y) {
22636
					$svgscale_x = $svgscale_y;
22637
				}
22638
			}
22639
			// correct X alignment
22640
			switch (substr($aspect_ratio_align, 1, 3)) {
22641
				case 'Min': {
22642
					// do nothing
22643
					break;
22644
				}
22645
				case 'Max': {
22646
					$svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22647
					break;
22648
				}
22649
				default:
22650
				case 'Mid': {
22651
					$svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22652
					break;
22653
				}
22654
			}
22655
			// correct Y alignment
22656
			switch (substr($aspect_ratio_align, 5)) {
22657
				case 'Min': {
22658
					// do nothing
22659
					break;
22660
				}
22661
				case 'Max': {
22662
					$svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22663
					break;
22664
				}
22665
				default:
22666
				case 'Mid': {
22667
					$svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22668
					break;
22669
				}
22670
			}
22671
		}
22672
		// store current page break mode
22673
		$page_break_mode = $this->AutoPageBreak;
22674
		$page_break_margin = $this->getBreakMargin();
22675
		$cell_padding = $this->cell_padding;
22676
		$this->SetCellPadding(0);
22677
		$this->SetAutoPageBreak(false);
22678
		// save the current graphic state
22679
		$this->_out('q'.$this->epsmarker);
22680
		// set initial clipping mask
22681
		$this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
22682
		// scale and translate
22683
		$e = $ox * $this->k * (1 - $svgscale_x);
22684
		$f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22685
		$this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22686
		// creates a new XML parser to be used by the other XML functions
22687
		$this->parser = xml_parser_create('UTF-8');
22688
		// the following function allows to use parser inside object
22689
		xml_set_object($this->parser, $this);
22690
		// disable case-folding for this XML parser
22691
		xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22692
		// sets the element handler functions for the XML parser
22693
		xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22694
		// sets the character data handler function for the XML parser
22695
		xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22696
		// start parsing an XML document
22697
		if (!xml_parse($this->parser, $svgdata)) {
22698
			$error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
22699
			$this->Error($error_message);
22700
		}
22701
		// free this XML parser
22702
		xml_parser_free($this->parser);
22703
		// restore previous graphic state
22704
		$this->_out($this->epsmarker.'Q');
22705
		// restore graphic vars
22706
		$this->setGraphicVars($gvars);
22707
		$this->lasth = $gvars['lasth'];
22708
		if (!empty($border)) {
22709
			$bx = $this->x;
22710
			$by = $this->y;
22711
			$this->x = $ximg;
22712
			if ($this->rtl) {
22713
				$this->x += $w;
22714
			}
22715
			$this->y = $y;
22716
			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22717
			$this->x = $bx;
22718
			$this->y = $by;
22719
		}
22720
		if ($link) {
22721
			$this->Link($ximg, $y, $w, $h, $link, 0);
22722
		}
22723
		// set pointer to align the next text/objects
22724
		switch($align) {
22725
			case 'T':{
22726
				$this->y = $y;
22727
				$this->x = $this->img_rb_x;
22728
				break;
22729
			}
22730
			case 'M':{
22731
				$this->y = $y + round($h/2);
22732
				$this->x = $this->img_rb_x;
22733
				break;
22734
			}
22735
			case 'B':{
22736
				$this->y = $this->img_rb_y;
22737
				$this->x = $this->img_rb_x;
22738
				break;
22739
			}
22740
			case 'N':{
22741
				$this->SetY($this->img_rb_y);
22742
				break;
22743
			}
22744
			default:{
22745
				// restore pointer to starting position
22746
				$this->x = $gvars['x'];
22747
				$this->y = $gvars['y'];
22748
				$this->page = $gvars['page'];
22749
				$this->current_column = $gvars['current_column'];
22750
				$this->tMargin = $gvars['tMargin'];
22751
				$this->bMargin = $gvars['bMargin'];
22752
				$this->w = $gvars['w'];
22753
				$this->h = $gvars['h'];
22754
				$this->wPt = $gvars['wPt'];
22755
				$this->hPt = $gvars['hPt'];
22756
				$this->fwPt = $gvars['fwPt'];
22757
				$this->fhPt = $gvars['fhPt'];
22758
				break;
22759
			}
22760
		}
22761
		$this->endlinex = $this->img_rb_x;
22762
		// restore page break
22763
		$this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22764
		$this->cell_padding = $cell_padding;
22765
	}
22766
 
22767
	/**
22768
	 * Convert SVG transformation matrix to PDF.
22769
	 * @param $tm (array) original SVG transformation matrix
22770
	 * @return array transformation matrix
22771
	 * @protected
22772
	 * @since 5.0.000 (2010-05-02)
22773
	 */
22774
	protected function convertSVGtMatrix($tm) {
22775
		$a = $tm[0];
22776
		$b = -$tm[1];
22777
		$c = -$tm[2];
22778
		$d = $tm[3];
22779
		$e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
22780
		$f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
22781
		$x = 0;
22782
		$y = $this->h * $this->k;
22783
		$e = ($x * (1 - $a)) - ($y * $c) + $e;
22784
		$f = ($y * (1 - $d)) - ($x * $b) + $f;
22785
		return array($a, $b, $c, $d, $e, $f);
22786
	}
22787
 
22788
	/**
22789
	 * Apply SVG graphic transformation matrix.
22790
	 * @param $tm (array) original SVG transformation matrix
22791
	 * @protected
22792
	 * @since 5.0.000 (2010-05-02)
22793
	 */
22794
	protected function SVGTransform($tm) {
22795
		$this->Transform($this->convertSVGtMatrix($tm));
22796
	}
22797
 
22798
	/**
22799
	 * Apply the requested SVG styles (*** TO BE COMPLETED ***)
22800
	 * @param $svgstyle (array) array of SVG styles to apply
22801
	 * @param $prevsvgstyle (array) array of previous SVG style
22802
	 * @param $x (int) X origin of the bounding box
22803
	 * @param $y (int) Y origin of the bounding box
22804
	 * @param $w (int) width of the bounding box
22805
	 * @param $h (int) height of the bounding box
22806
	 * @param $clip_function (string) clip function
22807
	 * @param $clip_params (array) array of parameters for clipping function
22808
	 * @return object style
22809
	 * @author Nicola Asuni
22810
	 * @since 5.0.000 (2010-05-02)
22811
	 * @protected
22812
	 */
22813
	protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
22814
		if ($this->state != 2) {
22815
			 return;
22816
		}
22817
		$objstyle = '';
22818
		$minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
22819
		if (!isset($svgstyle['opacity'])) {
22820
			return $objstyle;
22821
		}
22822
		// clip-path
22823
		$regs = array();
22824
		if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
22825
			$clip_path = $this->svgclippaths[$regs[1]];
22826
			foreach ($clip_path as $cp) {
22827
				$this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
22828
			}
22829
		}
22830
		// opacity
22831
		if ($svgstyle['opacity'] != 1) {
22832
			$this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
22833
		}
22834
		// color
22835
		$fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
22836
		$this->SetFillColorArray($fill_color);
22837
		// text color
22838
		$text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
22839
		$this->SetTextColorArray($text_color);
22840
		// clip
22841
		if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
22842
			$top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
22843
			$right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
22844
			$bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
22845
			$left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
22846
			$cx = $x + $left;
22847
			$cy = $y + $top;
22848
			$cw = $w - $left - $right;
22849
			$ch = $h - $top - $bottom;
22850
			if ($svgstyle['clip-rule'] == 'evenodd') {
22851
				$clip_rule = 'CNZ';
22852
			} else {
22853
				$clip_rule = 'CEO';
22854
			}
22855
			$this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
22856
		}
22857
		// fill
22858
		$regs = array();
22859
		if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
22860
			// gradient
22861
			$gradient = $this->svggradients[$regs[1]];
22862
			if (isset($gradient['xref'])) {
22863
				// reference to another gradient definition
22864
				$newgradient = $this->svggradients[$gradient['xref']];
22865
				$newgradient['coords'] = $gradient['coords'];
22866
				$newgradient['mode'] = $gradient['mode'];
22867
				$newgradient['gradientUnits'] = $gradient['gradientUnits'];
22868
				if (isset($gradient['gradientTransform'])) {
22869
					$newgradient['gradientTransform'] = $gradient['gradientTransform'];
22870
				}
22871
				$gradient = $newgradient;
22872
			}
22873
			//save current Graphic State
22874
			$this->_out('q');
22875
			//set clipping area
22876
			if (!empty($clip_function) AND method_exists($this, $clip_function)) {
22877
				$bbox = call_user_func_array(array($this, $clip_function), $clip_params);
22878
				if (is_array($bbox) AND (count($bbox) == 4)) {
22879
					list($x, $y, $w, $h) = $bbox;
22880
				}
22881
			}
22882
			if ($gradient['mode'] == 'measure') {
22883
				if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
22884
					$gtm = $gradient['gradientTransform'];
22885
					// apply transformation matrix
22886
					$xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
22887
					$ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
22888
					$xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
22889
					$yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
22890
					if (isset($gradient['coords'][4])) {
22891
						$gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
22892
					}
22893
					$gradient['coords'][0] = $xa;
22894
					$gradient['coords'][1] = $ya;
22895
					$gradient['coords'][2] = $xb;
22896
					$gradient['coords'][3] = $yb;
22897
				}
22898
				// convert SVG coordinates to user units
22899
				$gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
22900
				$gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
22901
				$gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
22902
				$gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
22903
				if (isset($gradient['coords'][4])) {
22904
					$gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
22905
				}
22906
				if ($w <= $minlen) {
22907
					$w = $minlen;
22908
				}
22909
				if ($h <= $minlen) {
22910
					$h = $minlen;
22911
				}
22912
				// shift units
22913
				if ($gradient['gradientUnits'] == 'objectBoundingBox') {
22914
					// convert to SVG coordinate system
22915
					$gradient['coords'][0] += $x;
22916
					$gradient['coords'][1] += $y;
22917
					$gradient['coords'][2] += $x;
22918
					$gradient['coords'][3] += $y;
22919
				}
22920
				// calculate percentages
22921
				$gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
22922
				$gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
22923
				$gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
22924
				$gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
22925
				if (isset($gradient['coords'][4])) {
22926
					$gradient['coords'][4] /= $w;
22927
				}
22928
			} elseif ($gradient['mode'] == 'percentage') {
22929
				foreach($gradient['coords'] as $key => $val) {
22930
					$gradient['coords'][$key] = (intval($val) / 100);
22931
					if ($val < 0) {
22932
						$gradient['coords'][$key] = 0;
22933
					} elseif ($val > 1) {
22934
						$gradient['coords'][$key] = 1;
22935
					}
22936
				}
22937
			}
22938
			if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
22939
				// single color (no shading)
22940
				$gradient['coords'][0] = 1;
22941
				$gradient['coords'][1] = 0;
22942
				$gradient['coords'][2] = 0.999;
22943
				$gradient['coords'][3] = 0;
22944
			}
22945
			// swap Y coordinates
22946
			$tmp = $gradient['coords'][1];
22947
			$gradient['coords'][1] = $gradient['coords'][3];
22948
			$gradient['coords'][3] = $tmp;
22949
			// set transformation map for gradient
22950
			if ($gradient['type'] == 3) {
22951
				// circular gradient
22952
				$cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
22953
				$this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($w * $this->k), ($x * $this->k), ($cy * $this->k)));
22954
			} else {
22955
				$this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), (($this->h - ($y + $h)) * $this->k)));
22956
			}
22957
			if (count($gradient['stops']) > 1) {
22958
				$this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
22959
			}
22960
		} elseif ($svgstyle['fill'] != 'none') {
22961
			$fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
22962
			if ($svgstyle['fill-opacity'] != 1) {
22963
				$this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
22964
			}
22965
			$this->SetFillColorArray($fill_color);
22966
			if ($svgstyle['fill-rule'] == 'evenodd') {
22967
				$objstyle .= 'F*';
22968
			} else {
22969
				$objstyle .= 'F';
22970
			}
22971
		}
22972
		// stroke
22973
		if ($svgstyle['stroke'] != 'none') {
22974
			if ($svgstyle['stroke-opacity'] != 1) {
22975
				$this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
22976
			}
22977
			$stroke_style = array(
22978
				'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
22979
				'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
22980
				'cap' => $svgstyle['stroke-linecap'],
22981
				'join' => $svgstyle['stroke-linejoin']
22982
				);
22983
			if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
22984
				$stroke_style['dash'] = $svgstyle['stroke-dasharray'];
22985
			}
22986
			$this->SetLineStyle($stroke_style);
22987
			$objstyle .= 'D';
22988
		}
22989
		// font
22990
		$regs = array();
22991
		if (!empty($svgstyle['font'])) {
22992
			if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
22993
				$font_family = $this->getFontFamilyName($regs[1]);
22994
			} else {
22995
				$font_family = $svgstyle['font-family'];
22996
			}
22997
			if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
22998
				$font_size = trim($regs[1]);
22999
			} else {
23000
				$font_size = $svgstyle['font-size'];
23001
			}
23002
			if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23003
				$font_style = trim($regs[1]);
23004
			} else {
23005
				$font_style = $svgstyle['font-style'];
23006
			}
23007
			if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23008
				$font_weight = trim($regs[1]);
23009
			} else {
23010
				$font_weight = $svgstyle['font-weight'];
23011
			}
23012
			if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23013
				$font_stretch = trim($regs[1]);
23014
			} else {
23015
				$font_stretch = $svgstyle['font-stretch'];
23016
			}
23017
			if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23018
				$font_spacing = trim($regs[1]);
23019
			} else {
23020
				$font_spacing = $svgstyle['letter-spacing'];
23021
			}
23022
		} else {
23023
			$font_family = $this->getFontFamilyName($svgstyle['font-family']);
23024
			$font_size = $svgstyle['font-size'];
23025
			$font_style = $svgstyle['font-style'];
23026
			$font_weight = $svgstyle['font-weight'];
23027
			$font_stretch = $svgstyle['font-stretch'];
23028
			$font_spacing = $svgstyle['letter-spacing'];
23029
		}
23030
		$font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23031
		$font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23032
		$font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23033
		switch ($font_style) {
23034
			case 'italic': {
23035
				$font_style = 'I';
23036
				break;
23037
			}
23038
			case 'oblique': {
23039
				$font_style = 'I';
23040
				break;
23041
			}
23042
			default:
23043
			case 'normal': {
23044
				$font_style = '';
23045
				break;
23046
			}
23047
		}
23048
		switch ($font_weight) {
23049
			case 'bold':
23050
			case 'bolder': {
23051
				$font_style .= 'B';
23052
				break;
23053
			}
23054
		}
23055
		switch ($svgstyle['text-decoration']) {
23056
			case 'underline': {
23057
				$font_style .= 'U';
23058
				break;
23059
			}
23060
			case 'overline': {
23061
				$font_style .= 'O';
23062
				break;
23063
			}
23064
			case 'line-through': {
23065
				$font_style .= 'D';
23066
				break;
23067
			}
23068
			default:
23069
			case 'none': {
23070
				break;
23071
			}
23072
		}
23073
		$this->SetFont($font_family, $font_style, $font_size);
23074
		$this->setFontStretching($font_stretch);
23075
		$this->setFontSpacing($font_spacing);
23076
		return $objstyle;
23077
	}
23078
 
23079
	/**
23080
	 * Draws an SVG path
23081
	 * @param $d (string) attribute d of the path SVG element
23082
	 * @param $style (string) Style of rendering. Possible values are:
23083
	 * <ul>
23084
	 *	 <li>D or empty string: Draw (default).</li>
23085
	 *	 <li>F: Fill.</li>
23086
	 *	 <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23087
	 *	 <li>DF or FD: Draw and fill.</li>
23088
	 *	 <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23089
	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23090
	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23091
	 * </ul>
23092
	 * @return array of container box measures (x, y, w, h)
23093
	 * @author Nicola Asuni
23094
	 * @since 5.0.000 (2010-05-02)
23095
	 * @protected
23096
	 */
23097
	protected function SVGPath($d, $style='') {
23098
		if ($this->state != 2) {
23099
			 return;
23100
		}
23101
		// set fill/stroke style
23102
		$op = TCPDF_STATIC::getPathPaintOperator($style, '');
23103
		if (empty($op)) {
23104
			return;
23105
		}
23106
		$paths = array();
23107
		$d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23108
		preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23109
		$x = 0;
23110
		$y = 0;
23111
		$x1 = 0;
23112
		$y1 = 0;
23113
		$x2 = 0;
23114
		$y2 = 0;
23115
		$xmin = 2147483647;
23116
		$xmax = 0;
23117
		$ymin = 2147483647;
23118
		$ymax = 0;
23119
		$relcoord = false;
23120
		$minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23121
		$firstcmd = true; // used to print first point
23122
		// draw curve pieces
23123
		foreach ($paths as $key => $val) {
23124
			// get curve type
23125
			$cmd = trim($val[1]);
23126
			if (strtolower($cmd) == $cmd) {
23127
				// use relative coordinated instead of absolute
23128
				$relcoord = true;
23129
				$xoffset = $x;
23130
				$yoffset = $y;
23131
			} else {
23132
				$relcoord = false;
23133
				$xoffset = 0;
23134
				$yoffset = 0;
23135
			}
23136
			$params = array();
23137
			if (isset($val[2])) {
23138
				// get curve parameters
23139
				$rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23140
				$params = array();
23141
				foreach ($rawparams as $ck => $cp) {
23142
					$params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23143
					if (abs($params[$ck]) < $minlen) {
23144
						// aproximate little values to zero
23145
						$params[$ck] = 0;
23146
					}
23147
				}
23148
			}
23149
			// store current origin point
23150
			$x0 = $x;
23151
			$y0 = $y;
23152
			switch (strtoupper($cmd)) {
23153
				case 'M': { // moveto
23154
					foreach ($params as $ck => $cp) {
23155
						if (($ck % 2) == 0) {
23156
							$x = $cp + $xoffset;
23157
						} else {
23158
							$y = $cp + $yoffset;
23159
							if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23160
								if ($ck == 1) {
23161
									$this->_outPoint($x, $y);
23162
									$firstcmd = false;
23163
								} else {
23164
									$this->_outLine($x, $y);
23165
								}
23166
								$x0 = $x;
23167
								$y0 = $y;
23168
							}
23169
							$xmin = min($xmin, $x);
23170
							$ymin = min($ymin, $y);
23171
							$xmax = max($xmax, $x);
23172
							$ymax = max($ymax, $y);
23173
							if ($relcoord) {
23174
								$xoffset = $x;
23175
								$yoffset = $y;
23176
							}
23177
						}
23178
					}
23179
					break;
23180
				}
23181
				case 'L': { // lineto
23182
					foreach ($params as $ck => $cp) {
23183
						if (($ck % 2) == 0) {
23184
							$x = $cp + $xoffset;
23185
						} else {
23186
							$y = $cp + $yoffset;
23187
							if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23188
								$this->_outLine($x, $y);
23189
								$x0 = $x;
23190
								$y0 = $y;
23191
							}
23192
							$xmin = min($xmin, $x);
23193
							$ymin = min($ymin, $y);
23194
							$xmax = max($xmax, $x);
23195
							$ymax = max($ymax, $y);
23196
							if ($relcoord) {
23197
								$xoffset = $x;
23198
								$yoffset = $y;
23199
							}
23200
						}
23201
					}
23202
					break;
23203
				}
23204
				case 'H': { // horizontal lineto
23205
					foreach ($params as $ck => $cp) {
23206
						$x = $cp + $xoffset;
23207
						if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23208
							$this->_outLine($x, $y);
23209
							$x0 = $x;
23210
							$y0 = $y;
23211
						}
23212
						$xmin = min($xmin, $x);
23213
						$xmax = max($xmax, $x);
23214
						if ($relcoord) {
23215
							$xoffset = $x;
23216
						}
23217
					}
23218
					break;
23219
				}
23220
				case 'V': { // vertical lineto
23221
					foreach ($params as $ck => $cp) {
23222
						$y = $cp + $yoffset;
23223
						if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23224
							$this->_outLine($x, $y);
23225
							$x0 = $x;
23226
							$y0 = $y;
23227
						}
23228
						$ymin = min($ymin, $y);
23229
						$ymax = max($ymax, $y);
23230
						if ($relcoord) {
23231
							$yoffset = $y;
23232
						}
23233
					}
23234
					break;
23235
				}
23236
				case 'C': { // curveto
23237
					foreach ($params as $ck => $cp) {
23238
						$params[$ck] = $cp;
23239
						if ((($ck + 1) % 6) == 0) {
23240
							$x1 = $params[($ck - 5)] + $xoffset;
23241
							$y1 = $params[($ck - 4)] + $yoffset;
23242
							$x2 = $params[($ck - 3)] + $xoffset;
23243
							$y2 = $params[($ck - 2)] + $yoffset;
23244
							$x = $params[($ck - 1)] + $xoffset;
23245
							$y = $params[($ck)] + $yoffset;
23246
							$this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23247
							$xmin = min($xmin, $x, $x1, $x2);
23248
							$ymin = min($ymin, $y, $y1, $y2);
23249
							$xmax = max($xmax, $x, $x1, $x2);
23250
							$ymax = max($ymax, $y, $y1, $y2);
23251
							if ($relcoord) {
23252
								$xoffset = $x;
23253
								$yoffset = $y;
23254
							}
23255
						}
23256
					}
23257
					break;
23258
				}
23259
				case 'S': { // shorthand/smooth curveto
23260
					foreach ($params as $ck => $cp) {
23261
						$params[$ck] = $cp;
23262
						if ((($ck + 1) % 4) == 0) {
23263
							if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23264
								$x1 = (2 * $x) - $x2;
23265
								$y1 = (2 * $y) - $y2;
23266
							} else {
23267
								$x1 = $x;
23268
								$y1 = $y;
23269
							}
23270
							$x2 = $params[($ck - 3)] + $xoffset;
23271
							$y2 = $params[($ck - 2)] + $yoffset;
23272
							$x = $params[($ck - 1)] + $xoffset;
23273
							$y = $params[($ck)] + $yoffset;
23274
							$this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23275
							$xmin = min($xmin, $x, $x1, $x2);
23276
							$ymin = min($ymin, $y, $y1, $y2);
23277
							$xmax = max($xmax, $x, $x1, $x2);
23278
							$ymax = max($ymax, $y, $y1, $y2);
23279
							if ($relcoord) {
23280
								$xoffset = $x;
23281
								$yoffset = $y;
23282
							}
23283
						}
23284
					}
23285
					break;
23286
				}
23287
				case 'Q': { // quadratic Bézier curveto
23288
					foreach ($params as $ck => $cp) {
23289
						$params[$ck] = $cp;
23290
						if ((($ck + 1) % 4) == 0) {
23291
							// convert quadratic points to cubic points
23292
							$x1 = $params[($ck - 3)] + $xoffset;
23293
							$y1 = $params[($ck - 2)] + $yoffset;
23294
							$xa = ($x + (2 * $x1)) / 3;
23295
							$ya = ($y + (2 * $y1)) / 3;
23296
							$x = $params[($ck - 1)] + $xoffset;
23297
							$y = $params[($ck)] + $yoffset;
23298
							$xb = ($x + (2 * $x1)) / 3;
23299
							$yb = ($y + (2 * $y1)) / 3;
23300
							$this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23301
							$xmin = min($xmin, $x, $xa, $xb);
23302
							$ymin = min($ymin, $y, $ya, $yb);
23303
							$xmax = max($xmax, $x, $xa, $xb);
23304
							$ymax = max($ymax, $y, $ya, $yb);
23305
							if ($relcoord) {
23306
								$xoffset = $x;
23307
								$yoffset = $y;
23308
							}
23309
						}
23310
					}
23311
					break;
23312
				}
23313
				case 'T': { // shorthand/smooth quadratic Bézier curveto
23314
					foreach ($params as $ck => $cp) {
23315
						$params[$ck] = $cp;
23316
						if (($ck % 2) != 0) {
23317
							if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23318
								$x1 = (2 * $x) - $x1;
23319
								$y1 = (2 * $y) - $y1;
23320
							} else {
23321
								$x1 = $x;
23322
								$y1 = $y;
23323
							}
23324
							// convert quadratic points to cubic points
23325
							$xa = ($x + (2 * $x1)) / 3;
23326
							$ya = ($y + (2 * $y1)) / 3;
23327
							$x = $params[($ck - 1)] + $xoffset;
23328
							$y = $params[($ck)] + $yoffset;
23329
							$xb = ($x + (2 * $x1)) / 3;
23330
							$yb = ($y + (2 * $y1)) / 3;
23331
							$this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23332
							$xmin = min($xmin, $x, $xa, $xb);
23333
							$ymin = min($ymin, $y, $ya, $yb);
23334
							$xmax = max($xmax, $x, $xa, $xb);
23335
							$ymax = max($ymax, $y, $ya, $yb);
23336
							if ($relcoord) {
23337
								$xoffset = $x;
23338
								$yoffset = $y;
23339
							}
23340
						}
23341
					}
23342
					break;
23343
				}
23344
				case 'A': { // elliptical arc
23345
					foreach ($params as $ck => $cp) {
23346
						$params[$ck] = $cp;
23347
						if ((($ck + 1) % 7) == 0) {
23348
							$x0 = $x;
23349
							$y0 = $y;
23350
							$rx = abs($params[($ck - 6)]);
23351
							$ry = abs($params[($ck - 5)]);
23352
							$ang = -$rawparams[($ck - 4)];
23353
							$angle = deg2rad($ang);
23354
							$fa = $rawparams[($ck - 3)]; // large-arc-flag
23355
							$fs = $rawparams[($ck - 2)]; // sweep-flag
23356
							$x = $params[($ck - 1)] + $xoffset;
23357
							$y = $params[$ck] + $yoffset;
23358
							if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23359
								// endpoints are almost identical
23360
								$xmin = min($xmin, $x);
23361
								$ymin = min($ymin, $y);
23362
								$xmax = max($xmax, $x);
23363
								$ymax = max($ymax, $y);
23364
							} else {
23365
								$cos_ang = cos($angle);
23366
								$sin_ang = sin($angle);
23367
								$a = (($x0 - $x) / 2);
23368
								$b = (($y0 - $y) / 2);
23369
								$xa = ($a * $cos_ang) - ($b * $sin_ang);
23370
								$ya = ($a * $sin_ang) + ($b * $cos_ang);
23371
								$rx2 = $rx * $rx;
23372
								$ry2 = $ry * $ry;
23373
								$xa2 = $xa * $xa;
23374
								$ya2 = $ya * $ya;
23375
								$delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23376
								if ($delta > 1) {
23377
									$rx *= sqrt($delta);
23378
									$ry *= sqrt($delta);
23379
									$rx2 = $rx * $rx;
23380
									$ry2 = $ry * $ry;
23381
								}
23382
								$numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23383
								if ($numerator < 0) {
23384
									$root = 0;
23385
								} else {
23386
									$root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23387
								}
23388
								if ($fa == $fs){
23389
									$root *= -1;
23390
								}
23391
								$cax = $root * (($rx * $ya) / $ry);
23392
								$cay = -$root * (($ry * $xa) / $rx);
23393
								// coordinates of ellipse center
23394
								$cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23395
								$cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23396
								// get angles
23397
								$angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23398
								$dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23399
								if (($fs == 0) AND ($dang > 0)) {
23400
									$dang -= (2 * M_PI);
23401
								} elseif (($fs == 1) AND ($dang < 0)) {
23402
									$dang += (2 * M_PI);
23403
								}
23404
								$angf = $angs - $dang;
23405
								if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23406
									// reverse angles
23407
									$tmp = $angs;
23408
									$angs = $angf;
23409
									$angf = $tmp;
23410
								}
23411
								$angs = round(rad2deg($angs), 6);
23412
								$angf = round(rad2deg($angf), 6);
23413
								// covent angles to positive values
23414
								if (($angs < 0) AND ($angf < 0)) {
23415
									$angs += 360;
23416
									$angf += 360;
23417
								}
23418
								$pie = false;
23419
								if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23420
									$pie = true;
23421
								}
23422
								list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23423
								$xmin = min($xmin, $x, $axmin);
23424
								$ymin = min($ymin, $y, $aymin);
23425
								$xmax = max($xmax, $x, $axmax);
23426
								$ymax = max($ymax, $y, $aymax);
23427
							}
23428
							if ($relcoord) {
23429
								$xoffset = $x;
23430
								$yoffset = $y;
23431
							}
23432
						}
23433
					}
23434
					break;
23435
				}
23436
				case 'Z': {
23437
					$this->_out('h');
23438
					break;
23439
				}
23440
			}
23441
			$firstcmd = false;
23442
		} // end foreach
23443
		if (!empty($op)) {
23444
			$this->_out($op);
23445
		}
23446
		return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23447
	}
23448
 
23449
	/**
23450
	 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23451
	 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23452
	 * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.
23453
	 * @param $attribs (array) The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on.
23454
	 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
23455
	 * @author Nicola Asuni
23456
	 * @since 5.0.000 (2010-05-02)
23457
	 * @protected
23458
	 */
23459
	protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23460
		// check if we are in clip mode
23461
		if ($this->svgclipmode) {
23462
			$this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23463
			return;
23464
		}
23465
		if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23466
			if (!isset($attribs['id'])) {
23467
				$attribs['id'] = 'DF_'.(count($this->svgdefs) + 1);
23468
			}
23469
			$this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23470
			return;
23471
		}
23472
		$clipping = false;
23473
		if ($parser == 'clip-path') {
23474
			// set clipping mode
23475
			$clipping = true;
23476
		}
23477
		// get styling properties
23478
		$prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
23479
		$svgstyle = $this->svgstyles[0]; // set default style
23480
		if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23481
			// default fill attribute for clipping
23482
			$attribs['fill'] = 'none';
23483
		}
23484
		if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23485
			// fix style for regular expression
23486
			$attribs['style'] = ';'.$attribs['style'];
23487
		}
23488
		foreach ($prev_svgstyle as $key => $val) {
23489
			if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23490
				// inherit previous value
23491
				$svgstyle[$key] = $val;
23492
			}
23493
			if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23494
				// specific attribute settings
23495
				if ($attribs[$key] == 'inherit') {
23496
					$svgstyle[$key] = $val;
23497
				} else {
23498
					$svgstyle[$key] = $attribs[$key];
23499
				}
23500
			} elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23501
				// CSS style syntax
23502
				$attrval = array();
23503
				if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23504
					if ($attrval[1] == 'inherit') {
23505
						$svgstyle[$key] = $val;
23506
					} else {
23507
						$svgstyle[$key] = $attrval[1];
23508
					}
23509
				}
23510
			}
23511
		}
23512
		// transformation matrix
23513
		if (!empty($ctm)) {
23514
			$tm = $ctm;
23515
		} else {
23516
			//$tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
23517
			$tm = array(1,0,0,1,0,0);
23518
		}
23519
		if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23520
			$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform']));
23521
		}
23522
		$svgstyle['transfmatrix'] = $tm;
23523
		$invisible = false;
23524
		if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23525
			// the current graphics element is invisible (nothing is painted)
23526
			$invisible = true;
23527
		}
23528
		// process tag
23529
		switch($name) {
23530
			case 'defs': {
23531
				$this->svgdefsmode = true;
23532
				break;
23533
			}
23534
			// clipPath
23535
			case 'clipPath': {
23536
				if ($invisible) {
23537
					break;
23538
				}
23539
				$this->svgclipmode = true;
23540
				if (!isset($attribs['id'])) {
23541
					$attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23542
				}
23543
				$this->svgclipid = $attribs['id'];
23544
				$this->svgclippaths[$this->svgclipid] = array();
23545
				$this->svgcliptm[$this->svgclipid] = $tm;
23546
				break;
23547
			}
23548
			case 'svg': {
23549
				// start of SVG object
23550
				break;
23551
			}
23552
			case 'g': {
23553
				// group together related graphics elements
23554
				array_push($this->svgstyles, $svgstyle);
23555
				$this->StartTransform();
23556
				$this->SVGTransform($tm);
23557
				$this->setSVGStyles($svgstyle, $prev_svgstyle);
23558
				break;
23559
			}
23560
			case 'linearGradient': {
23561
				if ($this->pdfa_mode) {
23562
					break;
23563
				}
23564
				if (!isset($attribs['id'])) {
23565
					$attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23566
				}
23567
				$this->svggradientid = $attribs['id'];
23568
				$this->svggradients[$this->svggradientid] = array();
23569
				$this->svggradients[$this->svggradientid]['type'] = 2;
23570
				$this->svggradients[$this->svggradientid]['stops'] = array();
23571
				if (isset($attribs['gradientUnits'])) {
23572
					$this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23573
				} else {
23574
					$this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23575
				}
23576
				//$attribs['spreadMethod']
23577
				if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23578
					OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23579
						OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23580
						OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23581
						OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23582
					$this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23583
				} else {
23584
					$this->svggradients[$this->svggradientid]['mode'] = 'measure';
23585
				}
23586
				$x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23587
				$y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23588
				$x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23589
				$y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23590
				if (isset($attribs['gradientTransform'])) {
23591
					$this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23592
				}
23593
				$this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23594
				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23595
					// gradient is defined on another place
23596
					$this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23597
				}
23598
				break;
23599
			}
23600
			case 'radialGradient': {
23601
				if ($this->pdfa_mode) {
23602
					break;
23603
				}
23604
				if (!isset($attribs['id'])) {
23605
					$attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23606
				}
23607
				$this->svggradientid = $attribs['id'];
23608
				$this->svggradients[$this->svggradientid] = array();
23609
				$this->svggradients[$this->svggradientid]['type'] = 3;
23610
				$this->svggradients[$this->svggradientid]['stops'] = array();
23611
				if (isset($attribs['gradientUnits'])) {
23612
					$this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23613
				} else {
23614
					$this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23615
				}
23616
				//$attribs['spreadMethod']
23617
				if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23618
					OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23619
						OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) {
23620
					$this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23621
				} else {
23622
					$this->svggradients[$this->svggradientid]['mode'] = 'measure';
23623
				}
23624
				$cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23625
				$cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23626
				$fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23627
				$fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23628
				$r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23629
				if (isset($attribs['gradientTransform'])) {
23630
					$this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23631
				}
23632
				$this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23633
				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23634
					// gradient is defined on another place
23635
					$this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23636
				}
23637
				break;
23638
			}
23639
			case 'stop': {
23640
				// gradient stops
23641
				if (substr($attribs['offset'], -1) == '%') {
23642
					$offset = floatval(substr($attribs['offset'], -1)) / 100;
23643
				} else {
23644
					$offset = floatval($attribs['offset']);
23645
					if ($offset > 1) {
23646
						$offset /= 100;
23647
					}
23648
				}
23649
				$stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23650
				$opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23651
				$this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
23652
				break;
23653
			}
23654
			// paths
23655
			case 'path': {
23656
				if ($invisible) {
23657
					break;
23658
				}
23659
				if (isset($attribs['d'])) {
23660
					$d = trim($attribs['d']);
23661
					if (!empty($d)) {
23662
						if ($clipping) {
23663
							$this->SVGTransform($tm);
23664
							$this->SVGPath($d, 'CNZ');
23665
						} else {
23666
							$this->StartTransform();
23667
							$this->SVGTransform($tm);
23668
							$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
23669
							if (!empty($obstyle)) {
23670
								$this->SVGPath($d, $obstyle);
23671
							}
23672
							$this->StopTransform();
23673
						}
23674
					}
23675
				}
23676
				break;
23677
			}
23678
			// shapes
23679
			case 'rect': {
23680
				if ($invisible) {
23681
					break;
23682
				}
23683
				$x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
23684
				$y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
23685
				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
23686
				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
23687
				$rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
23688
				$ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
23689
				if ($clipping) {
23690
					$this->SVGTransform($tm);
23691
					$this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
23692
				} else {
23693
					$this->StartTransform();
23694
					$this->SVGTransform($tm);
23695
					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
23696
					if (!empty($obstyle)) {
23697
						$this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
23698
					}
23699
					$this->StopTransform();
23700
				}
23701
				break;
23702
			}
23703
			case 'circle': {
23704
				if ($invisible) {
23705
					break;
23706
				}
23707
				$r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
23708
				$cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
23709
				$cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
23710
				$x = ($cx - $r);
23711
				$y = ($cy - $r);
23712
				$w = (2 * $r);
23713
				$h = $w;
23714
				if ($clipping) {
23715
					$this->SVGTransform($tm);
23716
					$this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
23717
				} else {
23718
					$this->StartTransform();
23719
					$this->SVGTransform($tm);
23720
					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
23721
					if (!empty($obstyle)) {
23722
						$this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
23723
					}
23724
					$this->StopTransform();
23725
				}
23726
				break;
23727
			}
23728
			case 'ellipse': {
23729
				if ($invisible) {
23730
					break;
23731
				}
23732
				$rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
23733
				$ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
23734
				$cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
23735
				$cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
23736
				$x = ($cx - $rx);
23737
				$y = ($cy - $ry);
23738
				$w = (2 * $rx);
23739
				$h = (2 * $ry);
23740
				if ($clipping) {
23741
					$this->SVGTransform($tm);
23742
					$this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
23743
				} else {
23744
					$this->StartTransform();
23745
					$this->SVGTransform($tm);
23746
					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
23747
					if (!empty($obstyle)) {
23748
						$this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
23749
					}
23750
					$this->StopTransform();
23751
				}
23752
				break;
23753
			}
23754
			case 'line': {
23755
				if ($invisible) {
23756
					break;
23757
				}
23758
				$x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
23759
				$y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
23760
				$x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
23761
				$y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
23762
				$x = $x1;
23763
				$y = $y1;
23764
				$w = abs($x2 - $x1);
23765
				$h = abs($y2 - $y1);
23766
				if (!$clipping) {
23767
					$this->StartTransform();
23768
					$this->SVGTransform($tm);
23769
					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
23770
					$this->Line($x1, $y1, $x2, $y2);
23771
					$this->StopTransform();
23772
				}
23773
				break;
23774
			}
23775
			case 'polyline':
23776
			case 'polygon': {
23777
				if ($invisible) {
23778
					break;
23779
				}
23780
				$points = (isset($attribs['points'])?$attribs['points']:'0 0');
23781
				$points = trim($points);
23782
				// note that point may use a complex syntax not covered here
23783
				$points = preg_split('/[\,\s]+/si', $points);
23784
				if (count($points) < 4) {
23785
					break;
23786
				}
23787
				$p = array();
23788
				$xmin = 2147483647;
23789
				$xmax = 0;
23790
				$ymin = 2147483647;
23791
				$ymax = 0;
23792
				foreach ($points as $key => $val) {
23793
					$p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
23794
					if (($key % 2) == 0) {
23795
						// X coordinate
23796
						$xmin = min($xmin, $p[$key]);
23797
						$xmax = max($xmax, $p[$key]);
23798
					} else {
23799
						// Y coordinate
23800
						$ymin = min($ymin, $p[$key]);
23801
						$ymax = max($ymax, $p[$key]);
23802
					}
23803
				}
23804
				$x = $xmin;
23805
				$y = $ymin;
23806
				$w = ($xmax - $xmin);
23807
				$h = ($ymax - $ymin);
23808
				if ($name == 'polyline') {
23809
					$this->StartTransform();
23810
					$this->SVGTransform($tm);
23811
					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
23812
					if (!empty($obstyle)) {
23813
						$this->PolyLine($p, $obstyle, array(), array());
23814
					}
23815
					$this->StopTransform();
23816
				} else { // polygon
23817
					if ($clipping) {
23818
						$this->SVGTransform($tm);
23819
						$this->Polygon($p, 'CNZ', array(), array(), true);
23820
					} else {
23821
						$this->StartTransform();
23822
						$this->SVGTransform($tm);
23823
						$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
23824
						if (!empty($obstyle)) {
23825
							$this->Polygon($p, $obstyle, array(), array(), true);
23826
						}
23827
						$this->StopTransform();
23828
					}
23829
				}
23830
				break;
23831
			}
23832
			// image
23833
			case 'image': {
23834
				if ($invisible) {
23835
					break;
23836
				}
23837
				if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
23838
					break;
23839
				}
23840
				$x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
23841
				$y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
23842
				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
23843
				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
23844
				$img = $attribs['xlink:href'];
23845
				if (!$clipping) {
23846
					$this->StartTransform();
23847
					$this->SVGTransform($tm);
23848
					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
23849
					if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
23850
						// embedded image encoded as base64
23851
						$img = '@'.base64_decode(substr($img, strlen($m[0])));
23852
					} else {
23853
						// fix image path
23854
						if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
23855
							// replace relative path with full server path
23856
							$img = $this->svgdir.'/'.$img;
23857
						}
23858
						if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
23859
							$findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
23860
							if (($findroot === false) OR ($findroot > 1)) {
23861
								if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
23862
									$img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
23863
								} else {
23864
									$img = $_SERVER['DOCUMENT_ROOT'].$img;
23865
								}
23866
							}
23867
						}
23868
						$img = urldecode($img);
23869
						$testscrtype = @parse_url($img);
23870
						if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
23871
							// convert URL to server path
23872
							$img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
23873
						}
23874
					}
23875
					// get image type
23876
					$imgtype = TCPDF_IMAGES::getImageFileType($img);
23877
					if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
23878
						$this->ImageEps($img, $x, $y, $w, $h);
23879
					} elseif ($imgtype == 'svg') {
23880
						$this->ImageSVG($img, $x, $y, $w, $h);
23881
					} else {
23882
						$this->Image($img, $x, $y, $w, $h);
23883
					}
23884
					$this->StopTransform();
23885
				}
23886
				break;
23887
			}
23888
			// text
23889
			case 'text':
23890
			case 'tspan': {
23891
				// only basic support - advanced features must be implemented
23892
				$this->svgtextmode['invisible'] = $invisible;
23893
				if ($invisible) {
23894
					break;
23895
				}
23896
				array_push($this->svgstyles, $svgstyle);
23897
				if (isset($attribs['x'])) {
23898
					$x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
23899
				} elseif ($name == 'tspan') {
23900
					$x = $this->x;
23901
				} else {
23902
					$x = 0;
23903
				}
23904
				if (isset($attribs['dx'])) {
23905
					$x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
23906
				}
23907
				if (isset($attribs['y'])) {
23908
					$y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
23909
				} elseif ($name == 'tspan') {
23910
					$y = $this->y;
23911
				} else {
23912
					$y = 0;
23913
				}
23914
				if (isset($attribs['dy'])) {
23915
					$y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
23916
				}
23917
				$svgstyle['text-color'] = $svgstyle['fill'];
23918
				$this->svgtext = '';
23919
				if (isset($svgstyle['text-anchor'])) {
23920
					$this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
23921
				} else {
23922
					$this->svgtextmode['text-anchor'] = 'start';
23923
				}
23924
				if (isset($svgstyle['direction'])) {
23925
					if ($svgstyle['direction'] == 'rtl') {
23926
						$this->svgtextmode['rtl'] = true;
23927
					} else {
23928
						$this->svgtextmode['rtl'] = false;
23929
					}
23930
				} else {
23931
					$this->svgtextmode['rtl'] = false;
23932
				}
23933
				if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
23934
					$this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
23935
				} else {
23936
					$this->svgtextmode['stroke'] = false;
23937
				}
23938
				$this->StartTransform();
23939
				$this->SVGTransform($tm);
23940
				$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
23941
				$this->x = $x;
23942
				$this->y = $y;
23943
				break;
23944
			}
23945
			// use
23946
			case 'use': {
23947
				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23948
					$svgdefid = substr($attribs['xlink:href'], 1);
23949
					if (isset($this->svgdefs[$svgdefid])) {
23950
						$use = $this->svgdefs[$svgdefid];
23951
						if (isset($attribs['xlink:href'])) {
23952
							unset($attribs['xlink:href']);
23953
						}
23954
						if (isset($attribs['id'])) {
23955
							unset($attribs['id']);
23956
						}
23957
						$attribs = array_merge($attribs, $use['attribs']);
23958
						$this->startSVGElementHandler($parser, $use['name'], $attribs);
23959
					}
23960
				}
23961
				break;
23962
			}
23963
			default: {
23964
				break;
23965
			}
23966
		} // end of switch
23967
	}
23968
 
23969
	/**
23970
	 * Sets the closing SVG element handler function for the XML parser.
23971
	 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23972
	 * @param $name (string) The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.
23973
	 * @author Nicola Asuni
23974
	 * @since 5.0.000 (2010-05-02)
23975
	 * @protected
23976
	 */
23977
	protected function endSVGElementHandler($parser, $name) {
23978
		switch($name) {
23979
			case 'defs': {
23980
				$this->svgdefsmode = false;
23981
				break;
23982
			}
23983
			// clipPath
23984
			case 'clipPath': {
23985
				$this->svgclipmode = false;
23986
				break;
23987
			}
23988
			case 'g': {
23989
				// ungroup: remove last style from array
23990
				array_pop($this->svgstyles);
23991
				$this->StopTransform();
23992
				break;
23993
			}
23994
			case 'text':
23995
			case 'tspan': {
23996
				if ($this->svgtextmode['invisible']) {
23997
					// This implementation must be fixed to following the rule:
23998
					// If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations.
23999
					break;
24000
				}
24001
				// print text
24002
				$text = $this->svgtext;
24003
				//$text = $this->stringTrim($text);
24004
				$textlen = $this->GetStringWidth($text);
24005
				if ($this->svgtextmode['text-anchor'] != 'start') {
24006
					// check if string is RTL text
24007
					if ($this->svgtextmode['text-anchor'] == 'end') {
24008
						if ($this->svgtextmode['rtl']) {
24009
							$this->x += $textlen;
24010
						} else {
24011
							$this->x -= $textlen;
24012
						}
24013
					} elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24014
						if ($this->svgtextmode['rtl']) {
24015
							$this->x += ($textlen / 2);
24016
						} else {
24017
							$this->x -= ($textlen / 2);
24018
						}
24019
					}
24020
				}
24021
				$textrendermode = $this->textrendermode;
24022
				$textstrokewidth = $this->textstrokewidth;
24023
				$this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24024
				if ($name == 'text') {
24025
					// store current coordinates
24026
					$tmpx = $this->x;
24027
					$tmpy = $this->y;
24028
				}
24029
				$this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24030
				if ($name == 'text') {
24031
					// restore coordinates
24032
					$this->x = $tmpx;
24033
					$this->y = $tmpy;
24034
				}
24035
				// restore previous rendering mode
24036
				$this->textrendermode = $textrendermode;
24037
				$this->textstrokewidth = $textstrokewidth;
24038
				$this->svgtext = '';
24039
				$this->StopTransform();
24040
				array_pop($this->svgstyles);
24041
				break;
24042
			}
24043
			default: {
24044
				break;
24045
			}
24046
		}
24047
	}
24048
 
24049
	/**
24050
	 * Sets the character data handler function for the XML parser.
24051
	 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24052
	 * @param $data (string) The second parameter, data, contains the character data as a string.
24053
	 * @author Nicola Asuni
24054
	 * @since 5.0.000 (2010-05-02)
24055
	 * @protected
24056
	 */
24057
	protected function segSVGContentHandler($parser, $data) {
24058
		$this->svgtext .= $data;
24059
	}
24060
 
24061
	// --- END SVG METHODS -----------------------------------------------------
24062
 
24063
} // END OF TCPDF CLASS
24064
 
24065
//============================================================+
24066
// END OF FILE
24067
//============================================================+